🌐 Localize the new feature: thinking
This commit is contained in:
@@ -1290,5 +1290,14 @@
|
|||||||
"purchase": "Purchase",
|
"purchase": "Purchase",
|
||||||
"multiplierLabel": "Multiplier",
|
"multiplierLabel": "Multiplier",
|
||||||
"specialOnly": "Special Only",
|
"specialOnly": "Special Only",
|
||||||
"matches": "Matches"
|
"matches": "Matches",
|
||||||
|
"thoughtDefaultTopic": "Reflection",
|
||||||
|
"thoughtAiName": "SN-chan",
|
||||||
|
"thoughtUserName": "You",
|
||||||
|
"thoughtStreamingHint": "Sn-chan is thinking...",
|
||||||
|
"thoughtInputHint": "Ask sn-chan anything...",
|
||||||
|
"thoughtNewConversation": "Start New Conversation",
|
||||||
|
"thoughtParseError": "Failed to parse AI response",
|
||||||
|
"aiThought": "AI Thought",
|
||||||
|
"aiThoughtTitle": "Let sn-chan think"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1081,5 +1081,14 @@
|
|||||||
"postPublish": "发布帖子",
|
"postPublish": "发布帖子",
|
||||||
"restoreDraftTitle": "恢复草稿",
|
"restoreDraftTitle": "恢复草稿",
|
||||||
"restoreDraftMessage": "发现了一个草稿。你想要恢复它吗?",
|
"restoreDraftMessage": "发现了一个草稿。你想要恢复它吗?",
|
||||||
"draft": "草稿"
|
"draft": "草稿",
|
||||||
|
"thoughtDefaultTopic": "寻思",
|
||||||
|
"thoughtAiName": "SN 酱",
|
||||||
|
"thoughtUserName": "您",
|
||||||
|
"thoughtStreamingHint": "Sn-chan 正在思考...",
|
||||||
|
"thoughtInputHint": "问 sn-chan 任何问题...",
|
||||||
|
"thoughtNewConversation": "开始新对话",
|
||||||
|
"thoughtParseError": "解析 AI 响应失败",
|
||||||
|
"aiThought": "寻思",
|
||||||
|
"aiThoughtTitle": "让 SN 酱寻思寻思"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -173,7 +173,7 @@ class TabsScreen extends HookConsumerWidget {
|
|||||||
horizontal: 24,
|
horizontal: 24,
|
||||||
),
|
),
|
||||||
leading: const Icon(Symbols.bubble_chart),
|
leading: const Icon(Symbols.bubble_chart),
|
||||||
title: Text('让 SN 酱寻思寻思'),
|
title: Text('aiThoughtTitle'.tr()),
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
context.pushNamed('thought');
|
context.pushNamed('thought');
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import "dart:async";
|
import "dart:async";
|
||||||
import "dart:convert";
|
import "dart:convert";
|
||||||
import "package:dio/dio.dart";
|
import "package:dio/dio.dart";
|
||||||
|
import "package:easy_localization/easy_localization.dart";
|
||||||
import "package:flutter/material.dart";
|
import "package:flutter/material.dart";
|
||||||
import "package:flutter_hooks/flutter_hooks.dart";
|
import "package:flutter_hooks/flutter_hooks.dart";
|
||||||
import "package:gap/gap.dart";
|
import "package:gap/gap.dart";
|
||||||
@@ -43,7 +44,7 @@ class ThoughtScreen extends HookConsumerWidget {
|
|||||||
: const AsyncValue<List<SnThinkingThought>>.data([]);
|
: const AsyncValue<List<SnThinkingThought>>.data([]);
|
||||||
|
|
||||||
final localThoughts = useState<List<SnThinkingThought>>([]);
|
final localThoughts = useState<List<SnThinkingThought>>([]);
|
||||||
final currentTopic = useState<String?>('寻思');
|
final currentTopic = useState<String?>('aiThought'.tr());
|
||||||
|
|
||||||
final messageController = useTextEditingController();
|
final messageController = useTextEditingController();
|
||||||
final scrollController = useScrollController();
|
final scrollController = useScrollController();
|
||||||
@@ -61,7 +62,7 @@ class ThoughtScreen extends HookConsumerWidget {
|
|||||||
if (data.isNotEmpty && data.first.sequence?.topic != null) {
|
if (data.isNotEmpty && data.first.sequence?.topic != null) {
|
||||||
currentTopic.value = data.first.sequence!.topic;
|
currentTopic.value = data.first.sequence!.topic;
|
||||||
} else {
|
} else {
|
||||||
currentTopic.value = '寻思';
|
currentTopic.value = 'aiThought'.tr();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return null;
|
return null;
|
||||||
@@ -186,7 +187,7 @@ class ThoughtScreen extends HookConsumerWidget {
|
|||||||
currentTopic.value = topic;
|
currentTopic.value = topic;
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
showErrorAlert('Failed to parse AI response');
|
showErrorAlert('thoughtParseError'.tr());
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onError: (error) {
|
onError: (error) {
|
||||||
@@ -195,7 +196,7 @@ class ThoughtScreen extends HookConsumerWidget {
|
|||||||
// Handle streaming response errors differently
|
// Handle streaming response errors differently
|
||||||
if (error is DioException && error.response?.data is ResponseBody) {
|
if (error is DioException && error.response?.data is ResponseBody) {
|
||||||
// For streaming responses, show a generic error message
|
// For streaming responses, show a generic error message
|
||||||
showErrorAlert('Failed to get AI response. Please try again.');
|
showErrorAlert('toughtParseError'.tr());
|
||||||
} else {
|
} else {
|
||||||
showErrorAlert(error);
|
showErrorAlert(error);
|
||||||
}
|
}
|
||||||
@@ -243,8 +244,8 @@ class ThoughtScreen extends HookConsumerWidget {
|
|||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
thought.role == ThinkingThoughtRole.assistant
|
thought.role == ThinkingThoughtRole.assistant
|
||||||
? 'SN 酱'
|
? 'toughtAiName'.tr()
|
||||||
: '您',
|
: 'thoughtUserName'.tr(),
|
||||||
style: Theme.of(context).textTheme.titleSmall,
|
style: Theme.of(context).textTheme.titleSmall,
|
||||||
),
|
),
|
||||||
Tooltip(
|
Tooltip(
|
||||||
@@ -309,7 +310,10 @@ class ThoughtScreen extends HookConsumerWidget {
|
|||||||
children: [
|
children: [
|
||||||
Icon(Symbols.smart_toy, size: 20),
|
Icon(Symbols.smart_toy, size: 20),
|
||||||
const Gap(8),
|
const Gap(8),
|
||||||
Text('SN 酱', style: Theme.of(context).textTheme.titleSmall),
|
Text(
|
||||||
|
'thoughtAiName'.tr(),
|
||||||
|
style: Theme.of(context).textTheme.titleSmall,
|
||||||
|
),
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: 16,
|
width: 16,
|
||||||
@@ -327,27 +331,9 @@ class ThoughtScreen extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget newConversationButton() => Container(
|
|
||||||
margin: const EdgeInsets.symmetric(vertical: 8, horizontal: 16),
|
|
||||||
child: FilledButton.icon(
|
|
||||||
onPressed: () {
|
|
||||||
// Clear current conversation and start new one
|
|
||||||
selectedSequenceId.value = null;
|
|
||||||
localThoughts.value = [];
|
|
||||||
currentTopic.value = '寻思';
|
|
||||||
messageController.clear();
|
|
||||||
},
|
|
||||||
icon: const Icon(Symbols.add),
|
|
||||||
label: const Text('开始新对话'),
|
|
||||||
style: FilledButton.styleFrom(
|
|
||||||
minimumSize: const Size(double.infinity, 48),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
return AppScaffold(
|
return AppScaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(currentTopic.value ?? '寻思'),
|
title: Text(currentTopic.value ?? 'aiThought'.tr()),
|
||||||
actions: [
|
actions: [
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Symbols.history),
|
icon: const Icon(Symbols.history),
|
||||||
@@ -364,29 +350,25 @@ class ThoughtScreen extends HookConsumerWidget {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
if (localThoughts.value.isNotEmpty &&
|
||||||
|
!isStreaming.value &&
|
||||||
|
localThoughts.value.last.role == ThinkingThoughtRole.assistant)
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Symbols.add),
|
||||||
|
tooltip: 'thoughtNewConversation'.tr(),
|
||||||
|
onPressed: () {
|
||||||
|
// Clear current conversation and start new one
|
||||||
|
selectedSequenceId.value = null;
|
||||||
|
localThoughts.value = [];
|
||||||
|
currentTopic.value = 'aiThought'.tr();
|
||||||
|
messageController.clear();
|
||||||
|
},
|
||||||
|
),
|
||||||
const Gap(8),
|
const Gap(8),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
body: Column(
|
body: Column(
|
||||||
children: [
|
children: [
|
||||||
// New conversation button - only show when there are messages and last is AI
|
|
||||||
AnimatedOpacity(
|
|
||||||
opacity:
|
|
||||||
(localThoughts.value.isNotEmpty &&
|
|
||||||
localThoughts.value.last.role ==
|
|
||||||
ThinkingThoughtRole.assistant &&
|
|
||||||
!isStreaming.value)
|
|
||||||
? 1.0
|
|
||||||
: 0.0,
|
|
||||||
duration: const Duration(milliseconds: 300),
|
|
||||||
child:
|
|
||||||
(localThoughts.value.isNotEmpty &&
|
|
||||||
localThoughts.value.last.role ==
|
|
||||||
ThinkingThoughtRole.assistant &&
|
|
||||||
!isStreaming.value)
|
|
||||||
? newConversationButton()
|
|
||||||
: const SizedBox.shrink(),
|
|
||||||
),
|
|
||||||
Expanded(
|
Expanded(
|
||||||
child: thoughts.when(
|
child: thoughts.when(
|
||||||
data:
|
data:
|
||||||
@@ -447,8 +429,8 @@ class ThoughtScreen extends HookConsumerWidget {
|
|||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
hintText:
|
hintText:
|
||||||
isStreaming.value
|
isStreaming.value
|
||||||
? 'Sn-chan is thinking...'
|
? 'thoughtStreamingHint'.tr()
|
||||||
: 'Ask sn-chan anything...',
|
: 'thoughtInputHint'.tr(),
|
||||||
border: InputBorder.none,
|
border: InputBorder.none,
|
||||||
isDense: true,
|
isDense: true,
|
||||||
contentPadding: const EdgeInsets.symmetric(
|
contentPadding: const EdgeInsets.symmetric(
|
||||||
|
|||||||
Reference in New Issue
Block a user