feat(l10n): Add localization for code interpreter states

This commit is contained in:
cogwheel0
2025-12-09 23:04:18 +05:30
parent ed6d588518
commit 5b7cd0dd42
12 changed files with 152 additions and 14 deletions

View File

@@ -526,12 +526,65 @@ class _AssistantMessageWidgetState extends ConsumerState<AssistantMessageWidget>
: CrossFadeState.showFirst,
duration: const Duration(milliseconds: 200),
),
// Render file images when tool call is done
// Mirrors Open WebUI's Collapsible.svelte file rendering
if (tc.done && tc.files != null) ...[
_buildToolCallFiles(tc.files!),
],
],
),
),
);
}
/// Builds image widgets from tool call files array.
/// Mirrors Open WebUI's Collapsible.svelte file rendering logic:
/// - String starting with 'data:image/' -> base64 image
/// - Object with type='image' and url -> network image
Widget _buildToolCallFiles(List<dynamic> files) {
final imageUrls = <String>[];
for (final file in files) {
if (file is String) {
// Base64 image data URL
if (file.startsWith('data:image/')) {
imageUrls.add(file);
}
} else if (file is Map) {
// Object with type and url
final type = file['type']?.toString();
final url = file['url']?.toString();
if (type == 'image' && url != null && url.isNotEmpty) {
imageUrls.add(url);
}
}
}
if (imageUrls.isEmpty) {
return const SizedBox.shrink();
}
return Padding(
padding: const EdgeInsets.only(top: Spacing.sm),
child: Wrap(
spacing: Spacing.sm,
runSpacing: Spacing.sm,
children: imageUrls.map((url) {
return EnhancedImageAttachment(
attachmentId: url,
isMarkdownFormat: true,
constraints: BoxConstraints(
maxWidth: imageUrls.length == 1 ? 400 : 200,
maxHeight: imageUrls.length == 1 ? 300 : 150,
),
disableAnimation: false,
);
}).toList(),
),
);
}
Widget _buildSegmentedContent() {
final children = <Widget>[];
bool firstToolSpacerAdded = false;
@@ -1323,21 +1376,43 @@ class _AssistantMessageWidgetState extends ConsumerState<AssistantMessageWidget>
Widget _buildReasoningTile(ReasoningEntry rc, int index) {
final isExpanded = _expandedReasoning.contains(index);
final theme = context.conduitTheme;
// Show shimmer when streaming and this is an active/incomplete reasoning
final showShimmer = widget.isStreaming && rc.duration == 0;
// Show shimmer when reasoning is not done (mirrors OpenWebUI's done !== 'true')
final showShimmer = !rc.isDone;
String headerText() {
final l10n = AppLocalizations.of(context)!;
final hasSummary = rc.summary.isNotEmpty;
final isThinkingSummary =
rc.summary.trim().toLowerCase() == 'thinking…' ||
rc.summary.trim().toLowerCase() == 'thinking...';
if (widget.isStreaming) {
return hasSummary ? rc.summary : l10n.thinking;
final summaryLower = rc.summary.trim().toLowerCase();
// Mirror Open WebUI's Collapsible.svelte logic for different block types
if (rc.isCodeInterpreter) {
// Code interpreter: "Analyzing..." -> "Analyzed"
if (!rc.isDone) {
return l10n.analyzing;
}
return l10n.analyzed;
}
// Reasoning block
final isThinkingSummary =
summaryLower == 'thinking…' ||
summaryLower == 'thinking...' ||
summaryLower.startsWith('thinking');
// - If not done (streaming): show "Thinking..."
// - If done with duration: show "Thought for X seconds"
// - If done without duration: show "Thoughts" or custom summary
if (!rc.isDone) {
// Still thinking - use summary if available, else default
return hasSummary && !isThinkingSummary ? rc.summary : l10n.thinking;
}
// Done thinking - check duration
if (rc.duration > 0) {
return l10n.thoughtForDuration(rc.formattedDuration);
}
// No duration - use custom summary if meaningful, else default
if (!hasSummary || isThinkingSummary) {
return l10n.thoughts;
}