docs: 更新协议文档,删除废弃计划文档

- 更新 http-error-codes, user-points-chat-data-protocol
- 更新 divination-run-protocol, profile-protocol
- 删除废弃的后端和前端设计计划文档
This commit is contained in:
qzl
2026-04-08 17:23:02 +08:00
parent 49fc9a116f
commit e80a82bef4
57 changed files with 4117 additions and 2269 deletions
@@ -0,0 +1,249 @@
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import '../theme/design_tokens.dart';
import 'app_loading_indicator.dart';
enum MessageComposerMode { text, holdToSpeak }
enum MessageComposerProcess { idle, recording, transcribing }
class MessageComposer extends StatelessWidget {
const MessageComposer({
super.key,
required this.mode,
required this.process,
required this.hasMessage,
required this.isWaitingAgent,
required this.iconSize,
required this.composerMinHeight,
required this.onTapRightAction,
required this.onHoldToSpeakStart,
required this.onHoldToSpeakEnd,
required this.onHoldToSpeakMoveUpdate,
required this.onHoldToSpeakCancel,
required this.textInputChild,
required this.holdToSpeakText,
required this.recordingText,
required this.transcribingText,
required this.recordingHintText,
this.showRecordingInlineFeedback = true,
});
final MessageComposerMode mode;
final MessageComposerProcess process;
final bool hasMessage;
final bool isWaitingAgent;
final double iconSize;
final double composerMinHeight;
final VoidCallback onTapRightAction;
final VoidCallback onHoldToSpeakStart;
final VoidCallback onHoldToSpeakEnd;
final ValueChanged<LongPressMoveUpdateDetails> onHoldToSpeakMoveUpdate;
final VoidCallback onHoldToSpeakCancel;
final Widget textInputChild;
final String holdToSpeakText;
final String recordingText;
final String transcribingText;
final String recordingHintText;
final bool showRecordingInlineFeedback;
bool get _isHoldMode => mode == MessageComposerMode.holdToSpeak;
bool get _isRecording => process == MessageComposerProcess.recording;
bool get _isTranscribing => process == MessageComposerProcess.transcribing;
@override
Widget build(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme;
return Container(
padding: const EdgeInsets.symmetric(
horizontal: AppSpacing.md,
vertical: AppSpacing.sm,
),
decoration: BoxDecoration(
color: colorScheme.surfaceContainerLow,
borderRadius: BorderRadius.circular(AppRadius.lg),
border: Border.all(color: colorScheme.outlineVariant, width: 0.5),
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(child: _buildCenterArea(colorScheme)),
const SizedBox(width: AppSpacing.sm),
_buildRightAction(colorScheme),
],
),
);
}
Widget _buildRightAction(ColorScheme colorScheme) {
if (_isTranscribing) {
return SizedBox(
width: iconSize,
height: iconSize,
child: AppLoadingIndicator(
variant: AppLoadingVariant.inline,
size: iconSize,
strokeWidth: AppSpacing.xs / 2,
color: colorScheme.primary,
trackColor: colorScheme.primaryContainer,
),
);
}
if (_isRecording) {
return Container(
width: iconSize,
height: iconSize,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: colorScheme.error.withValues(alpha: 0.1),
),
child: Icon(
Icons.fiber_manual_record,
size: iconSize * 0.6,
color: colorScheme.error,
),
);
}
return IconButton(
visualDensity: VisualDensity.compact,
padding: EdgeInsets.zero,
constraints: BoxConstraints(minWidth: iconSize, minHeight: iconSize),
onPressed: onTapRightAction,
icon: Icon(
_resolveRightIcon(),
size: iconSize,
color: _resolveRightIconColor(colorScheme),
),
);
}
Widget _buildCenterArea(ColorScheme colorScheme) {
return SizedBox(
height: composerMinHeight,
child: AnimatedSwitcher(
duration: const Duration(milliseconds: 180),
switchInCurve: Curves.easeOut,
switchOutCurve: Curves.easeOut,
child: _isHoldMode
? _buildHoldToSpeakArea(
key: const ValueKey('hold_mode'),
colorScheme: colorScheme,
)
: _buildTextInputArea(key: const ValueKey('text_mode')),
),
);
}
Widget _buildTextInputArea({required Key key}) {
return SizedBox(key: key, height: composerMinHeight, child: textInputChild);
}
Widget _buildHoldToSpeakArea({
required Key key,
required ColorScheme colorScheme,
}) {
return RawGestureDetector(
behavior: HitTestBehavior.opaque,
gestures: {
LongPressGestureRecognizer:
GestureRecognizerFactoryWithHandlers<LongPressGestureRecognizer>(
() => LongPressGestureRecognizer(
duration: const Duration(milliseconds: 120),
),
(instance) {
instance.onLongPressStart = (details) => onHoldToSpeakStart();
instance.onLongPressEnd = (details) => onHoldToSpeakEnd();
instance.onLongPressMoveUpdate = onHoldToSpeakMoveUpdate;
instance.onLongPressCancel = onHoldToSpeakCancel;
},
),
},
child: Container(
key: key,
width: double.infinity,
height: composerMinHeight,
alignment: Alignment.center,
child: _buildHoldToSpeakContent(colorScheme),
),
);
}
Widget _buildHoldToSpeakContent(ColorScheme colorScheme) {
if (_isRecording) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
width: 8,
height: 8,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: colorScheme.error,
),
),
const SizedBox(width: AppSpacing.sm),
Text(
recordingText,
style: TextStyle(
color: colorScheme.onSurface,
fontWeight: FontWeight.w500,
),
),
],
);
}
if (_isTranscribing) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(
width: 14,
height: 14,
child: CircularProgressIndicator(
strokeWidth: 2,
color: colorScheme.primary,
),
),
const SizedBox(width: AppSpacing.sm),
Text(
transcribingText,
style: TextStyle(color: colorScheme.onSurfaceVariant),
),
],
);
}
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.mic, size: 16, color: colorScheme.onSurfaceVariant),
const SizedBox(width: AppSpacing.sm),
Text(
holdToSpeakText,
style: TextStyle(color: colorScheme.onSurfaceVariant),
),
],
);
}
IconData _resolveRightIcon() {
if (isWaitingAgent) {
return Icons.stop_rounded;
}
if (hasMessage) {
return Icons.send_rounded;
}
return _isHoldMode ? Icons.keyboard_rounded : Icons.mic_rounded;
}
Color _resolveRightIconColor(ColorScheme colorScheme) {
if (isWaitingAgent || hasMessage) {
return colorScheme.primary;
}
return colorScheme.onSurfaceVariant;
}
}