refactor: 重构 AgentScope 运行时模块并优化前端附件展示

This commit is contained in:
qzl
2026-03-13 15:42:01 +08:00
parent a10a2db27a
commit 4c10929498
28 changed files with 1494 additions and 2163 deletions
@@ -470,10 +470,6 @@ class _HomeScreenState extends State<HomeScreen>
),
),
),
if (item.attachments.isNotEmpty && !hasRenderableAttachments) ...[
const SizedBox(width: _itemSpacing / 2),
_buildAttachmentBadge(item.attachments.length),
],
],
),
if (hasRenderableAttachments)
@@ -495,7 +491,7 @@ class _HomeScreenState extends State<HomeScreen>
final renderableAttachments =
imageAttachments ?? _collectRenderableImageAttachments(attachments);
if (renderableAttachments.isEmpty) {
return _buildAttachmentBadge(attachments.length);
return const SizedBox.shrink();
}
return Wrap(
spacing: _attachmentPreviewGap,
@@ -512,18 +508,18 @@ class _HomeScreenState extends State<HomeScreen>
}
bool _isRenderableImageAttachment(Map<String, dynamic> attachment) {
final url = attachment['url'];
final mimeType = attachment['mimeType'];
final previewPath = attachment['previewPath'];
return mimeType is String &&
mimeType.startsWith('image/') &&
previewPath is String &&
previewPath.isNotEmpty;
return url is String &&
url.isNotEmpty &&
mimeType is String &&
mimeType.startsWith('image/');
}
Widget _buildHistoryAttachmentTile(Map<String, dynamic> attachment) {
final previewPath = attachment['previewPath'];
if (previewPath is! String || previewPath.isEmpty) {
return _buildAttachmentBadge(1);
final url = attachment['url'];
if (url is! String || url.isEmpty) {
return const SizedBox.shrink();
}
return ClipRRect(
borderRadius: BorderRadius.circular(_attachmentPreviewRadius),
@@ -531,51 +527,35 @@ class _HomeScreenState extends State<HomeScreen>
width: _attachmentPreviewSize,
height: _attachmentPreviewSize,
color: AppColors.slate100,
child: FutureBuilder<Uint8List?>(
future: _chatBloc.loadAttachmentPreview(previewPath),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(
child: SizedBox(
width: _transcribingSpinnerSize,
height: _transcribingSpinnerSize,
child: CircularProgressIndicator(
strokeWidth: _transcribingStrokeWidth,
),
child: Image.network(
url,
fit: BoxFit.cover,
loadingBuilder: (context, child, loadingProgress) {
if (loadingProgress == null) return child;
return const Center(
child: SizedBox(
width: _transcribingSpinnerSize,
height: _transcribingSpinnerSize,
child: CircularProgressIndicator(
strokeWidth: _transcribingStrokeWidth,
),
);
}
final data = snapshot.data;
if (data == null || data.isEmpty) {
return const Center(
child: Icon(
LucideIcons.imageOff,
size: _iconSize,
color: AppColors.slate500,
),
);
}
return Image.memory(data, fit: BoxFit.cover, gaplessPlayback: true);
),
);
},
errorBuilder: (context, error, stackTrace) {
return const Center(
child: Icon(
LucideIcons.imageOff,
size: _iconSize,
color: AppColors.slate500,
),
);
},
),
),
);
}
Widget _buildAttachmentBadge(int count) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: AppColors.slate200,
borderRadius: BorderRadius.circular(8),
),
child: Text(
'图片附件 x$count',
style: const TextStyle(fontSize: 12, color: AppColors.slate600),
),
);
}
Widget _buildToolCallItem(ToolCallItem item) {
final (statusText, statusColor, statusIcon) = switch (item.status) {
ToolCallStatus.pending => (