diff --git a/src/Example/Program.cs b/src/Example/Program.cs
index 3751555..f8a1e76 100644
--- a/src/Example/Program.cs
+++ b/src/Example/Program.cs
@@ -1,2 +1,72 @@
-// See https://aka.ms/new-console-template for more information
-Console.WriteLine("Hello, World!");
+using TinyTUI;
+using TinyTUI.Stdio;
+using TinyTUI.Stdout;
+
+using var input = new ConsoleTerminalInput();
+var output = new ConsoleTerminalOutput();
+var done = new ManualResetEventSlim();
+var latestSize = input.CurrentSize;
+
+output.ClearScreen();
+output.HideCursor();
+Render(output, latestSize, "等待输入");
+
+input.DataReceived += (_, data) =>
+{
+ if (data == "\x1b")
+ {
+ done.Set();
+ return;
+ }
+
+ Render(output, latestSize, $"按键: {FormatInput(data)}");
+};
+
+input.Resized += (_, size) =>
+{
+ latestSize = size;
+ Render(output, latestSize, "窗口尺寸已变化");
+};
+
+try
+{
+ input.Start();
+ done.Wait();
+}
+finally
+{
+ input.Stop();
+ output.ShowCursor();
+ output.Write("\r\n");
+ output.Flush();
+}
+
+static void Render(ConsoleTerminalOutput output, TerminalSize size, string message)
+{
+ output.ClearScreen();
+ output.Write("TinyTUI IO 示例\r\n");
+ output.Write("================\r\n");
+ output.Write($"尺寸: {size.Columns} x {size.Rows}\r\n");
+ output.Write($"{message}\r\n");
+ output.Write("\r\n");
+ output.Write("测试方式:\r\n");
+ output.Write("- 输入普通字符 观察按键内容\r\n");
+ output.Write("- 按方向键 Home End Delete F1-F12 观察原始序列\r\n");
+ output.Write("- 改变终端窗口大小 观察尺寸刷新\r\n");
+ output.Write("- 按 Esc 退出\r\n");
+ output.Flush();
+}
+
+static string FormatInput(string data)
+{
+ return string.Join(' ', data.Select(static c => c switch
+ {
+ '\r' => "\\r",
+ '\n' => "\\n",
+ '\t' => "\\t",
+ '\b' => "\\b",
+ '\x1b' => "\\x1b",
+ _ when char.IsControl(c) => $"\\x{(int)c:x2}",
+ _ => c.ToString(),
+ }));
+}
diff --git a/src/TinyTUI/Stdio/ConsoleTerminalInput.cs b/src/TinyTUI/Stdio/ConsoleTerminalInput.cs
new file mode 100644
index 0000000..d1acc72
--- /dev/null
+++ b/src/TinyTUI/Stdio/ConsoleTerminalInput.cs
@@ -0,0 +1,158 @@
+namespace TinyTUI.Stdio;
+
+///
+/// 基于 System.Console 的终端输入实现
+///
+public sealed class ConsoleTerminalInput : ITerminalInput
+{
+ private static readonly TimeSpan PollInterval = TimeSpan.FromMilliseconds(16);
+
+ private CancellationTokenSource? _cancellation;
+ private Task? _inputTask;
+ private Task? _resizeTask;
+ private bool _previousTreatControlCAsInput;
+
+ ///
+ public TerminalSize CurrentSize => ReadCurrentSize();
+
+ ///
+ public event EventHandler? DataReceived;
+
+ ///
+ public event EventHandler? Resized;
+
+ ///
+ public void Start()
+ {
+ if (_cancellation is not null)
+ {
+ return;
+ }
+
+ _previousTreatControlCAsInput = Console.TreatControlCAsInput;
+ Console.TreatControlCAsInput = true;
+
+ _cancellation = new CancellationTokenSource();
+ _inputTask = Task.Run(() => ReadInputLoop(_cancellation.Token));
+ _resizeTask = Task.Run(() => WatchResizeLoop(_cancellation.Token));
+ }
+
+ ///
+ public void Stop()
+ {
+ var cancellation = _cancellation;
+ if (cancellation is null)
+ {
+ return;
+ }
+
+ cancellation.Cancel();
+
+ var tasks = new[] { _inputTask, _resizeTask }.OfType().ToArray();
+
+ try
+ {
+ Task.WaitAll(tasks, TimeSpan.FromMilliseconds(250));
+ }
+ catch
+ {
+ // 停止阶段不向调用方传播后台轮询任务异常
+ }
+
+ _inputTask = null;
+ _resizeTask = null;
+ _cancellation = null;
+
+ cancellation.Dispose();
+ Console.TreatControlCAsInput = _previousTreatControlCAsInput;
+ }
+
+ ///
+ public void Dispose()
+ {
+ Stop();
+ }
+
+ private void ReadInputLoop(CancellationToken cancellationToken)
+ {
+ while (!cancellationToken.IsCancellationRequested)
+ {
+ if (!Console.KeyAvailable)
+ {
+ Thread.Sleep(PollInterval);
+ continue;
+ }
+
+ var key = Console.ReadKey(intercept: true);
+ var data = EncodeKey(key);
+
+ if (data.Length > 0)
+ {
+ DataReceived?.Invoke(this, data);
+ }
+ }
+ }
+
+ private void WatchResizeLoop(CancellationToken cancellationToken)
+ {
+ var previous = CurrentSize;
+
+ while (!cancellationToken.IsCancellationRequested)
+ {
+ Thread.Sleep(PollInterval);
+
+ var current = CurrentSize;
+ if (current == previous)
+ {
+ continue;
+ }
+
+ previous = current;
+ Resized?.Invoke(this, current);
+ }
+ }
+
+ private static TerminalSize ReadCurrentSize()
+ {
+ try
+ {
+ return new TerminalSize(Console.WindowWidth, Console.WindowHeight);
+ }
+ catch
+ {
+ return new TerminalSize(80, 24);
+ }
+ }
+
+ private static string EncodeKey(ConsoleKeyInfo key) => key.Key switch
+ {
+ ConsoleKey.Enter => "\r",
+ ConsoleKey.Backspace => "\b",
+ ConsoleKey.Tab => "\t",
+ ConsoleKey.Escape => "\e",
+ ConsoleKey.UpArrow => "\e[A",
+ ConsoleKey.DownArrow => "\e[B",
+ ConsoleKey.RightArrow => "\e[C",
+ ConsoleKey.LeftArrow => "\e[D",
+ ConsoleKey.Home => "\e[H",
+ ConsoleKey.End => "\e[F",
+ ConsoleKey.Insert => "\e[2~",
+ ConsoleKey.Delete => "\e[3~",
+ ConsoleKey.PageUp => "\e[5~",
+ ConsoleKey.PageDown => "\e[6~",
+ ConsoleKey.F1 => "\eOP",
+ ConsoleKey.F2 => "\eOQ",
+ ConsoleKey.F3 => "\eOR",
+ ConsoleKey.F4 => "\eOS",
+ ConsoleKey.F5 => "\e[15~",
+ ConsoleKey.F6 => "\e[17~",
+ ConsoleKey.F7 => "\e[18~",
+ ConsoleKey.F8 => "\e[19~",
+ ConsoleKey.F9 => "\e[20~",
+ ConsoleKey.F10 => "\e[21~",
+ ConsoleKey.F11 => "\e[23~",
+ ConsoleKey.F12 => "\e[24~",
+ _ when key.KeyChar != '\0' => key.KeyChar.ToString(),
+ _ => string.Empty,
+ };
+}
diff --git a/src/TinyTUI/Stdout/ConsoleTerminalOutput.cs b/src/TinyTUI/Stdout/ConsoleTerminalOutput.cs
new file mode 100644
index 0000000..905123d
--- /dev/null
+++ b/src/TinyTUI/Stdout/ConsoleTerminalOutput.cs
@@ -0,0 +1,64 @@
+using System.Text;
+
+namespace TinyTUI.Stdout;
+
+///
+/// 基于 System.Console 的终端输出实现
+///
+public sealed class ConsoleTerminalOutput : ITerminalOutput
+{
+ private readonly TextWriter _writer;
+
+ ///
+ /// 创建使用 Console.Out 的终端输出实现
+ ///
+ public ConsoleTerminalOutput() : this(CreateConsoleWriter()) { }
+
+ ///
+ /// 创建使用指定 TextWriter 的终端输出实现
+ ///
+ public ConsoleTerminalOutput(TextWriter writer)
+ {
+ _writer = writer;
+ }
+
+ ///
+ public void Write(string value)
+ {
+ _writer.Write(value);
+ }
+
+ ///
+ public void Flush()
+ {
+ _writer.Flush();
+ }
+
+ ///
+ public void ClearScreen()
+ {
+ Write("\x1b[2J\x1b[H");
+ }
+
+ ///
+ public void HideCursor()
+ {
+ Write("\x1b[?25l");
+ }
+
+ ///
+ public void ShowCursor()
+ {
+ Write("\x1b[?25h");
+ }
+
+ private static TextWriter CreateConsoleWriter()
+ {
+ if (!Console.IsOutputRedirected)
+ {
+ Console.OutputEncoding = Encoding.UTF8;
+ }
+
+ return Console.Out;
+ }
+}