import type { Article, Feed } from '../../db/schema'; export type Panel = 'feeds' | 'articles' | 'reader'; export type Overlay = 'none' | 'help' | 'search' | 'addFeed' | 'editFeed' | 'confirm' | 'update' | 'hint'; export interface HintTarget { url: string; label: string; } export type LayoutMode = 'wide' | 'medium' | 'narrow'; export type SortOrder = 'newest' | 'oldest' | 'title'; export interface UpdateInfo { packageName: string; currentVersion: string; latestVersion: string; } export interface FeedWithCount extends Feed { unreadCount: number; } export interface ArticleWithFeed extends Article { feed?: Feed; } export interface CategoryState { name: string; collapsed: boolean; feeds: FeedWithCount[]; totalUnread: number; } export interface FetchProgress { current: number; total: number; feedTitle?: string; } export interface AppState { // Layout activePanel: Panel; layoutMode: LayoutMode; overlay: Overlay; feedPanelWidth: number | null; // null = auto, number = user-set columns articlePanelWidth: number | null; // null = auto, number = user-set columns (wide mode) // Data feeds: FeedWithCount[]; categories: CategoryState[]; articles: ArticleWithFeed[]; selectedArticle: ArticleWithFeed | null; // Selection selectedFeedId: number | null; // null = "All Articles" selectedFeedType: 'all' | 'starred' | 'feed'; feedListIndex: number; articleListIndex: number; // Filters showUnreadOnly: boolean; sortOrder: SortOrder; searchQuery: string; // Stats totalUnread: number; totalStarred: number; lastFetchedAt: Date | null; // UI state loading: boolean; fetchProgress: FetchProgress | null; confirmAction: ConfirmAction | null; error: string | null; editingFeed: FeedWithCount | null; updateInfo: UpdateInfo | null; // Hint mode (vimium-style link picker in reader). hintTargets is empty // unless overlay === 'hint'; hintBuffer accumulates keypresses until a // label resolves or fails to match any prefix. hintTargets: HintTarget[]; hintBuffer: string; } export interface ConfirmAction { message: string; onConfirm: () => void | Promise; } export type Action = | { type: 'SET_PANEL'; panel: Panel } | { type: 'SET_LAYOUT_MODE'; mode: LayoutMode } | { type: 'SET_OVERLAY'; overlay: Overlay } | { type: 'SET_FEEDS'; feeds: FeedWithCount[]; categories: CategoryState[] } | { type: 'SET_ARTICLES'; articles: ArticleWithFeed[] } | { type: 'SET_SELECTED_ARTICLE'; article: ArticleWithFeed | null } | { type: 'SELECT_FEED'; feedId: number | null; feedType: 'all' | 'starred' | 'feed' } | { type: 'SET_FEED_LIST_INDEX'; index: number } | { type: 'SET_ARTICLE_LIST_INDEX'; index: number } | { type: 'TOGGLE_UNREAD_FILTER' } | { type: 'CYCLE_SORT_ORDER' } | { type: 'SET_SEARCH_QUERY'; query: string } | { type: 'SET_STATS'; unread: number; starred: number; lastFetchedAt: Date | null } | { type: 'SET_LOADING'; loading: boolean } | { type: 'SET_FETCH_PROGRESS'; progress: FetchProgress | null } | { type: 'SET_CONFIRM'; action: ConfirmAction | null } | { type: 'SET_ERROR'; error: string | null } | { type: 'SET_EDITING_FEED'; feed: FeedWithCount | null } | { type: 'TOGGLE_CATEGORY'; categoryName: string } | { type: 'UPDATE_ARTICLE'; articleId: number; changes: Partial
} | { type: 'SET_FEED_PANEL_WIDTH'; width: number | null } | { type: 'SET_ARTICLE_PANEL_WIDTH'; width: number | null } | { type: 'SET_UPDATE_INFO'; info: UpdateInfo | null } | { type: 'ENTER_HINT_MODE'; targets: HintTarget[] } | { type: 'APPEND_HINT_KEY'; key: string } | { type: 'EXIT_HINT_MODE' }; export const initialState: AppState = { activePanel: 'feeds', layoutMode: 'wide', overlay: 'none', feedPanelWidth: null, articlePanelWidth: null, feeds: [], categories: [], articles: [], selectedArticle: null, selectedFeedId: null, selectedFeedType: 'all', feedListIndex: 0, articleListIndex: 0, showUnreadOnly: false, sortOrder: 'newest', searchQuery: '', totalUnread: 0, totalStarred: 0, lastFetchedAt: null, loading: true, fetchProgress: null, confirmAction: null, error: null, editingFeed: null, updateInfo: null, hintTargets: [], hintBuffer: '', };