diff --git a/lib/modules/agent_network/pages/networks_overview_page.dart b/lib/modules/agent_network/pages/networks_overview_page.dart index 3718135d..b906d54d 100644 --- a/lib/modules/agent_network/pages/networks_overview_page.dart +++ b/lib/modules/agent_network/pages/networks_overview_page.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'dart:io'; import 'package:nocterm/nocterm.dart'; import 'package:nocterm_riverpod/nocterm_riverpod.dart'; @@ -14,6 +15,9 @@ import 'package:vide_cli/theme/theme.dart'; import 'package:vide_cli/constants/text_opacity.dart'; import 'package:vide_cli/modules/commands/command_provider.dart'; import 'package:vide_cli/modules/commands/command.dart'; +import 'package:vide_cli/modules/haiku/haiku_providers.dart'; +import 'package:vide_cli/modules/haiku/message_enhancement_service.dart'; +import 'package:vide_cli/utils/project_detector.dart'; class NetworksOverviewPage extends StatefulComponent { const NetworksOverviewPage({super.key}); @@ -27,10 +31,48 @@ class _NetworksOverviewPageState extends State { String? _commandResult; bool _commandResultIsError = false; + // Placeholder animation state + Timer? _placeholderTimer; + bool _isLoadingPlaceholder = true; + bool _isTypingPlaceholder = false; + String _fullPlaceholder = ''; + String _displayedPlaceholder = ''; + int _typingIndex = 0; + @override void initState() { super.initState(); _loadProjectInfo(); + _generateStartupContent(); + } + + void _startTypingAnimation(String text) { + _placeholderTimer?.cancel(); + _fullPlaceholder = text; + _typingIndex = 0; + _displayedPlaceholder = ''; + _isTypingPlaceholder = true; + + _placeholderTimer = Timer.periodic(const Duration(milliseconds: 30), (_) { + if (mounted && _typingIndex < _fullPlaceholder.length) { + setState(() { + _typingIndex++; + _displayedPlaceholder = _fullPlaceholder.substring(0, _typingIndex); + }); + } else { + _placeholderTimer?.cancel(); + setState(() { + _isTypingPlaceholder = false; + _displayedPlaceholder = _fullPlaceholder; + }); + } + }); + } + + @override + void dispose() { + _placeholderTimer?.cancel(); + super.dispose(); } Future _loadProjectInfo() async { @@ -54,6 +96,25 @@ class _NetworksOverviewPageState extends State { return fullPath; } + /// Generate startup content using Haiku + void _generateStartupContent() { + final now = DateTime.now(); + + // Generate dynamic placeholder text + MessageEnhancementService.generatePlaceholderText( + now, + (placeholder) { + if (mounted) { + setState(() { + _isLoadingPlaceholder = false; + }); + context.read(placeholderTextProvider.notifier).state = placeholder; + _startTypingAnimation(placeholder); + } + }, + ); + } + void _handleSubmit(Message message) async { // Start a new agent network with the full message (preserves attachments) // This returns immediately - client creation happens in background @@ -137,6 +198,18 @@ class _NetworksOverviewPageState extends State { // Watch sidebar focus state from app-level provider final sidebarFocused = context.watch(sidebarFocusProvider); + // Get placeholder - empty while loading, then type in the text when ready + final String placeholder; + if (_isLoadingPlaceholder) { + placeholder = ''; + } else if (_isTypingPlaceholder) { + placeholder = _displayedPlaceholder; + } else { + placeholder = _displayedPlaceholder.isNotEmpty + ? _displayedPlaceholder + : (context.watch(placeholderTextProvider) ?? 'Describe your goal (you can attach images)'); + } + return Focusable( focused: !sidebarFocused, onKeyEvent: (event) { @@ -201,7 +274,7 @@ class _NetworksOverviewPageState extends State { Container( child: AttachmentTextField( focused: !sidebarFocused, - placeholder: 'Describe your goal (you can attach images)', + placeholder: placeholder ?? 'Describe your goal (you can attach images)', onSubmit: _handleSubmit, onCommand: _handleCommand, commandSuggestions: _getCommandSuggestions, diff --git a/lib/modules/haiku/prompts/placeholder_prompt.dart b/lib/modules/haiku/prompts/placeholder_prompt.dart new file mode 100644 index 00000000..5e48a26e --- /dev/null +++ b/lib/modules/haiku/prompts/placeholder_prompt.dart @@ -0,0 +1,23 @@ +/// Prompt builder for dynamic input field placeholder text +class PlaceholderPrompt { + static String build(DateTime now) { + final randomSeed = now.millisecondsSinceEpoch % 100; + + return ''' +Generate witty placeholder text for an AI coding assistant input field. + +Seed: $randomSeed + +RULES: +- 3-5 words only +- Playful, slightly cheeky tone +- Like a friend asking what you want to work on +- BANNED: "What's on your mind", "Describe your", "Enter your", "Type here" +- Be creative! Think: "What are we breaking today?", "Got bugs?", "Your wish, my code..." + +CRITICAL: Output ONLY the placeholder text itself. +NO explanations, NO options, NO numbering, NO markdown. +Just the 3-5 word phrase, nothing else. +'''; + } +}