Documentation
¶
Overview ¶
Package ui renders the aggregator — a topbar with search, a sidebar of source subscriptions, and a unified newest-first feed of items from every source — into an RGBA pixel buffer. Chrome and cards are drawn with the go-widgets painter; text is anti-aliased TrueType (see text.go) so it stays clean at any zoom / Retina scale. The package has no build tag, so its layout, hit-testing and rendering run under native `go test` and are snapshot-verifiable.
Index ¶
- Constants
- func MenuIconFolder(p painter.Painter, cell toolkit.Rect, ink toolkit.RGBA)
- func MenuIconMarkRead(p painter.Painter, cell toolkit.Rect, ink toolkit.RGBA)
- func MenuIconNewFolder(p painter.Painter, cell toolkit.Rect, ink toolkit.RGBA)
- func MenuIconRefresh(p painter.Painter, cell toolkit.Rect, ink toolkit.RGBA)
- func MenuIconUnsubscribe(p painter.Painter, cell toolkit.Rect, ink toolkit.RGBA)
- func ResolveTheme(name, os string, prefersDark bool) *toolkit.Theme
- func SetSystemFont(ttf []byte) bool
- func ThemeFor(os string, dark bool) *toolkit.Theme
- func WithAccent(t *toolkit.Theme, r, g, b uint8) *toolkit.Theme
- type A11yNode
- type AuthPrompt
- type DownloadItem
- type DownloadStatus
- type Focus
- type Hit
- type HitKind
- type LogEntry
- type MediaRequest
- type Mode
- type PrefetchRequest
- type ReconstructPart
- type Scene
- func (s *Scene) A11yTree() []A11yNode
- func (s *Scene) ActiveFilter() int
- func (s *Scene) ActiveProfile() settings.Profile
- func (s *Scene) ActiveProfileIndex() int
- func (s *Scene) AddInputSub()
- func (s *Scene) AdvanceAnim(n int)
- func (s *Scene) AnimFrame() int
- func (s *Scene) Animating() bool
- func (s *Scene) AuthPrompts() []AuthPrompt
- func (s *Scene) Backspace()
- func (s *Scene) BeginFolderRename(name string)
- func (s *Scene) BeginPreviewResize()
- func (s *Scene) BeginSidebarResize()
- func (s *Scene) BiometricUnlock() bool
- func (s *Scene) BookmarkedURLs() []string
- func (s *Scene) BrowseCaretKey(code string)
- func (s *Scene) BrowseEntry() *toolkit.SearchEntry
- func (s *Scene) BrowseFocused() bool
- func (s *Scene) BrowseGroups() []source.GroupInfo
- func (s *Scene) BrowseNodeExpanded(name string) bool
- func (s *Scene) BrowseSelectedNode() (name string, hasChildren, isGroup, subscribed, ok bool)
- func (s *Scene) Browser() *toolkit.Browser
- func (s *Scene) BrowserChromeHidden() bool
- func (s *Scene) BrowserFocused() bool
- func (s *Scene) BrowserSingleTab() bool
- func (s *Scene) BrowserVM() *tkbind.BrowserVM
- func (s *Scene) BrowserZoomInKey() string
- func (s *Scene) BrowserZoomOutKey() string
- func (s *Scene) CacheBackend() string
- func (s *Scene) CachePath() string
- func (s *Scene) CancelFolderRename()
- func (s *Scene) ChannelResults() []source.ChannelResult
- func (s *Scene) ClearPendingSource(k source.Kind, ch string)
- func (s *Scene) ClearSelection()
- func (s *Scene) CloseAccounts()
- func (s *Scene) CloseBrowse()
- func (s *Scene) CloseDetail()
- func (s *Scene) CloseLog()
- func (s *Scene) CloseSearch()
- func (s *Scene) CloseSettings()
- func (s *Scene) CommentsLoading(id string) bool
- func (s *Scene) CommitCache()
- func (s *Scene) CommitCacheBackend()
- func (s *Scene) CommitCacheSize()
- func (s *Scene) CommitFolderRename()
- func (s *Scene) CommitRename()
- func (s *Scene) CommitZoomKeys()
- func (s *Scene) ContextMenuActive() bool
- func (s *Scene) ContextMenuEvent(ev toolkit.Event)
- func (s *Scene) Copy() bool
- func (s *Scene) DeleteFolder(name string)
- func (s *Scene) DeleteProfile(i int)
- func (s *Scene) Detail() source.Item
- func (s *Scene) DismissPreviewOnSwitch() bool
- func (s *Scene) Downloads() []DownloadItem
- func (s *Scene) DraggingPreview() bool
- func (s *Scene) DraggingSidebar() bool
- func (s *Scene) Draw(buf []byte)
- func (s *Scene) EditedAccounts() []settings.Account
- func (s *Scene) EndPreviewResize()
- func (s *Scene) EndSidebarResize()
- func (s *Scene) FeedAnimating() bool
- func (s *Scene) FeedCardList() *virtual.CardList[source.Item]
- func (s *Scene) FeedClickAt(x, y int)
- func (s *Scene) FeedEvent(ev toolkit.Event)
- func (s *Scene) FeedGeom() (x, w int)
- func (s *Scene) FeedModelItems() []source.Item
- func (s *Scene) FeedScrollOffset() int
- func (s *Scene) FeedScrollToBottom()
- func (s *Scene) FeedTick(dt float64)
- func (s *Scene) FeedWheel(dy int)
- func (s *Scene) FinishPreviewImage(id string, img *image.RGBA)
- func (s *Scene) Focus() Focus
- func (s *Scene) FocusAccountField(key string)
- func (s *Scene) FocusBrowseFilter(v bool)
- func (s *Scene) FocusCache()
- func (s *Scene) FocusCacheBackend()
- func (s *Scene) FocusCacheSize()
- func (s *Scene) FocusChannel()
- func (s *Scene) FocusRename()
- func (s *Scene) FocusSearch(v bool)
- func (s *Scene) FocusSearchQuery(v bool)
- func (s *Scene) FocusSearchRegex(v bool)
- func (s *Scene) FocusSettingsField(f Focus)
- func (s *Scene) FocusZoomIn()
- func (s *Scene) FocusZoomOut()
- func (s *Scene) FolderCollapsed(name string) bool
- func (s *Scene) FolderNames() []string
- func (s *Scene) FolderRenaming() bool
- func (s *Scene) FollowImportKinds() []source.Kind
- func (s *Scene) ForwardBrowserClick(x, y int) bool
- func (s *Scene) ForwardBrowserDrag(x, y int) bool
- func (s *Scene) ForwardBrowserKey(name string, r rune) bool
- func (s *Scene) ForwardBrowserRelease(x, y int)
- func (s *Scene) ForwardBrowserScroll(dy int) bool
- func (s *Scene) ForwardBrowserScrollShift(dy int, shift bool) bool
- func (s *Scene) GIFPlaying() bool
- func (s *Scene) GroupExpanded(base string) bool
- func (s *Scene) GroupParts(base string) ([]ReconstructPart, bool)
- func (s *Scene) GroupPreviewItem(base string) (source.Item, bool)
- func (s *Scene) HasGroupStats(name string) bool
- func (s *Scene) HasSelection() bool
- func (s *Scene) HasThumb(id string) bool
- func (s *Scene) HitTest(x, y int) Hit
- func (s *Scene) ImagePrefetch() []PrefetchRequest
- func (s *Scene) InfiniteScroll() bool
- func (s *Scene) InvalidateBrowse()
- func (s *Scene) InvalidateSearch()
- func (s *Scene) IsBookmarked(url string) bool
- func (s *Scene) IsDownloadQueued(id string) bool
- func (s *Scene) IsPendingSub(k source.Kind, ch string) bool
- func (s *Scene) IsSubscribed(k source.Kind, channel string) bool
- func (s *Scene) LayoutPreview()
- func (s *Scene) Loading() bool
- func (s *Scene) LoadingProgress() (done, total int)
- func (s *Scene) LogEntries() []LogEntry
- func (s *Scene) MediaCacheMB() int
- func (s *Scene) MediaPrefetch() []MediaRequest
- func (s *Scene) Mode() Mode
- func (s *Scene) MouseMove(x, y int)
- func (s *Scene) MoveSubToFolder(sub int, folderName string)
- func (s *Scene) NativeControls() []toolkit.NativeControl
- func (s *Scene) NativeHit(key string) (Hit, bool)
- func (s *Scene) NativeSettingsCommit(key string) (Focus, bool)
- func (s *Scene) NavBrowse(dir int)
- func (s *Scene) NewProfile()
- func (s *Scene) NewSidebarFolder(base string) string
- func (s *Scene) OpenAccounts()
- func (s *Scene) OpenBrowse()
- func (s *Scene) OpenContextMenu(m *toolkit.ContextMenu, x, y int)
- func (s *Scene) OpenDetail(it source.Item)
- func (s *Scene) OpenLog()
- func (s *Scene) OpenSearch()
- func (s *Scene) OpenSettings()
- func (s *Scene) PendingCount() int
- func (s *Scene) PostResults() []source.Item
- func (s *Scene) PrefetchWebURLs(it source.Item, n int) []string
- func (s *Scene) PreviewComments(id string) ([]source.Comment, bool)
- func (s *Scene) PreviewFocusable() bool
- func (s *Scene) PreviewFocused() bool
- func (s *Scene) PreviewItem() (source.Item, bool)
- func (s *Scene) PreviewOpenButton() (toolkit.Rect, bool)
- func (s *Scene) PreviewTextScale() float64
- func (s *Scene) RemoveSub(prof, sub int)
- func (s *Scene) RemoveSubFromFolder(sub int)
- func (s *Scene) RenameFolder(old, newName string)
- func (s *Scene) RenamingFolder() string
- func (s *Scene) Resize(w, h int)
- func (s *Scene) Rev() int
- func (s *Scene) Scroll(dy int)
- func (s *Scene) ScrollPreviewKey(dir int, page bool) bool
- func (s *Scene) ScrollY() int
- func (s *Scene) Search() string
- func (s *Scene) SearchEntry() *toolkit.SearchEntry
- func (s *Scene) SearchFocused() bool
- func (s *Scene) SearchLoading() bool
- func (s *Scene) SearchQuery() string
- func (s *Scene) SearchQueryFocused() bool
- func (s *Scene) SearchRegex() string
- func (s *Scene) SearchRegexFocused() bool
- func (s *Scene) SearchStatus() string
- func (s *Scene) SearchTabPosts() bool
- func (s *Scene) SelectAccount(k source.Kind)
- func (s *Scene) SelectEditProfile(i int)
- func (s *Scene) SelectKind(k source.Kind)
- func (s *Scene) SelectPreview(it source.Item)
- func (s *Scene) SelectableAt(x, y int) bool
- func (s *Scene) SelectedAccount() source.Kind
- func (s *Scene) SelectedBrowseGroup() (string, bool)
- func (s *Scene) SelectionBegin(x, y int)
- func (s *Scene) SelectionEnd()
- func (s *Scene) SetAccountField(k source.Kind, key, value string)
- func (s *Scene) SetAccounts(accts []settings.Account)
- func (s *Scene) SetActive(i int)
- func (s *Scene) SetActiveProfile(i int)
- func (s *Scene) SetAuthPrompts(p []AuthPrompt)
- func (s *Scene) SetBiometricUnlock(v bool)
- func (s *Scene) SetBookmarked(url string, on bool)
- func (s *Scene) SetBookmarks(urls []string)
- func (s *Scene) SetBrowseGroups(groups []source.GroupInfo)
- func (s *Scene) SetBrowserChromeHidden(v bool)
- func (s *Scene) SetBrowserSingleTab(v bool)
- func (s *Scene) SetBrowserZoomKeys(in, out string)
- func (s *Scene) SetCacheBackend(b string)
- func (s *Scene) SetCachePath(p string)
- func (s *Scene) SetChannelResults(rs []source.ChannelResult)
- func (s *Scene) SetComments(id string, comments []source.Comment)
- func (s *Scene) SetCommentsLoading(id string, v bool)
- func (s *Scene) SetDismissPreviewOnSwitch(v bool)
- func (s *Scene) SetDownloads(items []DownloadItem)
- func (s *Scene) SetFeedSelectHook(f func(it source.Item, viaKeyboard bool))
- func (s *Scene) SetFollowImportKinds(kinds []source.Kind)
- func (s *Scene) SetGIFPlaying(v bool)
- func (s *Scene) SetGroupStats(name string, st source.GroupStats)
- func (s *Scene) SetInfiniteScroll(v bool)
- func (s *Scene) SetItems(items []source.Item)
- func (s *Scene) SetLoading(active bool, done, total int)
- func (s *Scene) SetLogSource(fn func() []LogEntry)
- func (s *Scene) SetMediaCacheMB(mb int)
- func (s *Scene) SetPendingSources(subs []source.Subscription)
- func (s *Scene) SetPostResults(items []source.Item)
- func (s *Scene) SetPreviewLoading(v bool)
- func (s *Scene) SetPreviewTextScale(f float64)
- func (s *Scene) SetProfiles(profiles []settings.Profile, active int)
- func (s *Scene) SetRenderCacheStats(fn func() (pages, bytes int))
- func (s *Scene) SetScale(f float64)
- func (s *Scene) SetSearch(v string)
- func (s *Scene) SetSearchLoading(v bool)
- func (s *Scene) SetSearchStatus(v string)
- func (s *Scene) SetSearchTab(posts bool)
- func (s *Scene) SetSeen(seen map[string]int)
- func (s *Scene) SetSidebarFolders(folders []settings.Folder)
- func (s *Scene) SetSignInBrowser(name string)
- func (s *Scene) SetStatus(v string)
- func (s *Scene) SetSubs(subs []Subscription)
- func (s *Scene) SetTheme(t *toolkit.Theme)
- func (s *Scene) SetThemeName(name string)
- func (s *Scene) SetThumb(id string, img *image.RGBA)
- func (s *Scene) SetUsenetServer(addr string)
- func (s *Scene) Settings() *settings.Settings
- func (s *Scene) SidebarCollapsed() bool
- func (s *Scene) SidebarContextAt(x, y int) SidebarContext
- func (s *Scene) SidebarFolders() []settings.Folder
- func (s *Scene) SidebarSourceOpen() source.Kind
- func (s *Scene) SignInBrowser() string
- func (s *Scene) SubAt(index int) (Subscription, bool)
- func (s *Scene) SubMarker(index int) (key string, marker int, ok bool)
- func (s *Scene) SubscribeActive(k source.Kind, channel string) bool
- func (s *Scene) SyncBookmarkStar()
- func (s *Scene) TextZoomButtons() (smaller, larger toolkit.Rect, shown bool)
- func (s *Scene) ThemeName() string
- func (s *Scene) ToggleAccountBool(key string)
- func (s *Scene) ToggleBrowseNode(name string)
- func (s *Scene) ToggleGroup(base string)
- func (s *Scene) TogglePreviewFocus() bool
- func (s *Scene) ToggleSidebar()
- func (s *Scene) ToggleSidebarFolder(name string)
- func (s *Scene) ToggleSidebarSource(kind source.Kind)
- func (s *Scene) TypeRune(r rune)
- func (s *Scene) UnsubscribeActive(k source.Kind, channel string) bool
- func (s *Scene) UsenetServer() string
- func (s *Scene) WebPreviewActive() bool
- func (s *Scene) WebPreviewURL(it source.Item) string
- func (s *Scene) ZoomBrowserIn()
- func (s *Scene) ZoomBrowserOut()
- func (s *Scene) ZoomKeyDir(r rune) int
- type SidebarContext
- type SidebarCtxKind
- type Subscription
- type WebLink
Constants ¶
const ( MinW = 360 MinH = 240 MinZoom = 0.5 MaxZoom = 3.0 ZoomStep = 0.1 )
Minimum sensible surface and zoom bounds.
const ( OSMac = "mac" OSLinux = "linux" OSWindows = "windows" )
OS tokens understood by ThemeFor. The front-end derives these from navigator.platform / userAgentData.
const AllFilter = -1
AllFilter is the Active value that shows every source (no filter).
Variables ¶
This section is empty.
Functions ¶
func MenuIconMarkRead ¶
func MenuIconNewFolder ¶
MenuIconNewFolder and MenuIconFolder paint the sidebar folder context-menu glyphs into the cell the toolkit Menu hands them, in the row's own ink (so they invert on hover). They match toolkit.MenuItem.Icon so the Handler can hand them straight to the menu without this package's private icon handles leaking out.
func MenuIconRefresh ¶
MenuIconRefresh, MenuIconMarkRead and MenuIconUnsubscribe paint the sidebar context menu's row glyphs into the cell the toolkit Menu hands them, in the row's own ink (so they invert on hover). They match toolkit.MenuItem.Icon, so the Handler can hand them straight to the menu without this package's private icon handles leaking out.
func MenuIconUnsubscribe ¶
func ResolveTheme ¶
ResolveTheme maps a persisted theme name ("system"|"light"|"dark") plus the host OS and the system dark preference to a concrete palette.
func SetSystemFont ¶
SetSystemFont installs a host UI typeface (e.g. macOS SF from /System/Library/Fonts/SFNS.ttf) so the reader matches the native look. SF is shipped as a variable font whose only instance the pure-Go rasteriser can reach is Regular, so bold weights are synthesised by over-striking (see toolkit.NewSyntheticBoldFont). It returns false and keeps the embedded Go fonts if ttf cannot be parsed. The font cache is dropped so already-built chains re-derive from the new primary.
func ThemeFor ¶
ThemeFor returns the palette matching os (one of the OS* tokens) in the requested light/dark variant. Unknown platforms fall back to the toolkit default theme.
func WithAccent ¶
WithAccent overrides a theme's accent colour with a harvested system accent (e.g. macOS -[NSColor controlAccentColor]) and recomputes the on-accent label colour so topbar text stays legible whatever hue the user picked.
Types ¶
type A11yNode ¶
A11yNode is one element of the reader's accessibility tree: the toolkit's description of it, plus where it sits on screen so a platform bridge can hit-test and focus it.
Rect is in screen coordinates. A feed card scrolled out of view still appears in the tree, with a rect above or below the viewport: a reader must be able to walk the whole list, not only the part that happens to be painted, and it is the bridge's job to scroll to what the user selects.
type AuthPrompt ¶
AuthPrompt names a provider whose subscription failed because it needs the user to sign in or supply configuration. The feed renders one clickable "needs sign-in" banner per prompt; a click opens the Accounts editor for Kind.
type DownloadItem ¶
type DownloadItem struct {
ID string
Name string
Status DownloadStatus
Done int
Total int
}
DownloadItem is one row of the download panel, keyed by the post's release base (the same id the group card's checkbox toggles).
type DownloadStatus ¶
type DownloadStatus int
DownloadStatus is the lifecycle state of a queued download.
const ( DLQueued DownloadStatus = iota // waiting for a worker DLActive // downloading (Done/Total parts) DLDone // reconstructed + saved DLFailed // errored )
type Focus ¶
type Focus int
Focus names which settings text field currently has keyboard focus.
const ( FocusNone Focus = iota FocusChannel FocusRename FocusCache FocusCacheSize // the media-cache size-limit (MB) field FocusCacheBackend // the media-cache backend (plugin path) field FocusZoomIn // the "zoom in" browser-shortcut key field FocusZoomOut // the "zoom out" browser-shortcut key field )
Settings-view focus states.
type Hit ¶
type Hit struct {
Kind HitKind
Item source.Item // HitItem
Sub int // HitSub: index into Subs, or AllFilter; HitRemoveSub: sub index
Profile int // HitProfile / HitSelectProfile / HitDeleteProfile / HitRemoveSub / HitRenameProfile
Value string // HitTheme / HitSelectKind / HitImportFollows (source kind)
}
Hit is the result of Scene.HitTest.
type HitKind ¶
type HitKind int
HitKind classifies what a click landed on.
const ( HitNone HitKind = iota HitItem // a feed item (open it in the detail view) — Item set HitSub // a sidebar subscription (filter the feed) — Sub set (AllFilter = All) HitSearch // the topbar search field (focus it) HitBack // the detail view's back button (return to the feed) HitOpenExternal // the detail view's "open original" button — Item set HitProfile // a sidebar profile tab (switch active profile) — Profile set HitSettings // the sidebar ⚙ Settings entry (open preferences) HitLog // the sidebar Network-log entry (open the HTTP log) HitCloseLog // the log view's "< Back" button (return to the feed) HitBurger // the topbar burger button (collapse/expand the sidebar) HitRefresh // the topbar refresh button (re-fetch the feed) — mirrors the pull-to-refresh gesture HitSidebarDivider // the draggable divider at the sidebar's right edge HitFixAuth // an in-feed "needs sign-in" banner row — Value = source kind HitToggleGroup // a Usenet group card's header/chevron — Value = release base HitReconstruct // a Usenet group card's "Reconstruct" affordance — Value = release base HitPreviewGroup // a Usenet group card's body (preview it in the pane) — Value = release base HitOpenPreview // the preview pane's "Open" button (full reading view) — Item set HitPreviewDivider // the preview pane's left-edge resize grip (start a drag) HitPreviewTextSmaller // the preview toolbar's "A−" pill (shrink the reader text) HitPreviewTextLarger // the preview toolbar's "A+" pill (grow the reader text) HitToggleDownload // a complete post's download checkbox — Value = release base HitClearDownloads // the download panel's "Clear" button HitToggleFolder // a sidebar virtual-folder row (collapse/expand) — Value = folder name HitToggleSource // a sidebar source-group header (accordion expand/collapse) — Value = source kind // Newsgroup browser (Mode == ModeBrowse): HitBrowse // the sidebar "+ Browse newsgroups" entry (open the browser) HitCloseBrowse // the browser's "‹ Back" button (return to the feed) HitBrowseRefresh // the browser's Refresh button (re-fetch the full group list) HitBrowseFilter // the browser's regexp filter field (focus it) HitToggleBrowseNode // a tree node's chevron/row (expand/collapse) — Value = node name HitSubscribeGroup // a tree leaf's Subscribe affordance — Value = full group name HitUnsubscribeGroup // a subscribed tree leaf's ✓ (unsubscribe) — Value = full group name // Settings-view actions (Mode == ModeSettings): HitSelectProfile // Profile = index being edited HitNewProfile HitDeleteProfile // Profile = index HitRenameProfile // focus the rename field for Profile = index HitSelectKind // Value = source kind for the add-subscription palette HitAddSub // commit the channel input into the edited profile HitRemoveSub // Profile = index, Sub = subscription index HitFocusChannel // focus the add-channel input HitFocusCache // focus the media-cache path input HitFocusCacheSize // focus the media-cache size-limit (MB) input HitFocusCacheBackend // focus the media-cache backend (plugin path) input HitFocusZoomIn // focus the zoom-in browser-shortcut key input HitFocusZoomOut // focus the zoom-out browser-shortcut key input HitTheme // Value = "system"|"light"|"dark" HitSignInBrowser // Value = "default"|"firefox"|"chrome"|"safari"|"edge" (sign-in browser) HitBrowserTabs // Value = "multi"|"single" (web-preview browser tab mode) HitBrowserChrome // Value = "shown"|"hidden" (web-preview toolbar/urlbar visibility) HitInfiniteScroll // Value = "on"|"off" (fetch next page on scroll-to-bottom) HitDismissPreviewOnSwitch // Value = "on"|"off" (close the preview when the sub/group changes) HitBiometricUnlock // Value = "on"|"off" (store/read secrets behind a biometric gate) HitClearRenderCache // empty the web-preview render cache HitCloseSettings // leave the settings view // Accounts-view actions (Mode == ModeAccounts): HitAccounts // the sidebar 👤 Accounts entry (open the accounts editor) HitCloseAccounts // leave the accounts view (commit) HitSelectAccount // Value = source kind for the provider being edited HitFocusAccountField // Value = credential field key to focus HitToggleAccountBool // Value = bool credential field key to flip (Usenet TLS) HitImportRedditFirefox // the Reddit editor's "Import session from Firefox" button HitImportFollows // a follow-capable provider's "Import subscriptions" button (Value = source kind) HitRedditSignIn // the Reddit editor's "Sign in to Reddit in browser" button HitImportSession // the Instagram/TikTok/X editor's "Import session from Firefox" button // Reddit search view actions (Mode == ModeSearch): HitSearchReddit // the sidebar "🔍 Search Reddit" entry (open the search view) HitCloseSearch // the search view's "‹ Back" button (return to the feed) HitSearchTab // switch the results tab — Value = "subreddits"|"posts" HitFocusSearchQuery // focus the search-query input HitFocusSearchRegex // focus the subreddit-results regexp filter input HitRunSearch // run the search for the current query + tab HitSubscribeChannel // subscribe to a channel result — Item.Source = platform, Value = channel handle HitSubscribePostSearch // save the post keyword search as a live channel — Value = query )
type LogEntry ¶
type LogEntry struct {
When time.Time
Method string
URL string
Status int
Bytes int64
Dur time.Duration
Err string
}
LogEntry is one HTTP exchange as the log view needs it. It mirrors internal/httplog.Entry so the ui package stays free of that dependency; the app converts between the two.
type MediaRequest ¶
MediaRequest names one feed item's remote thumbnail: the item ID the decoded image is keyed by, the URL to fetch it from, and the item's Created time (Unix seconds, UTC; 0 when unknown) so the on-disk media cache can stamp the stored file with the POST's date rather than the download moment.
type Mode ¶
type Mode int
Mode selects which view the scene renders.
const ( ModeFeed Mode = iota // the topbar + sidebar + unified feed ModeDetail // a single item's full detail / reading view ModeSettings // the in-canvas preferences editor ModeLog // the in-canvas HTTP-exchange (Network) log ModeAccounts // the in-canvas per-provider credentials editor ModeBrowse // the in-canvas newsgroup browser / subscribe view ModeSearch // the in-canvas Reddit search / discover view )
type PrefetchRequest ¶
type PrefetchRequest struct {
ID string
Parts []ReconstructPart
}
PrefetchRequest names a shown Usenet image post to prefetch: an id (the feed item id or a group's release base) and the member articles to fetch.
type ReconstructPart ¶
ReconstructPart names one article to fetch for a reconstruction: the bare message-id (no "news:" scheme, no angle brackets) and the file it belongs to.
type Scene ¶
type Scene struct {
W, H int
Items []source.Item // the unified feed (merge newest-first before setting)
Subs []Subscription
Active int // selected subscription index, or AllFilter
Status string
Scale float64 // display scale (zoom × devicePixelRatio); 0 => 1
// Feed-loading triggers, driven by the CardList's own edge accumulators.
// OnReachBottom is wired to the list's OnReachTop (a pull toward the top loads
// OLDER posts, since the newest sits at the bottom); OnPullRefresh is wired to
// the list's OnReachBottom (a pull past the newest re-aggregates). Both are
// nil-safe (only called when installed). infiniteScroll gates whether the
// next-page trigger is offered.
OnReachBottom func()
OnPullRefresh func()
// Profiles are the named sidebar tabs; the active one supplies Subs.
Profiles []settings.Profile
// Optional decoded thumbnails keyed by Item.ID (blitted when present).
Thumbs map[string]*image.RGBA
// contains filtered or unexported fields
}
Scene is the mutable aggregator UI state.
func (*Scene) A11yTree ¶
A11yTree describes what the reader is currently showing, as a flat list in reading order.
It is built from the scene's own laid-out state rather than from a widget tree, because there is no widget tree to walk: every view except the network log composes its widgets inside Draw and discards them. The durable structure is the layout — the same rects HitTest resolves against — which is also the better source: it is what the user can actually perceive and act on.
Tying the two together is deliberate. Anything interactive here carries the rect its hit test uses, so "you can click it" and "a reader can find it" are the same statement; TestA11yNodesAreReachable asserts exactly that by hit-testing every interactive node's centre.
func (*Scene) ActiveFilter ¶
ActiveFilter returns the selected sidebar filter — a subscription index, or AllFilter when no single subscription is selected. Pull-to-refresh reads it so it can re-fetch only the subscription currently in view.
func (*Scene) ActiveProfile ¶
ActiveProfile returns the active profile (a safe fallback when empty).
func (*Scene) ActiveProfileIndex ¶
ActiveProfileIndex returns the active profile index.
func (*Scene) AddInputSub ¶
func (s *Scene) AddInputSub()
AddInputSub appends the typed channel (under the selected source) to the edited profile and clears the field. Duplicates are ignored.
func (*Scene) AdvanceAnim ¶
AdvanceAnim advances the animation clock by n frames and marks the scene dirty. A present loop calls it while [Animating] is true; the indeterminate indicator derives its position from the frame counter. n is the number of tick intervals that have elapsed since the last advance, so a loop that throttles its redraws to below the tick rate (advancing every few ticks instead of every one) still turns the spinner at the same real-time speed — it just steps it in larger, less frequent increments. n <= 0 is a no-op, so a spurious call can never rewind or freeze the clock.
func (*Scene) Animating ¶
Animating reports whether the scene wants a fresh frame every tick (a loading indicator is moving): a feed refresh, OR a preview image / web-page render in flight (whose spinner must keep spinning). When false the scene is idle and a damage-gated present loop never re-draws — so animation costs nothing when nothing is loading.
func (*Scene) AuthPrompts ¶
func (s *Scene) AuthPrompts() []AuthPrompt
AuthPrompts returns the current in-feed sign-in prompts.
func (*Scene) Backspace ¶
func (s *Scene) Backspace()
Backspace removes the last rune of the focused text field.
func (*Scene) BeginFolderRename ¶
BeginFolderRename starts an inline rename of the folder named name: it seeds a focused toolkit.Entry with the current name (caret at the end) as the edit buffer. An unknown folder name is a no-op.
func (*Scene) BeginPreviewResize ¶
func (s *Scene) BeginPreviewResize()
BeginPreviewResize / EndPreviewResize / DraggingPreview drive the preview pane's left-edge divider drag, mirroring the sidebar divider.
func (*Scene) BeginSidebarResize ¶
func (s *Scene) BeginSidebarResize()
BeginSidebarResize starts a divider drag.
func (*Scene) BiometricUnlock ¶
BiometricUnlock reports whether biometric-gated secret storage is enabled.
func (*Scene) BookmarkedURLs ¶
BookmarkedURLs returns the bookmarked URLs in sorted order (stable snapshot for persistence).
func (*Scene) BrowseCaretKey ¶
BrowseCaretKey forwards a caret-navigation key (toolkit codes "ArrowLeft", "ArrowRight", "Home", "End") to the newsgroup filter, so a typo can be corrected mid-text instead of only backspaced from the end. The caller drives it only while the filter is focused; caret movement does not change the filter result, so the list scroll is left alone.
func (*Scene) BrowseEntry ¶
func (s *Scene) BrowseEntry() *toolkit.SearchEntry
BrowseEntry exposes the filter widget so the app can two-way bind it to the view-model like the topbar search (mvvm.BindField).
func (*Scene) BrowseFocused ¶
BrowseFocused reports whether the filter field holds keyboard focus.
func (*Scene) BrowseGroups ¶
BrowseGroups returns the loaded group list.
func (*Scene) BrowseNodeExpanded ¶
BrowseNodeExpanded reports whether the node with the given name is expanded.
func (*Scene) BrowseSelectedNode ¶
BrowseSelectedNode reports the keyboard-selected tree node: its full name, whether it has children (an expandable hierarchy), whether a real group exists at it, and whether that group is already subscribed. ok is false when there is no selectable row.
func (*Scene) Browser ¶
Browser exposes the embedded web-preview browser so the app can wire its OnNavigate render seam and drive it (Open / Deliver).
func (*Scene) BrowserChromeHidden ¶
BrowserChromeHidden reports whether the embedded browser's chrome is hidden.
func (*Scene) BrowserFocused ¶
BrowserFocused reports whether the embedded browser holds keyboard focus.
func (*Scene) BrowserSingleTab ¶
BrowserSingleTab reports whether the embedded browser is in single-tab mode.
func (*Scene) BrowserVM ¶
BrowserVM exposes the browser's MVVM view-model (observable URL / loading / history-guards and the Back/Forward/Reload commands).
func (*Scene) BrowserZoomInKey ¶
BrowserZoomInKey / BrowserZoomOutKey return the configured base keys as 1-rune strings ("" when unset) for the settings snapshot and the settings editor.
func (*Scene) BrowserZoomOutKey ¶
func (*Scene) CacheBackend ¶
CacheBackend returns the media-cache backend selector ("" / "local" for the built-in disk cache, or a filesystem path to a cache-plugin binary).
func (*Scene) CancelFolderRename ¶
func (s *Scene) CancelFolderRename()
CancelFolderRename abandons an in-progress inline rename, discarding the edit buffer and leaving the folder's name unchanged.
func (*Scene) ChannelResults ¶
func (s *Scene) ChannelResults() []source.ChannelResult
ChannelResults / PostResults expose the delivered results (front-ends/tests).
func (*Scene) ClearPendingSource ¶
ClearPendingSource clears the pending marker for one source+channel once it has returned (with items or an error).
func (*Scene) ClearSelection ¶
func (s *Scene) ClearSelection()
ClearSelection discards the current selection.
func (*Scene) CloseAccounts ¶
func (s *Scene) CloseAccounts()
CloseAccounts returns to the feed view.
func (*Scene) CloseDetail ¶
func (s *Scene) CloseDetail()
CloseDetail returns from the detail view to the feed.
func (*Scene) CloseLog ¶
func (s *Scene) CloseLog()
CloseLog returns from the Network-log view to the feed.
func (*Scene) CloseSettings ¶
func (s *Scene) CloseSettings()
CloseSettings returns to the feed view.
func (*Scene) CommentsLoading ¶
CommentsLoading reports whether a comment fetch is in flight for item id.
func (*Scene) CommitCache ¶
func (s *Scene) CommitCache()
CommitCache applies the cache-path buffer.
func (*Scene) CommitCacheBackend ¶
func (s *Scene) CommitCacheBackend()
CommitCacheBackend applies the cache-backend buffer: blank / "local" keeps the built-in disk cache, a filesystem path selects an external cache plugin. The change takes effect on the next launch (the backend is chosen at startup), like the plugins directory.
func (*Scene) CommitCacheSize ¶
func (s *Scene) CommitCacheSize()
CommitCacheSize applies the media-cache size-limit (MB) buffer: on a valid integer it sets the limit, clamped to [MinMediaCacheMB, MaxMediaCacheMB] (0 → DefaultMediaCacheMB); on a parse error the current value is kept. Either way the buffer is re-synced to the resulting value so the field shows the real, clamped limit rather than the raw typed text.
func (*Scene) CommitFolderRename ¶
func (s *Scene) CommitFolderRename()
CommitFolderRename applies the inline rename when the edit buffer is non-blank and actually changed, then clears the rename state. The window layer persists the settings afterwards (App.PersistSettings), matching the other folder edits.
func (*Scene) CommitRename ¶
func (s *Scene) CommitRename()
CommitRename applies the rename buffer to the edited profile (blank ignored).
func (*Scene) CommitZoomKeys ¶
func (s *Scene) CommitZoomKeys()
CommitZoomKeys applies the two zoom-key buffers (each a single printable rune) to the browser zoom bindings. A blank buffer leaves that binding unchanged.
func (*Scene) ContextMenuActive ¶
ContextMenuActive reports whether a context menu is currently popped up, so the input path can route events to it instead of the scene beneath.
func (*Scene) ContextMenuEvent ¶
ContextMenuEvent forwards one input event to the open context menu and drops the menu once it has closed itself (an item fired, a click landed outside, or Escape was pressed). It is a no-op when no menu is open.
func (*Scene) Copy ¶
Copy copies the current context to the system clipboard, reporting whether anything was copied. Precedence, most specific first: the focused web-preview address bar (so Cmd/Ctrl+C copies the URL you are editing, with a select-all highlight); then a non-empty reading-view text selection (the user picked exactly what to copy); then the whole article being read (detail item, else the preview item) as plain text.
func (*Scene) DeleteFolder ¶
DeleteFolder removes the folder named name; its subscriptions return to the sidebar root. An unknown name is a no-op.
func (*Scene) DeleteProfile ¶
DeleteProfile removes profile i (never below one profile), re-clamps the active + edited indices, and rebuilds the sidebar.
func (*Scene) DismissPreviewOnSwitch ¶
DismissPreviewOnSwitch reports the current dismiss-preview-on-switch setting.
func (*Scene) Downloads ¶
func (s *Scene) Downloads() []DownloadItem
Downloads returns the current download list.
func (*Scene) DraggingPreview ¶
func (*Scene) DraggingSidebar ¶
DraggingSidebar reports whether the divider is being dragged.
func (*Scene) EditedAccounts ¶
EditedAccounts projects the editable buffers back into persisted accounts, in the schema's stable order, dropping providers (and individual fields) whose values are blank so an untouched provider leaves no empty account behind.
func (*Scene) EndPreviewResize ¶
func (s *Scene) EndPreviewResize()
func (*Scene) EndSidebarResize ¶
func (s *Scene) EndSidebarResize()
EndSidebarResize ends a divider drag.
func (*Scene) FeedAnimating ¶
FeedAnimating reports whether the feed CardList's fetch strips are spinning (a fetch is in flight), so a gated present loop keeps ticking the feed.
func (*Scene) FeedCardList ¶
FeedCardList exposes the feed's virtual.CardList (front-ends/tests drive and inspect it: bounds, scroll, events, animation).
func (*Scene) FeedClickAt ¶
FeedClickAt routes a screen-space click into the feed. A click over an EXPANDED group card's member row previews that member article directly (its own item, like the old member row) and stops there; every other click goes to the CardList (selecting the card under the pointer, translating to the list's widget-local coordinates). A click above the list origin (e.g. on a pinned banner) maps to a negative local Y the CardList treats as no card.
func (*Scene) FeedEvent ¶
FeedEvent routes one input event into the feed CardList (wheel scroll, selection keys, or a widget-local click), setting the keyboard-origin flag around it so the select hook can tell a cursor move from a click. localY is only read for a click (feed-relative). It is the input seam that replaces the hand-drawn feed's wheel / arrow / hit-test handling.
func (*Scene) FeedGeom ¶
feedGeom returns the feed list's left origin and width, accounting for the sidebar on the left and the preview pane on the right. Every feed layout / draw / hit-test path goes through it so the three stay consistent. FeedGeom is the feed pane's horizontal extent in scene pixels: where the cards, the scrollbar and the empty-state label all live. Exported so a caller -- a test, a host laying something over the pane -- can ask for the geometry the renderer actually uses instead of recomputing it and being subtly wrong.
func (*Scene) FeedModelItems ¶
FeedModelItems returns the feed model's current items (oldest→newest), a stable snapshot for tests/inspection.
func (*Scene) FeedScrollOffset ¶
FeedScrollOffset exposes the feed CardList's current pixel scroll offset. The window-app layer, the scrollbar and the a11y tree read it to map feed content to screen space.
func (*Scene) FeedScrollToBottom ¶
func (s *Scene) FeedScrollToBottom()
FeedScrollToBottom opens the feed at its newest (bottom) post — the app calls it after switching tabs / filters (it is also armed automatically by SetActive / SetItems).
func (*Scene) FeedTick ¶
FeedTick advances the feed CardList's pull-to-fetch spinners by dt seconds through the toolkit animation tree, and reports whether the feed still wants frames. A present loop drives it while the feed is fetching.
func (*Scene) FeedWheel ¶
FeedWheel routes a device-pixel wheel delta into the feed CardList as a signed whole-row scroll (magnitude ≥ 1 for any non-zero nudge, so a small notch still advances a card). The CardList owns the scroll anchor and the infinite-scroll / pull-to-refresh accumulators, so this is the only feed-wheel path.
func (*Scene) FinishPreviewImage ¶
FinishPreviewImage stores the reconstructed image (when non-nil) and clears the pane's loading state if the fetch was for the item still being previewed. The image is keyed by id, so a late result for a since-deselected item is cached (for its card thumbnail) without disturbing the current selection.
func (*Scene) FocusAccountField ¶
FocusAccountField gives keyboard focus to a credential field.
func (*Scene) FocusBrowseFilter ¶
FocusBrowseFilter gives (or removes) keyboard focus to the filter field.
func (*Scene) FocusCache ¶
func (s *Scene) FocusCache()
func (*Scene) FocusCacheBackend ¶
func (s *Scene) FocusCacheBackend()
FocusCacheBackend gives keyboard focus to the cache-backend (plugin path) field.
func (*Scene) FocusCacheSize ¶
func (s *Scene) FocusCacheSize()
FocusCacheSize gives keyboard focus to the media-cache size-limit (MB) field.
func (*Scene) FocusChannel ¶
func (s *Scene) FocusChannel()
func (*Scene) FocusRename ¶
func (s *Scene) FocusRename()
FocusRename / FocusChannel / FocusCache give keyboard focus to a text field.
func (*Scene) FocusSearch ¶
FocusSearch gives (or removes) keyboard focus to the search field. A focus change dismisses the copied-highlight.
func (*Scene) FocusSearchQuery ¶
FocusSearchQuery gives (or removes) keyboard focus to the query field. Focusing the query blurs the regexp field so keystrokes never land in both.
func (*Scene) FocusSearchRegex ¶
FocusSearchRegex gives (or removes) keyboard focus to the regexp filter field.
func (*Scene) FocusSettingsField ¶
FocusSettingsField gives keyboard focus to a settings field by name. The windowapp Handler calls it before commitSettingsField so a native entry's Enter commits the field the person was editing even if no keystroke set focus first.
func (*Scene) FocusZoomIn ¶
func (s *Scene) FocusZoomIn()
FocusZoomIn / FocusZoomOut give keyboard focus to a zoom-key field.
func (*Scene) FocusZoomOut ¶
func (s *Scene) FocusZoomOut()
func (*Scene) FolderCollapsed ¶
FolderCollapsed reports whether the folder named name is currently collapsed (for tests / front-ends).
func (*Scene) FolderNames ¶
FolderNames returns the folder names in their current order (for the "Move to folder ▸" submenu).
func (*Scene) FolderRenaming ¶
FolderRenaming reports whether a folder is currently being renamed inline.
func (*Scene) FollowImportKinds ¶
FollowImportKinds returns the source kinds currently marked follow-capable, in lexical order — the set Scene.SetFollowImportKinds last recorded. It lets a caller (and tests) read back which providers offer the "Import subscriptions" action without going through the accounts-editor hit test.
func (*Scene) ForwardBrowserClick ¶
ForwardBrowserClick forwards a click at screen coords (x, y) into the embedded browser when the web preview is active and the point lands inside the widget, translating to widget-local coordinates. It returns true when the browser consumed the click (so the caller does no further routing). A click outside the widget (or when the browser is not shown) blurs the browser's keyboard focus and returns false so the caller falls through to its own hit-testing.
func (*Scene) ForwardBrowserDrag ¶
ForwardBrowserDrag forwards pointer motion at screen coords (x, y) into the embedded browser as an EventMouseDrag, but ONLY while a browser press is active (a prior ForwardBrowserClick consumed a press inside the widget). This is what lets a grabbed scrollbar thumb — vertical OR horizontal — follow the mouse. It returns true when the drag was routed to the browser. A drag with no active browser press is ignored (returns false) so the caller keeps its own drag handling (e.g. text selection or a pane-divider resize).
func (*Scene) ForwardBrowserKey ¶
ForwardBrowserKey forwards an editing key or printable rune into the embedded browser's address field when the web preview is active and the browser holds keyboard focus (a prior click landed inside it). Enter commits/navigates the address, Backspace edits it, and a printable rune types into it. Returns true when the key was routed to the browser.
func (*Scene) ForwardBrowserRelease ¶
ForwardBrowserRelease forwards a mouse-button release into the embedded browser when the web preview is active, so a toolbar button pressed on the preceding click clears its press-feedback face. Coordinates are screen-space (translated to widget-local); a release anywhere clears the press.
func (*Scene) ForwardBrowserScroll ¶
ForwardBrowserScroll forwards a wheel delta (device px) into the embedded browser when the web preview is active and the pointer is over the widget, converting pixels to the widget's row-based scroll intent. Returns true when the browser consumed the wheel. It is the plain (unshifted) wheel path — a convenience over ForwardBrowserScrollShift for callers with no modifier state.
func (*Scene) ForwardBrowserScrollShift ¶
ForwardBrowserScrollShift is ForwardBrowserScroll with the Shift modifier: when shift is true the wheel is delivered to the browser as a shifted EventScroll, which the toolkit Browser interprets as HORIZONTAL scrolling (the vertical bar has a plain-wheel path; the horizontal bar rides shift+wheel). The drag path (ForwardBrowserDrag) already moves either scrollbar thumb; this adds the wheel affordance once a back-end can report the Shift state on a scroll event.
func (*Scene) GIFPlaying ¶
GIFPlaying reports whether an animated GIF is currently being driven into the preview (front-ends/tests observe it).
func (*Scene) GroupExpanded ¶
GroupExpanded reports whether the Usenet post group with the given release base is currently expanded (its member parts listed beneath the header).
func (*Scene) GroupParts ¶
func (s *Scene) GroupParts(base string) ([]ReconstructPart, bool)
GroupParts returns the member articles of the currently-displayed group with the given base, ready to hand to the Usenet provider's Reconstruct. It reports false when no such group is shown.
func (*Scene) GroupPreviewItem ¶
GroupPreviewItem synthesises a source.Item to preview a Usenet multipart post (a group card, which is not itself a single Item): its release base as title, a declared image attachment so the pane reserves the image box, and the id set to the base so the app can key the reconstructed thumbnail by it. Reports false when no shown group has that base.
func (*Scene) HasGroupStats ¶
HasGroupStats reports whether a sampled estimate is already cached for name (the app checks it before kicking off a scan, avoiding a re-scan).
func (*Scene) HasSelection ¶
HasSelection reports whether a non-empty text selection is active.
func (*Scene) ImagePrefetch ¶
func (s *Scene) ImagePrefetch() []PrefetchRequest
ImagePrefetch returns a request for every shown Usenet post likely to carry an image — a multipart group (binary post) and a standalone article whose subject names an image file — so the app can download their thumbnails in parallel.
func (*Scene) InfiniteScroll ¶
InfiniteScroll reports whether the bottom-of-feed next-page trigger is enabled.
func (*Scene) InvalidateBrowse ¶
func (s *Scene) InvalidateBrowse()
InvalidateBrowse resets the scroll after the filter binder writes BrowseEntry.Text directly; passed as mvvm.BindField's invalidate hook.
func (*Scene) InvalidateSearch ¶
func (s *Scene) InvalidateSearch()
InvalidateSearch bumps the damage sequence after the search binder writes SearchEntry.Text directly (bypassing SetSearch). The app's search binder passes it as mvvm.BindField's invalidate hook so a ViewModel-originated search change repaints the topbar (whose sprite cache keys on the widget text).
func (*Scene) IsBookmarked ¶
IsBookmarked reports whether url is in the bookmark set.
func (*Scene) IsDownloadQueued ¶
IsDownloadQueued reports whether id has an in-flight (queued or active) download — the checked state of its group card checkbox.
func (*Scene) IsPendingSub ¶
IsPendingSub reports whether the given source+channel is still loading.
func (*Scene) IsSubscribed ¶
IsSubscribed reports whether the active profile already subscribes to the given source+channel (case-insensitive on the channel).
func (*Scene) LayoutPreview ¶
func (s *Scene) LayoutPreview()
LayoutPreview lays out the preview pane on demand, outside a Draw. The app calls it right after a selection changes so the embedded browser has its real content bounds (and thus its real render width) BEFORE it fires a web-page open: without it the first open reads the stale/empty bounds left by the prior frame and renders — and caches — the page at width 0, so revisiting it later (now measured at the real width) misses the render cache and re-fetches. It is idempotent — the next Draw recomputes the same layout — so calling it early is harmless.
func (*Scene) LoadingProgress ¶
LoadingProgress returns how many sources have returned and the total.
func (*Scene) LogEntries ¶
LogEntries exposes the current log entries (for front-ends/tests).
func (*Scene) MediaCacheMB ¶
MediaCacheMB returns the media cache size limit, in MB.
func (*Scene) MediaPrefetch ¶
func (s *Scene) MediaPrefetch() []MediaRequest
MediaPrefetch lists the remote thumbnails the currently shown feed is still missing: one per item that declares media, names it over http(s), and has no decoded thumbnail yet.
Usenet posts are excluded — their images arrive over NNTP through Scene.ImagePrefetch, which was the ONLY path that ever populated Thumbs. So every card from every other source drew the "image"/"video" placeholder forever, however well its provider had reported the media. This is the list the app fetches to close that gap.
func (*Scene) MouseMove ¶
MouseMove applies a divider drag: while dragging, the pointer's x becomes the new sidebar width (clamped when read). It is a no-op otherwise, so front-ends can forward every pointer move unconditionally.
func (*Scene) MoveSubToFolder ¶
MoveSubToFolder moves the subscription at sub index into the folder named folderName (creating the folder if it does not yet exist), removing the subscription from any other folder first. A bad sub index is a no-op.
func (*Scene) NativeControls ¶
func (s *Scene) NativeControls() []toolkit.NativeControl
NativeControls returns the native controls accumulated over the last Draw, in visual order. It is what the windowapp Handler hands the platform through the application Surface's Controls field.
func (*Scene) NativeHit ¶
NativeHit returns the Hit a native button's activation should run, if the key names one. The windowapp Handler uses it to give each button descriptor an OnActivate that dispatches through the same path a click would.
func (*Scene) NativeSettingsCommit ¶
NativeSettingsCommit reports the settings field a native entry's Enter should commit, if the key names one. The windowapp Handler uses it to give the descriptor an OnActivate that focuses that field and runs commitSettingsField — the same path a keyboard Enter takes when the drawn field has focus.
func (*Scene) NavBrowse ¶
NavBrowse moves the keyboard selection dir rows through the visible tree (dir<0 up, dir>0 down), clamped to the current rows, and scrolls it into view.
func (*Scene) NewProfile ¶
func (s *Scene) NewProfile()
NewProfile appends a fresh profile and selects it for editing.
func (*Scene) NewSidebarFolder ¶
NewSidebarFolder creates a new (empty) folder with a unique name derived from base (blank → "New Folder") and returns its final name.
func (*Scene) OpenAccounts ¶
func (s *Scene) OpenAccounts()
OpenAccounts enters the credentials editor (defaulting to Reddit, the reason the feature exists).
func (*Scene) OpenBrowse ¶
func (s *Scene) OpenBrowse()
OpenBrowse enters the newsgroup browser view.
func (*Scene) OpenContextMenu ¶
func (s *Scene) OpenContextMenu(m *toolkit.ContextMenu, x, y int)
OpenContextMenu pops the given toolkit ContextMenu up at (x, y). The menu is bounded to the whole surface so it clamps itself on screen and treats a click outside its body as a dismissal. The scene owns none of the menu's behaviour — it only holds it so Draw can paint it and events can reach it.
func (*Scene) OpenDetail ¶
OpenDetail switches to the in-app reading view for it (instead of a browser).
func (*Scene) OpenSearch ¶
func (s *Scene) OpenSearch()
OpenSearch enters the Reddit search view, resetting the scroll and focus.
func (*Scene) OpenSettings ¶
func (s *Scene) OpenSettings()
OpenSettings enters the preferences view, editing the active profile.
func (*Scene) PendingCount ¶
PendingCount returns how many sources are still loading.
func (*Scene) PostResults ¶
func (*Scene) PrefetchWebURLs ¶
PrefetchWebURLs returns up to n distinct web-renderable target URLs for the feed items immediately adjacent to it (previous, then next, then the ones just beyond), for background pre-rendering so selecting a neighbour is instant. It has no side effects — it does not scroll or change the selection, unlike [Scene.NavItem]. Items with no web target (Usenet posts, text-only posts) are skipped; a mismatch (it not in the feed) yields nothing.
func (*Scene) PreviewComments ¶
PreviewComments returns the fetched comment thread for item id and whether a fetch has completed (an entry exists, possibly empty). Front-ends/tests use it to observe what the app delivered.
func (*Scene) PreviewFocusable ¶
PreviewFocusable reports whether the preview pane can currently take keyboard focus: it is laid out (visible) and has a selected item to scroll.
func (*Scene) PreviewFocused ¶
PreviewFocused reports whether the preview pane holds keyboard focus (so the scroll keys drive it rather than the feed card list).
func (*Scene) PreviewItem ¶
PreviewItem returns the previewed item and whether one is selected.
func (*Scene) PreviewOpenButton ¶
PreviewOpenButton returns the preview pane's "Open" button rect and whether it is currently shown (an item is selected and the pane is visible). It reflects the last layout; front-ends/tests call it after a Draw/HitTest.
func (*Scene) PreviewTextScale ¶
PreviewTextScale returns the current reader/preview text zoom multiplier.
func (*Scene) RemoveSubFromFolder ¶
RemoveSubFromFolder takes the subscription at sub index out of whatever folder holds it, so it returns to the sidebar root. A bad index (or a subscription in no folder) is a no-op beyond the redraw.
func (*Scene) RenameFolder ¶
RenameFolder renames the folder named old to newName (unique-ified), carrying its collapse state across. A blank new name, an unknown old name, or old == new is a no-op.
func (*Scene) RenamingFolder ¶
RenamingFolder returns the folder currently being renamed inline, or "" when none is (for tests / front-ends).
func (*Scene) Rev ¶
Rev returns the current damage/commit sequence. It advances whenever any state that affects the rendered frame changes, so a double-buffered present loop can skip redundant redraws/uploads.
func (*Scene) Scroll ¶
Scroll adjusts the vertical scroll of whichever view is showing, clamped to its content height.
func (*Scene) ScrollPreviewKey ¶
ScrollPreviewKey scrolls the focused preview pane by one keyboard step: dir<0 scrolls up, dir>0 down; page requests a (near) full viewport move (PageUp / PageDown / Space). For a web-linked item it forwards to the embedded browser; for a text/image item it nudges previewScroll. It consumes the key (returns true) whenever the pane holds focus — even with nothing to scroll — so the key does not fall through to the feed selection. It is a no-op (false) when the pane is not focused or dir is 0.
func (*Scene) ScrollY ¶
ScrollY exposes the feed's current scroll offset (used by the window app layer). It reads the virtual.CardList's pixel scroll offset.
func (*Scene) SearchEntry ¶
func (s *Scene) SearchEntry() *toolkit.SearchEntry
SearchEntry exposes the topbar's search widget so the app can two-way bind vm.Search to it (mvvm.BindField(&entry.Text, &entry.OnChange)).
func (*Scene) SearchFocused ¶
SearchFocused reports whether the search field has keyboard focus.
func (*Scene) SearchLoading ¶
SearchLoading reports whether a search request is in flight.
func (*Scene) SearchQuery ¶
SearchQuery returns the trimmed query text.
func (*Scene) SearchQueryFocused ¶
SearchQueryFocused / SearchRegexFocused report which field holds focus.
func (*Scene) SearchRegex ¶
SearchRegex returns the trimmed subreddit-results regexp filter text.
func (*Scene) SearchRegexFocused ¶
func (*Scene) SearchStatus ¶
SearchStatus returns the current search status line.
func (*Scene) SearchTabPosts ¶
SearchTabPosts reports whether the post-search tab is active (else subreddits).
func (*Scene) SelectAccount ¶
SelectAccount picks which provider the editor operates on.
func (*Scene) SelectEditProfile ¶
SelectEditProfile picks which profile the editor operates on (clamped) and seeds the rename buffer with its name.
func (*Scene) SelectKind ¶
SelectKind records which source the add-subscription palette will use.
func (*Scene) SelectPreview ¶
SelectPreview loads it into the preview pane (clicking a feed item), resetting the pane scroll. The app calls SetPreviewLoading before an async image fetch.
func (*Scene) SelectableAt ¶
SelectableAt reports whether a press at screen point (x, y) should begin a text selection: the reading surfaces whose runs are collected each frame — the full-screen detail view, or the preview pane's text summary (not the embedded web browser, which handles its own selection). A front-end consults it before starting a drag-selection.
func (*Scene) SelectedAccount ¶
SelectedAccount reports which provider the accounts editor is operating on.
func (*Scene) SelectedBrowseGroup ¶
SelectedBrowseGroup returns the name of the group under the browser's keyboard selection, and whether the selected row is a group (not a hierarchy node). The app uses it to scan the focused group's content mix.
func (*Scene) SelectionBegin ¶
SelectionBegin starts a text selection at screen point (x, y) over the runs of the reading surface drawn last frame. A press with no drag leaves the selection empty (so a plain click clears any prior selection).
func (*Scene) SelectionEnd ¶
func (s *Scene) SelectionEnd()
SelectionEnd finishes a drag; the selection stays in place for a copy.
func (*Scene) SetAccountField ¶
SetAccountField writes value into provider k's credential buffer for key, lazily allocating. It lets the app inject a credential obtained outside the editor — e.g. a reddit_session cookie imported from Firefox — so the editor reflects it and a commit persists it.
func (*Scene) SetAccounts ¶
SetAccounts seeds the editable credential buffers from the persisted accounts.
func (*Scene) SetActive ¶
SetActive selects the sidebar filter (a subscription index, or AllFilter). A filter switch opens the (re-filtered) feed at its newest post.
func (*Scene) SetActiveProfile ¶
SetActiveProfile switches the active profile (clamped) and rebuilds the sidebar subscriptions from it.
func (*Scene) SetAuthPrompts ¶
func (s *Scene) SetAuthPrompts(p []AuthPrompt)
SetAuthPrompts replaces the set of providers that need sign-in/configuration, as collected by the app after an aggregate. The feed renders one clickable banner per prompt above the cards; an empty slice removes the banner.
func (*Scene) SetBiometricUnlock ¶
SetBiometricUnlock records whether the account secrets are stored/read behind a biometric user-presence gate (Touch ID on macOS). The app persists it and re-writes the secrets so the gate takes effect.
func (*Scene) SetBookmarked ¶
SetBookmarked adds or removes url from the bookmark set (lazily allocating).
func (*Scene) SetBookmarks ¶
SetBookmarks seeds the bookmarked-URL set from the persisted settings.
func (*Scene) SetBrowseGroups ¶
SetBrowseGroups stores the server's full active group list (name + estimated post count — the browse view's data source) and invalidates the cached tree so the next layout rebuilds it.
func (*Scene) SetBrowserChromeHidden ¶
SetBrowserChromeHidden hides (v=true) or shows the embedded browser's toolbar + tab strip, so a page can render chrome-free in the preview.
func (*Scene) SetBrowserSingleTab ¶
SetBrowserSingleTab selects the embedded browser's tab mode: single-tab reuses one tab per page (no strip), multi-tab (the default) keeps a switchable strip.
func (*Scene) SetBrowserZoomKeys ¶
SetBrowserZoomKeys configures the base runes that, held with a command-style modifier (Ctrl/Cmd), zoom the embedded web preview in / out. A blank or multi-rune string leaves the corresponding binding unchanged, so a missing persisted value keeps the '='/'-' default.
func (*Scene) SetCacheBackend ¶
SetCacheBackend records the media-cache backend selector.
func (*Scene) SetCachePath ¶
SetCachePath records the media cache directory.
func (*Scene) SetChannelResults ¶
func (s *Scene) SetChannelResults(rs []source.ChannelResult)
SetChannelResults delivers channel-discovery results (subreddits, accounts, hashtags — whatever the searched platforms returned), switches to the channels tab, clears loading and resets the scroll.
func (*Scene) SetComments ¶
SetComments attaches the flattened comment thread for an item id (the app delivers it after an async fetch) and clears that id's loading marker, so the preview pane paints the thread in place of the "loading comments" line. A nil slice is stored as an explicit "fetched, none" so the pane shows "0 comments" rather than spinning.
func (*Scene) SetCommentsLoading ¶
SetCommentsLoading marks (v=true) or clears that a comment fetch is in flight for item id, so the pane shows a loading line beneath the post until SetComments lands.
func (*Scene) SetDismissPreviewOnSwitch ¶
SetDismissPreviewOnSwitch controls whether switching to another subscription/group closes the preview pane (see SetActive).
func (*Scene) SetDownloads ¶
func (s *Scene) SetDownloads(items []DownloadItem)
SetDownloads replaces the download list (the app pushes the full snapshot).
func (*Scene) SetFeedSelectHook ¶
SetFeedSelectHook installs the app callback fired when a feed card is selected (click or keyboard). Its bool argument is true when the selection came from a keyboard cursor move, so the app can debounce a web render. Passing nil restores the scene's own preview-only behaviour.
func (*Scene) SetFollowImportKinds ¶
SetFollowImportKinds records which source kinds can import the connected account's follows (their provider implements source.FollowImporter), so the accounts editor shows the "Import subscriptions" affordance for exactly those. The app calls it whenever it (re)builds the provider registry.
func (*Scene) SetGIFPlaying ¶
SetGIFPlaying marks whether the app is currently driving an animated GIF's frames into the embedded browser. While true, Animating stays true so the present loop keeps ticking and the GIF advances; the app clears it when the preview navigates away or closes.
func (*Scene) SetGroupStats ¶
func (s *Scene) SetGroupStats(name string, st source.GroupStats)
SetGroupStats stores the sampled content-mix estimate for a group and repaints so its browser row picks up the "~B% bin · ~I% img" suffix.
func (*Scene) SetInfiniteScroll ¶
SetInfiniteScroll enables or disables fetching the next page of posts when the feed list is scrolled to the bottom (the OnReachBottom trigger). Pull-to-refresh is always on and unaffected — only the bottom trigger is gated.
func (*Scene) SetLoading ¶
SetLoading sets the live-loading feedback. active marks a refresh as in progress (which drives the in-feed indicator and the per-frame animation); done/total report how many sources have returned. Turning loading off clears the pending-source markers so nothing is left dimmed.
func (*Scene) SetLogSource ¶
SetLogSource installs the callback the log view queries each frame for the current exchanges (newest-first). Nil leaves the view empty.
func (*Scene) SetMediaCacheMB ¶
SetMediaCacheMB records the media cache size limit, in MB.
func (*Scene) SetPendingSources ¶
func (s *Scene) SetPendingSources(subs []source.Subscription)
SetPendingSources marks every given subscription as still-loading, so the sidebar rows for those sources show a pending marker until they return.
func (*Scene) SetPostResults ¶
SetPostResults delivers post-search results, switches to the posts tab, clears loading and resets the scroll.
func (*Scene) SetPreviewLoading ¶
SetPreviewLoading marks (or clears) that an image fetch is in flight for the previewed item, so the pane shows a spinner until SetThumb lands.
func (*Scene) SetPreviewTextScale ¶
SetPreviewTextScale sets the reader/preview text zoom (a text post's title, body and meta, and the header of any previewed post), clamped to the supported range, and requests a redraw. The A−/A+ preview-toolbar pills drive it.
func (*Scene) SetProfiles ¶
SetProfiles replaces the profile list and selects the active index (clamped), deriving the sidebar subscriptions from the newly-active profile.
func (*Scene) SetRenderCacheStats ¶
SetRenderCacheStats installs the callback the Settings view queries for the web-preview render cache's current size (pages, bytes). Nil shows an empty cache in the caption.
func (*Scene) SetSearchLoading ¶
SetSearchLoading marks whether a Reddit search request is in flight (folded into Animating so the spinner keeps ticking) and clears the status while busy.
func (*Scene) SetSearchStatus ¶
SetSearchStatus sets an informational/error line for the search view (e.g. "No results" or a provider message) and clears the loading flag.
func (*Scene) SetSearchTab ¶
SetSearchTab selects the subreddit-discovery or post-search results tab and resets the result scroll.
func (*Scene) SetSeen ¶
SetSeen records the per-subscription seen markers (from the app's persisted store) and re-renders the sidebar so the unseen counts update.
func (*Scene) SetSidebarFolders ¶
SetSidebarFolders replaces the virtual-folder set (seeded from the persisted settings at startup) and re-renders the sidebar.
func (*Scene) SetSignInBrowser ¶
SetSignInBrowser records the sign-in browser choice (unrecognised values fall back to the default so the picker can never persist a bad value).
func (*Scene) SetStatus ¶
SetStatus sets the status-line message and marks the scene dirty. The Status field stays exported (the renderer reads it directly); this setter is the write path the view-model binds to, so a status change triggers a redraw on its own rather than relying on an adjacent SetItems/SetLoading to touch.
func (*Scene) SetSubs ¶
func (s *Scene) SetSubs(subs []Subscription)
SetSubs replaces the sidebar subscriptions.
func (*Scene) SetThemeName ¶
SetThemeName records the theme choice (the host resolves it to a palette).
func (*Scene) SetThumb ¶
SetThumb attaches a decoded thumbnail for an item so the next Draw picks it up (the CardList re-renders the card from the live thumbnail cache each frame).
func (*Scene) SetUsenetServer ¶
SetUsenetServer records the configured Usenet server address. A non-empty value gates the sidebar "Browse newsgroups" entry and titles the browse view.
func (*Scene) SidebarCollapsed ¶
SidebarCollapsed reports whether the sidebar is currently collapsed.
func (*Scene) SidebarContextAt ¶
func (s *Scene) SidebarContextAt(x, y int) SidebarContext
SidebarContextAt maps a screen point to the sidebar context-menu target under it: a subscription, a folder, or nothing. The window layer calls it on a secondary click.
func (*Scene) SidebarFolders ¶
SidebarFolders returns a copy of the current virtual-folder set (a stable snapshot for the settings persistence layer).
func (*Scene) SidebarSourceOpen ¶
SidebarSourceOpen reports which source group is currently expanded in the sidebar accordion, or "" when they are all collapsed (for tests / front-ends).
func (*Scene) SignInBrowser ¶
SignInBrowser returns the browser launched for a provider sign-in flow (default|firefox|chrome|safari|edge).
func (*Scene) SubAt ¶
func (s *Scene) SubAt(index int) (Subscription, bool)
SubAt returns the active-profile subscription at index, or ok=false when the index is out of range (e.g. AllFilter). The Handler uses it to name the source a sidebar right-click landed on.
func (*Scene) SubMarker ¶
SubMarker returns the subscription's key and current marker so the app can persist it as "seen" when the group is opened. ok is false for a bad index.
func (*Scene) SubscribeActive ¶
SubscribeActive adds a source+channel subscription to the active profile (with the standard 25-item limit) and rebuilds the sidebar, reporting whether it was added (false when it was already present or there is no active profile). The app persists + re-aggregates after a true return.
func (*Scene) SyncBookmarkStar ¶
func (s *Scene) SyncBookmarkStar()
SyncBookmarkStar sets the address-bar star to reflect whether the browser's current page is bookmarked (called after each navigation).
func (*Scene) TextZoomButtons ¶
TextZoomButtons returns the A−/A+ preview-toolbar pill rects and whether they are shown (an item is previewed and the pane is visible). Front-ends/tests call it after a Draw/HitTest.
func (*Scene) ToggleAccountBool ¶
ToggleAccountBool flips a boolean credential field (e.g. Usenet TLS).
func (*Scene) ToggleBrowseNode ¶
ToggleBrowseNode expands or collapses the tree node with the given full name.
func (*Scene) ToggleGroup ¶
ToggleGroup expands or collapses the Usenet post group with the given release base, then re-measures and re-lays-out its feed row so the CardList accounts for the members appearing or disappearing beneath the header. It is the action a click on the group card's disclosure chevron runs.
func (*Scene) TogglePreviewFocus ¶
TogglePreviewFocus flips keyboard focus between the feed card list and the preview pane (the Tab key). Focus is only granted when the pane is focusable; otherwise it stays on the feed and the call is a no-op. It returns the new focused state.
func (*Scene) ToggleSidebar ¶
func (s *Scene) ToggleSidebar()
ToggleSidebar collapses or expands the sidebar (transient UI chrome, not persisted).
func (*Scene) ToggleSidebarFolder ¶
ToggleSidebarFolder flips a folder's collapse state (session-only). It is the action a click on a folder row runs.
func (*Scene) ToggleSidebarSource ¶
ToggleSidebarSource drives the sidebar's source accordion: it expands kind's auto-group (collapsing whatever source was open), or collapses it if it was already the open one. Session-only, the action a click on a source header row runs; only one source group is ever open, keeping a huge subscription list short.
func (*Scene) TypeRune ¶
TypeRune appends r to whichever text field currently has focus: the topbar search (feed view) or the channel/rename/cache field (settings view).
func (*Scene) UnsubscribeActive ¶
UnsubscribeActive removes the source+channel subscription from the active profile (case-insensitive on the channel) and rebuilds the sidebar, reporting whether one was removed (false when absent or there is no active profile). The app persists + re-aggregates after a true return.
func (*Scene) UsenetServer ¶
UsenetServer returns the configured Usenet server address ("" when none).
func (*Scene) WebPreviewActive ¶
WebPreviewActive reports whether the embedded browser is the active preview content — a web-linked item is selected. Front-ends check it before routing a raw mouse/scroll/key event into the browser widget.
func (*Scene) WebPreviewURL ¶
WebPreviewURL returns the external http(s) target page to render for it, or "" when there is none or the source isn't web-renderable. Usenet posts preview as reconstructed images, not web pages, so they never return a URL here.
func (*Scene) ZoomBrowserIn ¶
func (s *Scene) ZoomBrowserIn()
ZoomBrowserIn / ZoomBrowserOut zoom the embedded web preview through its MVVM commands — the same path the toolbar +/- buttons drive — so the zoom stays clamped and mirrors into the view-model.
func (*Scene) ZoomBrowserOut ¶
func (s *Scene) ZoomBrowserOut()
func (*Scene) ZoomKeyDir ¶
ZoomKeyDir reports the zoom direction bound to rune r: +1 for the zoom-in key, -1 for the zoom-out key, and 0 for anything else (including the zero rune). If the two keys are misconfigured to the same rune, zoom-in wins.
type SidebarContext ¶
type SidebarContext struct {
Kind SidebarCtxKind
Sub int
Source source.Kind
Channel string
Folder string
}
SidebarContext identifies what a sidebar right-click landed on, so the window layer can build the appropriate context menu. Kind is one of the exported SidebarCtx* constants; Sub/Source/Channel describe a subscription target and Folder names a folder target.
type SidebarCtxKind ¶
type SidebarCtxKind int
SidebarCtxKind classifies a sidebar right-click target.
const ( SidebarCtxNone SidebarCtxKind = iota // empty space or a non-menuable entry SidebarCtxSub // a subscription row SidebarCtxFolder // a folder row )
type Subscription ¶
type Subscription struct {
Source source.Kind
Channel string
Label string // display name; falls back to Channel or the source name
}
Subscription is one sidebar entry: a source + channel to pull, with a label.
type WebLink ¶
WebLink is one clickable anchor in a rendered web preview: Rect is its bounds in the rendered image's own pixel coordinates (at the width it was rendered), Href the resolved absolute target. The pane maps a click through the image's display rect + scale back to these to find the anchor under the cursor.
Source Files
¶
- a11y.go
- a11ytree.go
- accounts_view.go
- browse_tree.go
- browse_view.go
- clipboard.go
- contextmenu.go
- detail.go
- download_view.go
- feedcards.go
- group.go
- icons.go
- loading.go
- log_view.go
- media_prefetch.go
- native_controls.go
- preview_view.go
- render.go
- scene.go
- scrollbar.go
- search_view.go
- selection.go
- settings_view.go
- sidebar.go
- sidebar_folders.go
- sidebar_tree.go
- statusbar_view.go
- subcount.go
- text.go
- theme.go