television/assets/js/857f147c.c75b7a0c.js
2025-07-07 15:22:29 +00:00

1 line
24 KiB
JavaScript

"use strict";(self.webpackChunktelevision_website=self.webpackChunktelevision_website||[]).push([[20],{9254:(e,n,i)=>{i.r(n),i.d(n,{assets:()=>a,contentTitle:()=>o,default:()=>h,frontMatter:()=>t,metadata:()=>s,toc:()=>c});const s=JSON.parse('{"id":"Developers/ARCHITECTURE","title":"Architecture Documentation","description":"NOTE: what follows has mostly been assembled using AI as an experiment and as a basis for further improvements. @lalvarezt and I (@alexpasmantier) have been proofreading it to make sure all the information is technically correct and really reflects the code\'s architecture, so that **other developers may rely on it as a technical source of truth** when getting started with the repo.","source":"@site/../docs/02-Developers/01-ARCHITECTURE.md","sourceDirName":"02-Developers","slug":"/Developers/ARCHITECTURE","permalink":"/television/docs/Developers/ARCHITECTURE","draft":false,"unlisted":false,"tags":[],"version":"current","sidebarPosition":1,"frontMatter":{},"sidebar":"docSidebar","previous":{"title":"Showcase","permalink":"/television/docs/Users/showcase"},"next":{"title":"UI Features documentation","permalink":"/television/docs/Developers/ui-features"}}');var r=i(3420),l=i(5404);const t={},o="Architecture Documentation",a={},c=[{value:"Overview",id:"overview",level:2},{value:"High-Level Architecture",id:"high-level-architecture",level:2},{value:"How It Works",id:"how-it-works",level:2},{value:"1. Startup",id:"1-startup",level:3},{value:"2. Runtime Event Flow",id:"2-runtime-event-flow",level:3},{value:"Core Components",id:"core-components",level:2},{value:"Application Orchestrator (<code>app.rs</code>)",id:"application-orchestrator-apprs",level:3},{value:"Event System",id:"event-system",level:3},{value:"Event Loop (<code>loops/event_loop.rs</code>)",id:"event-loop-loopsevent_looprs",level:4},{value:"Actions (<code>action.rs</code>)",id:"actions-actionrs",level:4},{value:"Television Core (<code>television.rs</code>)",id:"television-core-televisionrs",level:3},{value:"Channel System",id:"channel-system",level:3},{value:"Channel Config (<code>channels/prototypes/</code>)",id:"channel-config-channelsprototypes",level:4},{value:"Channel Runtime (<code>channels/channel.rs</code>)",id:"channel-runtime-channelschannelrs",level:4},{value:"Rendering System",id:"rendering-system",level:3},{value:"Render Loop (<code>loops/render_loop.rs</code>)",id:"render-loop-loopsrender_looprs",level:4},{value:"Drawing (<code>draw.rs</code>)",id:"drawing-drawrs",level:4},{value:"Configuration System",id:"configuration-system",level:3},{value:"Preview System (<code>previewer/</code>)",id:"preview-system-previewer",level:3},{value:"Watch Timer (<code>loops/watch_timer.rs</code>)",id:"watch-timer-loopswatch_timerrs",level:3},{value:"Communication",id:"communication",level:2},{value:"Data Flow",id:"data-flow",level:3},{value:"Design Patterns",id:"design-patterns",level:2},{value:"1. Actor Model",id:"1-actor-model",level:3},{value:"2. Command Pattern",id:"2-command-pattern",level:3},{value:"3. Observer Pattern",id:"3-observer-pattern",level:3},{value:"4. Plugin Architecture",id:"4-plugin-architecture",level:3},{value:"Performance",id:"performance",level:2},{value:"How to Extend",id:"how-to-extend",level:2},{value:"Adding New Channels",id:"adding-new-channels",level:3},{value:"Custom Keybindings",id:"custom-keybindings",level:3},{value:"UI Themes",id:"ui-themes",level:3}];function d(e){const n={code:"code",em:"em",h1:"h1",h2:"h2",h3:"h3",h4:"h4",header:"header",li:"li",mermaid:"mermaid",ol:"ol",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,l.R)(),...e.components};return(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(n.header,{children:(0,r.jsx)(n.h1,{id:"architecture-documentation",children:"Architecture Documentation"})}),"\n",(0,r.jsx)(n.p,{children:(0,r.jsxs)(n.em,{children:["NOTE: what follows has mostly been assembled using AI as an experiment and as a basis for further improvements. @lalvarezt and I (@alexpasmantier) have been proofreading it to make sure all the information is technically correct and really reflects the code's architecture, so that ",(0,r.jsx)(n.strong,{children:"other developers may rely on it as a technical source of truth"})," when getting started with the repo."]})}),"\n",(0,r.jsx)(n.h2,{id:"overview",children:"Overview"}),"\n",(0,r.jsx)(n.p,{children:"Television is a terminal fuzzy finder built with Rust. It uses async/await and separate loops for event handling, rendering, and background tasks to stay responsive."}),"\n",(0,r.jsx)(n.h2,{id:"high-level-architecture",children:"High-Level Architecture"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{children:" \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n \u2502 CLI & Config \u2502\u2500\u2500\u2500\u25ba\u2502 Application \u2502\u2500\u2500\u2500\u25ba\u2502 Output \u2502\n \u2502 \u2502 \u2502 Orchestrator \u2502 \u2502 \u2502\n \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n \u2502\n \u25bc\n \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n \u2502 Event Loops \u2502\n \u2502 \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u2502\n \u2502 \u2502 Event Loop \u2502 \u2502 Render Loop \u2502 \u2502 Watch Timer \u2502 \u2502\n \u2502 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2502\n \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n \u2502\n \u25bc\n \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n \u2502 Core Components \u2502\n \u2502 \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u2502\n \u2502 \u2502 Television \u2502 \u2502 Channels \u2502 \u2502 Previewer \u2502 \u2502\n \u2502 \u2502 (State) \u2502 \u2502 (Sources) \u2502 \u2502 \u2502 \u2502\n \u2502 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2502\n \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n"})}),"\n",(0,r.jsx)(n.h2,{id:"how-it-works",children:"How It Works"}),"\n",(0,r.jsx)(n.h3,{id:"1-startup",children:"1. Startup"}),"\n",(0,r.jsx)(n.mermaid,{value:"sequenceDiagram\n participant CLI\n participant Config\n participant Cable\n participant App\n participant Loops\n\n CLI->>Config: Parse args & load config files\n Config->>Cable: Load channel definitions\n Cable->>App: Initialize with merged config\n App->>Loops: Start event, render, and watch loops\n Loops->>App: Begin main event loop"}),"\n",(0,r.jsx)(n.h3,{id:"2-runtime-event-flow",children:"2. Runtime Event Flow"}),"\n",(0,r.jsx)(n.mermaid,{value:"flowchart TD\n A[User Input] --\x3e B[Event Loop]\n B --\x3e C[Convert to Action]\n C --\x3e D[Action Channel]\n D --\x3e E[App Handler]\n E --\x3e F{Action Type}\n\n F --\x3e|Input| G[Update Television State]\n F --\x3e|Navigation| H[Update Picker]\n F --\x3e|Render| I[Send Render Task]\n F --\x3e|Channel Switch| J[Reload Channel]\n\n G --\x3e K[Trigger Render]\n H --\x3e K\n I --\x3e L[Render Loop]\n J --\x3e M[Channel Reload]\n\n L --\x3e N[Update Terminal]\n M --\x3e O[Update Results]\n O --\x3e K\n K --\x3e L"}),"\n",(0,r.jsx)(n.h2,{id:"core-components",children:"Core Components"}),"\n",(0,r.jsxs)(n.h3,{id:"application-orchestrator-apprs",children:["Application Orchestrator (",(0,r.jsx)(n.code,{children:"app.rs"}),")"]}),"\n",(0,r.jsx)(n.mermaid,{value:"graph TB\n A[main.rs] --\x3e B[App]\n\n B --\x3e C[Event Loop]\n B --\x3e D[Render Loop]\n B --\x3e E[Watch Timer]\n\n B --\x3e F[Television Core]\n F --\x3e G[Channels]\n F --\x3e H[Previewer]\n\n C --\x3e|Events| B\n D --\x3e|UI State| B\n E --\x3e|Timer Actions| B\n\n B --\x3e|Actions| I[Action Handler]\n I --\x3e|Render Tasks| D\n I --\x3e|State Updates| F"}),"\n",(0,r.jsx)(n.p,{children:"The main app that coordinates everything:"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsx)(n.p,{children:(0,r.jsx)(n.strong,{children:"What it does:"})}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsx)(n.li,{children:"Manages app state and lifecycle"}),"\n",(0,r.jsx)(n.li,{children:"Routes messages between loops using async channels"}),"\n",(0,r.jsx)(n.li,{children:"Handles actions and state changes"}),"\n",(0,r.jsx)(n.li,{children:"Starts and stops components"}),"\n"]}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsx)(n.p,{children:(0,r.jsx)(n.strong,{children:"Key channels:"})}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:"action_tx/rx"}),": Actions from events to main loop"]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:"render_tx/rx"}),": Rendering tasks to render loop"]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:"event_rx"}),": Events from event loop"]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:"ui_state_tx/rx"}),": UI state feedback from render loop"]}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,r.jsx)(n.h3,{id:"event-system",children:"Event System"}),"\n",(0,r.jsxs)(n.h4,{id:"event-loop-loopsevent_looprs",children:["Event Loop (",(0,r.jsx)(n.code,{children:"loops/event_loop.rs"}),")"]}),"\n",(0,r.jsx)(n.mermaid,{value:"flowchart LR\nA[Raw Event] --\x3e B[Event Loop]\nB --\x3e C{Event Type}\nC --\x3e|Keyboard| D[Key Mapping]\nC --\x3e|Mouse| E[Mouse Handler]\nC --\x3e|System| F[System Handler]\nD --\x3e G[Action]\nE --\x3e G\nF --\x3e G\nG --\x3e H[Action Channel]\nH --\x3e I[App Handler]\n"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Purpose:"})," Handles keyboard input, mouse events, and system signals"]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Input:"})," Key presses, mouse clicks, terminal resize, Ctrl+C"]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Output:"})," Events sent to main loop"]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Features:"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsx)(n.li,{children:"Non-blocking event reading"}),"\n",(0,r.jsx)(n.li,{children:"Clean shutdown handling"}),"\n",(0,r.jsx)(n.li,{children:"Regular ticks for animations"}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,r.jsxs)(n.h4,{id:"actions-actionrs",children:["Actions (",(0,r.jsx)(n.code,{children:"action.rs"}),")"]}),"\n",(0,r.jsx)(n.p,{children:"All user interactions become actions:"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-rust",children:"pub enum Action {\n // Input actions\n AddInputChar(char),\n DeletePrevChar,\n\n // Navigation actions\n SelectNextEntry,\n SelectPrevEntry,\n\n // Application actions\n ConfirmSelection,\n ToggleRemoteControl,\n Render,\n\n // System actions\n Resize(u16, u16),\n Quit,\n}\n"})}),"\n",(0,r.jsxs)(n.h3,{id:"television-core-televisionrs",children:["Television Core (",(0,r.jsx)(n.code,{children:"television.rs"}),")"]}),"\n",(0,r.jsx)(n.p,{children:"The main state manager:"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsx)(n.p,{children:(0,r.jsx)(n.strong,{children:"What it tracks:"})}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsx)(n.li,{children:"Current mode (Channel vs RemoteControl)"}),"\n",(0,r.jsx)(n.li,{children:"Search pattern and matching settings"}),"\n",(0,r.jsx)(n.li,{children:"Selected entries and picker state"}),"\n",(0,r.jsx)(n.li,{children:"Preview state and handles"}),"\n"]}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsx)(n.p,{children:(0,r.jsx)(n.strong,{children:"What it does:"})}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsx)(n.li,{children:"Pattern matching and filtering"}),"\n",(0,r.jsx)(n.li,{children:"Entry selection and multi-selection"}),"\n",(0,r.jsx)(n.li,{children:"Channel switching and mode changes"}),"\n",(0,r.jsx)(n.li,{children:"Preview coordination"}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,r.jsx)(n.h3,{id:"channel-system",children:"Channel System"}),"\n",(0,r.jsx)(n.mermaid,{value:"stateDiagram-v2\n [*] --\x3e Loading: Channel Selected\n Loading --\x3e Ready: Source Command Complete\n Ready --\x3e Filtering: User Input\n Ready --\x3e [*]: Channel Switch\n Filtering --\x3e Ready: Results Updated\n Ready --\x3e Reloading: Watch Timer / Manual Reload\n Reloading --\x3e Loading: Reload Complete\n\n note right of Loading\n Running source command\n Streaming results to matcher\n end note\n\n note right of Filtering\n Real-time fuzzy matching\n UI updates every few ms\n end note"}),"\n",(0,r.jsx)(n.mermaid,{value:'graph TB\n\n subgraph "Runtime Channel"\n F[Channel Instance] --\x3e G[Matcher]\n end\n\n subgraph "Channel Operations"\n J[Load] --\x3e K[Execute Source]\n K --\x3e L[Stream Results]\n M[Filter & Match] --\x3e N[Update UI]\n N --\x3e O[Handle Selection]\n end\n\n L --\x3e G\n G --\x3e M'}),"\n",(0,r.jsxs)(n.h4,{id:"channel-config-channelsprototypes",children:["Channel Config (",(0,r.jsx)(n.code,{children:"channels/prototypes/"}),")"]}),"\n",(0,r.jsx)(n.p,{children:"Channels are defined in TOML files:"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-toml",children:'[metadata]\nname = "files"\ndescription = "File finder"\n\n[source]\ncommand = "fd -t f"\n\n[preview]\ncommand = "bat --color=always \'{}\'"\n\n[ui]\npreview_panel = { size = 70 }\n\n[keybindings]\nshortcut = "f1"\n'})}),"\n",(0,r.jsxs)(n.h4,{id:"channel-runtime-channelschannelrs",children:["Channel Runtime (",(0,r.jsx)(n.code,{children:"channels/channel.rs"}),")"]}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Purpose:"})," Run source commands and manage results"]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Features:"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsx)(n.li,{children:"Async command execution with streaming results"}),"\n",(0,r.jsx)(n.li,{children:"Fuzzy matching with nucleo"}),"\n",(0,r.jsx)(n.li,{children:"Reload with debouncing"}),"\n",(0,r.jsx)(n.li,{children:"Multiple source commands"}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,r.jsx)(n.h3,{id:"rendering-system",children:"Rendering System"}),"\n",(0,r.jsxs)(n.h4,{id:"render-loop-loopsrender_looprs",children:["Render Loop (",(0,r.jsx)(n.code,{children:"loops/render_loop.rs"}),")"]}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Purpose:"})," Update the UI without blocking the main loop"]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Input:"})," Rendering tasks via channel"]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Output:"})," Terminal updates and UI state feedback"]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Features:"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsx)(n.li,{children:"60 FPS frame rate capping to avoid CPU hogging"}),"\n",(0,r.jsx)(n.li,{children:"Synchronized terminal updates"}),"\n",(0,r.jsx)(n.li,{children:"Layout state tracking"}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,r.jsxs)(n.h4,{id:"drawing-drawrs",children:["Drawing (",(0,r.jsx)(n.code,{children:"draw.rs"}),")"]}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Purpose:"})," Coordinate UI component rendering"]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Components:"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsx)(n.li,{children:"Input box with cursor"}),"\n",(0,r.jsx)(n.li,{children:"Results list with selection"}),"\n",(0,r.jsx)(n.li,{children:"Preview panel with content"}),"\n",(0,r.jsx)(n.li,{children:"Status bar with info"}),"\n",(0,r.jsx)(n.li,{children:"Remote control panel"}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,r.jsx)(n.h3,{id:"configuration-system",children:"Configuration System"}),"\n",(0,r.jsx)(n.mermaid,{value:'flowchart TD\n A[Default Config] --\x3e C[User Config]\n C --\x3e D[Channel Config]\n D --\x3e E[CLI Overrides]\n E --\x3e F[Final Config]\n\n subgraph "Config Sources"\n G[embedded config.toml] --\x3e A\n I[$HOME/.config/television/config.toml] --\x3e C\n J[cable/*.toml] --\x3e D\n K[Command Line Args] --\x3e E\n end\n\n subgraph "Config Sections"\n F --\x3e L[Application Settings]\n F --\x3e M[UI Configuration]\n F --\x3e N[Keybindings]\n F --\x3e O[Themes]\n F --\x3e P[Channel Specs]\n end'}),"\n",(0,r.jsxs)(n.h3,{id:"preview-system-previewer",children:["Preview System (",(0,r.jsx)(n.code,{children:"previewer/"}),")"]}),"\n",(0,r.jsx)(n.mermaid,{value:"sequenceDiagram\n participant UI as UI Component\n participant TV as Television\n participant PR as Previewer\n participant CMD as Preview Command\n\n UI->>TV: Selection Changed\n TV->>PR: Preview Request (entry)\n PR->>CMD: Execute preview command\n CMD--\x3e>PR: Command output\n PR->>TV: Preview Response\n TV->>UI: Update preview panel\n\n Note over PR: Caching & Debouncing\n Note over CMD: Async execution"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"How it works:"})," Separate async task for non-blocking previews"]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Communication:"})," Request/response via channels"]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Features:"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsx)(n.li,{children:"Command-based preview generation"}),"\n",(0,r.jsx)(n.li,{children:"Caching and debouncing"}),"\n",(0,r.jsx)(n.li,{children:"Error handling and fallbacks"}),"\n",(0,r.jsx)(n.li,{children:"Syntax highlighting support"}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,r.jsxs)(n.h3,{id:"watch-timer-loopswatch_timerrs",children:["Watch Timer (",(0,r.jsx)(n.code,{children:"loops/watch_timer.rs"}),")"]}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Purpose:"})," Automatically reload channels"]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Features:"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsx)(n.li,{children:"Configurable intervals per channel"}),"\n",(0,r.jsx)(n.li,{children:"Auto start/stop on channel switch"}),"\n",(0,r.jsx)(n.li,{children:"Handles missed ticks"}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,r.jsx)(n.h2,{id:"communication",children:"Communication"}),"\n",(0,r.jsx)(n.mermaid,{value:'flowchart LR\n subgraph "Action Flow"\n direction LR\n A[event_loop] --\x3e B[action_channel] --\x3e C[app_handler] --\x3e D[television]\n end\n\n subgraph "Render Flow"\n direction LR\n E[app_handler] --\x3e F[render_channel] --\x3e G[render_loop] --\x3e H[terminal]\n end\n\n subgraph "Preview Flow"\n direction LR\n I[television] --\x3e J[preview_request] --\x3e K[previewer] --\x3e L[preview_response] --\x3e I\n end'}),"\n",(0,r.jsx)(n.h3,{id:"data-flow",children:"Data Flow"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"One direction:"})," Events \u2192 Actions \u2192 State changes \u2192 Render"]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Feedback:"})," UI state info flows back for optimization"]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Async:"})," All blocking operations happen in separate tasks"]}),"\n"]}),"\n",(0,r.jsx)(n.h2,{id:"design-patterns",children:"Design Patterns"}),"\n",(0,r.jsx)(n.h3,{id:"1-actor-model",children:"1. Actor Model"}),"\n",(0,r.jsx)(n.p,{children:"Each major component runs independently and communicates via messages."}),"\n",(0,r.jsx)(n.h3,{id:"2-command-pattern",children:"2. Command Pattern"}),"\n",(0,r.jsx)(n.p,{children:"All user interactions become Action enums."}),"\n",(0,r.jsx)(n.h3,{id:"3-observer-pattern",children:"3. Observer Pattern"}),"\n",(0,r.jsx)(n.p,{children:"UI state changes automatically trigger rendering updates."}),"\n",(0,r.jsx)(n.h3,{id:"4-plugin-architecture",children:"4. Plugin Architecture"}),"\n",(0,r.jsx)(n.p,{children:"Channels are dynamically loaded from TOML config files."}),"\n",(0,r.jsx)(n.h2,{id:"performance",children:"Performance"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Event Processing:"})," Non-blocking with batched processing"]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Rendering:"})," Capped at 60 FPS with dirty state tracking"]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Matching:"})," Incremental fuzzy matching with nucleo"]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Preview:"})," Async with caching and debouncing"]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Memory:"})," Bounded result sets with efficient data structures"]}),"\n"]}),"\n",(0,r.jsx)(n.h2,{id:"how-to-extend",children:"How to Extend"}),"\n",(0,r.jsx)(n.h3,{id:"adding-new-channels",children:"Adding New Channels"}),"\n",(0,r.jsxs)(n.ol,{children:["\n",(0,r.jsx)(n.li,{children:"Create TOML config file"}),"\n",(0,r.jsx)(n.li,{children:"Define source command and output format"}),"\n",(0,r.jsx)(n.li,{children:"Add preview command and UI settings (optional)"}),"\n",(0,r.jsx)(n.li,{children:"Put in cable directory"}),"\n"]}),"\n",(0,r.jsx)(n.h3,{id:"custom-keybindings",children:"Custom Keybindings"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsx)(n.li,{children:"Global keybindings in main config"}),"\n",(0,r.jsx)(n.li,{children:"Channel-specific keybindings in channel config"}),"\n",(0,r.jsx)(n.li,{children:"Runtime updates via remote control"}),"\n"]}),"\n",(0,r.jsx)(n.h3,{id:"ui-themes",children:"UI Themes"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsx)(n.li,{children:"Color scheme definitions in theme files"}),"\n",(0,r.jsx)(n.li,{children:"Component-specific styling"}),"\n",(0,r.jsx)(n.li,{children:"Runtime theme switching"}),"\n"]}),"\n",(0,r.jsx)(n.p,{children:"This architecture keeps things modular and fast, with clear separation between components and efficient async communication."})]})}function h(e={}){const{wrapper:n}={...(0,l.R)(),...e.components};return n?(0,r.jsx)(n,{...e,children:(0,r.jsx)(d,{...e})}):d(e)}}}]);