From e29448ed46cfce168c6ed01db47e53261b07c96a Mon Sep 17 00:00:00 2001 From: Udhay-Adithya Date: Sun, 7 Sep 2025 23:49:57 +0530 Subject: [PATCH] feat: add terminal page ui --- lib/screens/dashboard.dart | 15 +++++ lib/screens/mobile/dashboard.dart | 3 + lib/screens/mobile/navbar.dart | 9 +++ lib/screens/screens.dart | 1 + lib/screens/terminal/terminal_page.dart | 75 +++++++++++++++++++++++++ 5 files changed, 103 insertions(+) create mode 100644 lib/screens/terminal/terminal_page.dart diff --git a/lib/screens/dashboard.dart b/lib/screens/dashboard.dart index 6e711c15..2ad5a610 100644 --- a/lib/screens/dashboard.dart +++ b/lib/screens/dashboard.dart @@ -10,6 +10,7 @@ import 'envvar/environment_page.dart'; import 'home_page/home_page.dart'; import 'history/history_page.dart'; import 'settings_page.dart'; +import 'terminal/terminal_page.dart'; class Dashboard extends ConsumerWidget { const Dashboard({super.key}); @@ -70,6 +71,19 @@ class Dashboard extends ConsumerWidget { 'History', style: Theme.of(context).textTheme.labelSmall, ), + kVSpacer10, + IconButton( + isSelected: railIdx == 4, + onPressed: () { + ref.read(navRailIndexStateProvider.notifier).state = 4; + }, + icon: const Icon(Icons.terminal), + selectedIcon: const Icon(Icons.terminal), + ), + Text( + 'Terminal', + style: Theme.of(context).textTheme.labelSmall, + ), ], ), Expanded( @@ -121,6 +135,7 @@ class Dashboard extends ConsumerWidget { EnvironmentPage(), HistoryPage(), SettingsPage(), + TerminalPage(), ], ), ) diff --git a/lib/screens/mobile/dashboard.dart b/lib/screens/mobile/dashboard.dart index 4e4cbd1f..1ecac4b4 100644 --- a/lib/screens/mobile/dashboard.dart +++ b/lib/screens/mobile/dashboard.dart @@ -8,6 +8,7 @@ import 'requests_page/requests_page.dart'; import '../envvar/environment_page.dart'; import '../history/history_page.dart'; import '../settings_page.dart'; +import '../terminal/terminal_page.dart'; import 'widgets/page_base.dart'; import 'navbar.dart'; @@ -77,6 +78,8 @@ class PageBranch extends ConsumerWidget { title: 'Settings', scaffoldBody: SettingsPage(), ); + case 4: + return const TerminalPage(); default: return const RequestResponsePage(); } diff --git a/lib/screens/mobile/navbar.dart b/lib/screens/mobile/navbar.dart index 71fe8c5c..d1952021 100644 --- a/lib/screens/mobile/navbar.dart +++ b/lib/screens/mobile/navbar.dart @@ -57,6 +57,15 @@ class BottomNavBar extends ConsumerWidget { label: 'History', ), ), + Expanded( + child: NavbarButton( + railIdx: railIdx, + buttonIdx: 4, + selectedIcon: Icons.terminal, + icon: Icons.terminal, + label: 'Terminal', + ), + ), Expanded( child: NavbarButton( railIdx: railIdx, diff --git a/lib/screens/screens.dart b/lib/screens/screens.dart index c647c90d..281983e0 100644 --- a/lib/screens/screens.dart +++ b/lib/screens/screens.dart @@ -1,3 +1,4 @@ export 'dashboard.dart'; export 'mobile/mobile.dart'; export 'home_page/collection_pane.dart'; +export 'terminal/terminal_page.dart'; diff --git a/lib/screens/terminal/terminal_page.dart b/lib/screens/terminal/terminal_page.dart new file mode 100644 index 00000000..14237529 --- /dev/null +++ b/lib/screens/terminal/terminal_page.dart @@ -0,0 +1,75 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import '../../models/terminal_models.dart'; +import '../../providers/terminal_providers.dart'; + +class TerminalPage extends ConsumerWidget { + const TerminalPage({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final state = ref.watch(terminalStateProvider); + final entries = state.entries; + + return Scaffold( + appBar: AppBar(title: const Text('Terminal')), + body: ListView.separated( + itemCount: entries.length, + separatorBuilder: (_, __) => const Divider(height: 1), + itemBuilder: (ctx, i) { + final e = entries[i]; + final title = _titleFor(e); + final subtitle = _subtitleFor(e); + final icon = _iconFor(e); + return ListTile( + leading: Icon(icon), + title: Text(title, maxLines: 1, overflow: TextOverflow.ellipsis), + subtitle: Text(subtitle ?? '', + maxLines: 2, overflow: TextOverflow.ellipsis), + dense: true, + ); + }, + ), + ); + } + + IconData _iconFor(TerminalEntry e) { + switch (e.source) { + case TerminalSource.network: + return Icons.language; + case TerminalSource.js: + return Icons.javascript; + case TerminalSource.system: + return Icons.info_outline; + } + } + + String _titleFor(TerminalEntry e) { + switch (e.source) { + case TerminalSource.network: + final n = e.network!; + final status = n.responseStatus != null ? ' — ${n.responseStatus}' : ''; + return '${n.method.name.toUpperCase()} ${n.url}$status'; + case TerminalSource.js: + final j = e.js!; + return 'JS ${j.level}'; + case TerminalSource.system: + return 'System'; + } + } + + String? _subtitleFor(TerminalEntry e) { + switch (e.source) { + case TerminalSource.network: + final n = e.network!; + if (n.errorMessage != null) return n.errorMessage; + return n.responseBodyPreview ?? n.requestBodyPreview; + case TerminalSource.js: + final j = e.js!; + return j.args.join(' '); + case TerminalSource.system: + final s = e.system!; + return s.message; + } + } +}