162 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
			
		
		
	
	
			162 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
import 'dart:convert';
 | 
						|
 | 
						|
import 'package:easy_localization/easy_localization.dart';
 | 
						|
import 'package:flutter/material.dart';
 | 
						|
import 'package:gap/gap.dart';
 | 
						|
import 'package:google_fonts/google_fonts.dart';
 | 
						|
import 'package:material_symbols_icons/material_symbols_icons.dart';
 | 
						|
import 'package:island/models/thought.dart';
 | 
						|
 | 
						|
class FunctionCallsSection extends StatefulWidget {
 | 
						|
  const FunctionCallsSection({
 | 
						|
    super.key,
 | 
						|
    required this.isStreaming,
 | 
						|
    required this.streamingFunctionCalls,
 | 
						|
    this.thought,
 | 
						|
  });
 | 
						|
 | 
						|
  final bool isStreaming;
 | 
						|
  final List<String> streamingFunctionCalls;
 | 
						|
  final SnThinkingThought? thought;
 | 
						|
 | 
						|
  @override
 | 
						|
  State<FunctionCallsSection> createState() => _FunctionCallsSectionState();
 | 
						|
}
 | 
						|
 | 
						|
class _FunctionCallsSectionState extends State<FunctionCallsSection> {
 | 
						|
  bool _isExpanded = false;
 | 
						|
 | 
						|
  bool get _hasFunctionCalls {
 | 
						|
    if (widget.isStreaming) {
 | 
						|
      return widget.streamingFunctionCalls.isNotEmpty;
 | 
						|
    } else {
 | 
						|
      return widget.thought!.chunks.isNotEmpty &&
 | 
						|
          widget.thought!.chunks.any(
 | 
						|
            (chunk) => chunk.type == ThinkingChunkType.functionCall,
 | 
						|
          );
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  @override
 | 
						|
  Widget build(BuildContext context) {
 | 
						|
    if (!_hasFunctionCalls) {
 | 
						|
      return const SizedBox.shrink();
 | 
						|
    }
 | 
						|
 | 
						|
    return Column(
 | 
						|
      crossAxisAlignment: CrossAxisAlignment.start,
 | 
						|
      children: [
 | 
						|
        Container(
 | 
						|
          padding: const EdgeInsets.all(8),
 | 
						|
          decoration: BoxDecoration(
 | 
						|
            color: Theme.of(context).colorScheme.tertiaryContainer,
 | 
						|
            borderRadius: BorderRadius.circular(8),
 | 
						|
          ),
 | 
						|
          child: Column(
 | 
						|
            crossAxisAlignment: CrossAxisAlignment.start,
 | 
						|
            children: [
 | 
						|
              InkWell(
 | 
						|
                onTap: () => setState(() => _isExpanded = !_isExpanded),
 | 
						|
                child: Row(
 | 
						|
                  children: [
 | 
						|
                    Icon(
 | 
						|
                      Symbols.code,
 | 
						|
                      size: 14,
 | 
						|
                      color: Theme.of(context).colorScheme.tertiary,
 | 
						|
                    ),
 | 
						|
                    const Gap(4),
 | 
						|
                    Expanded(
 | 
						|
                      child: Text(
 | 
						|
                        'thoughtFunctionCall'.tr(),
 | 
						|
                        style: Theme.of(context).textTheme.bodySmall!.copyWith(
 | 
						|
                          fontWeight: FontWeight.w600,
 | 
						|
                          color: Theme.of(context).colorScheme.tertiary,
 | 
						|
                        ),
 | 
						|
                      ),
 | 
						|
                    ),
 | 
						|
                    Icon(
 | 
						|
                      _isExpanded ? Symbols.expand_more : Symbols.expand_less,
 | 
						|
                      size: 16,
 | 
						|
                      color: Theme.of(context).colorScheme.tertiary,
 | 
						|
                    ),
 | 
						|
                  ],
 | 
						|
                ),
 | 
						|
              ),
 | 
						|
              Visibility(visible: _isExpanded, child: const Gap(4)),
 | 
						|
              Visibility(
 | 
						|
                visible: _isExpanded,
 | 
						|
                child: Column(
 | 
						|
                  crossAxisAlignment: CrossAxisAlignment.start,
 | 
						|
                  children: [
 | 
						|
                    if (widget.isStreaming) ...[
 | 
						|
                      ...widget.streamingFunctionCalls.map(
 | 
						|
                        (call) => Container(
 | 
						|
                          width: double.infinity,
 | 
						|
                          padding: const EdgeInsets.all(8),
 | 
						|
                          margin: const EdgeInsets.only(bottom: 4),
 | 
						|
                          decoration: BoxDecoration(
 | 
						|
                            color: Theme.of(context).colorScheme.surface,
 | 
						|
                            borderRadius: BorderRadius.circular(4),
 | 
						|
                            border: Border.all(
 | 
						|
                              color: Theme.of(
 | 
						|
                                context,
 | 
						|
                              ).colorScheme.outline.withOpacity(0.3),
 | 
						|
                              width: 1,
 | 
						|
                            ),
 | 
						|
                          ),
 | 
						|
                          child: SelectableText(
 | 
						|
                            call,
 | 
						|
                            style: GoogleFonts.robotoMono(
 | 
						|
                              fontSize: 11,
 | 
						|
                              color: Theme.of(context).colorScheme.onSurface,
 | 
						|
                              height: 1.3,
 | 
						|
                            ),
 | 
						|
                          ),
 | 
						|
                        ),
 | 
						|
                      ),
 | 
						|
                    ] else ...[
 | 
						|
                      ...widget.thought!.chunks
 | 
						|
                          .where(
 | 
						|
                            (chunk) =>
 | 
						|
                                chunk.type == ThinkingChunkType.functionCall,
 | 
						|
                          )
 | 
						|
                          .map(
 | 
						|
                            (chunk) => Container(
 | 
						|
                              width: double.infinity,
 | 
						|
                              padding: const EdgeInsets.all(8),
 | 
						|
                              margin: const EdgeInsets.only(bottom: 4),
 | 
						|
                              decoration: BoxDecoration(
 | 
						|
                                color: Theme.of(context).colorScheme.surface,
 | 
						|
                                borderRadius: BorderRadius.circular(4),
 | 
						|
                                border: Border.all(
 | 
						|
                                  color: Theme.of(
 | 
						|
                                    context,
 | 
						|
                                  ).colorScheme.outline.withOpacity(0.3),
 | 
						|
                                  width: 1,
 | 
						|
                                ),
 | 
						|
                              ),
 | 
						|
                              child: SelectableText(
 | 
						|
                                JsonEncoder.withIndent(
 | 
						|
                                  '  ',
 | 
						|
                                ).convert(chunk.data),
 | 
						|
                                style: GoogleFonts.robotoMono(
 | 
						|
                                  fontSize: 11,
 | 
						|
                                  color:
 | 
						|
                                      Theme.of(context).colorScheme.onSurface,
 | 
						|
                                  height: 1.3,
 | 
						|
                                ),
 | 
						|
                              ),
 | 
						|
                            ),
 | 
						|
                          ),
 | 
						|
                    ],
 | 
						|
                  ],
 | 
						|
                ),
 | 
						|
              ),
 | 
						|
            ],
 | 
						|
          ),
 | 
						|
        ),
 | 
						|
      ],
 | 
						|
    );
 | 
						|
  }
 | 
						|
}
 |