From 1f1bf18a755e4ffade9e942096c46cf0c2c41c0a Mon Sep 17 00:00:00 2001 From: hailin Date: Sat, 28 Feb 2026 05:05:27 -0800 Subject: [PATCH] fix: remove clipboard paste menu item, fix timeline line overlap, dim input placeholder MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Remove redundant "从剪贴板粘贴" option from attachment menu (long-press to paste natively) - Remove super_clipboard dependency (no longer needed) - Fix timeline vertical line overlapping icon nodes by using dynamic dotRadius - Dim input field placeholder color to AppColors.textMuted Co-Authored-By: Claude Opus 4.6 --- .../chat/presentation/pages/chat_page.dart | 60 +------------------ .../widgets/timeline_event_node.dart | 23 ++++--- it0_app/pubspec.yaml | 1 - 3 files changed, 16 insertions(+), 68 deletions(-) diff --git a/it0_app/lib/features/chat/presentation/pages/chat_page.dart b/it0_app/lib/features/chat/presentation/pages/chat_page.dart index d683a73..3af272a 100644 --- a/it0_app/lib/features/chat/presentation/pages/chat_page.dart +++ b/it0_app/lib/features/chat/presentation/pages/chat_page.dart @@ -1,10 +1,8 @@ -import 'dart:async'; import 'dart:convert'; import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:image_picker/image_picker.dart'; -import 'package:super_clipboard/super_clipboard.dart'; import 'package:file_picker/file_picker.dart'; import '../../../../core/theme/app_colors.dart'; import '../../domain/entities/chat_message.dart'; @@ -93,11 +91,6 @@ class _ChatPageState extends ConsumerState { title: const Text('拍照'), onTap: () { Navigator.pop(ctx); _pickImage(ImageSource.camera); }, ), - ListTile( - leading: const Icon(Icons.content_paste), - title: const Text('从剪贴板粘贴'), - onTap: () { Navigator.pop(ctx); _pasteFromClipboard(); }, - ), ListTile( leading: const Icon(Icons.attach_file), title: const Text('选择文件'), @@ -193,58 +186,6 @@ class _ChatPageState extends ConsumerState { } } - Future _pasteFromClipboard() async { - final clipboard = SystemClipboard.instance; - if (clipboard == null) { - if (mounted) { - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar(content: Text('当前设备不支持剪贴板读取')), - ); - } - return; - } - - try { - final reader = await clipboard.read(); - for (final item in reader.items) { - // Try PNG first, then JPEG - for (final format in [Formats.png, Formats.jpeg]) { - if (item.canProvide(format)) { - final completer = Completer>(); - item.getFile(format, (file) async { - try { - final bytes = await file.readAll(); - completer.complete(bytes); - } catch (e) { - completer.completeError(e); - } - }, onError: (e) { - if (!completer.isCompleted) completer.completeError(e); - }); - final bytes = await completer.future; - final mediaType = format == Formats.png ? 'image/png' : 'image/jpeg'; - setState(() { - _pendingAttachments.add(ChatAttachment( - base64Data: base64Encode(bytes), - mediaType: mediaType, - fileName: 'clipboard.${format == Formats.png ? "png" : "jpg"}', - )); - }); - return; - } - } - } - } catch (e) { - // Clipboard read failed - } - - if (mounted) { - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar(content: Text('剪贴板中没有图片')), - ); - } - } - Future _pickFile() async { final remaining = _maxAttachments - _pendingAttachments.length; if (remaining <= 0) { @@ -673,6 +614,7 @@ class _ChatPageState extends ConsumerState { controller: _messageController, decoration: InputDecoration( hintText: isStreaming ? '追加指令...' : '输入指令...', + hintStyle: TextStyle(color: AppColors.textMuted), border: const OutlineInputBorder( borderRadius: BorderRadius.all(Radius.circular(24)), ), diff --git a/it0_app/lib/features/chat/presentation/widgets/timeline_event_node.dart b/it0_app/lib/features/chat/presentation/widgets/timeline_event_node.dart index c38d5d2..5890b02 100644 --- a/it0_app/lib/features/chat/presentation/widgets/timeline_event_node.dart +++ b/it0_app/lib/features/chat/presentation/widgets/timeline_event_node.dart @@ -40,12 +40,16 @@ class TimelineEventNode extends StatelessWidget { Widget build(BuildContext context) { // Use CustomPaint for the timeline line to avoid IntrinsicHeight which // causes bottom-overflow on long content. + // Icon nodes are 16px tall, plain dots are 10px. The gap in the + // vertical line must be large enough to avoid overlapping either. + final dotRadius = icon != null ? 10.0 : 6.0; return CustomPaint( painter: _TimelineLinePainter( color: AppColors.surfaceLight, isFirst: isFirst, isLast: isLast, - dotOffset: 14, // vertical center of the dot area + dotCenter: 14, // vertical center of the dot area + dotRadius: dotRadius, lineX: 14, // horizontal center of the 28px left column ), child: Row( @@ -109,14 +113,16 @@ class _TimelineLinePainter extends CustomPainter { final Color color; final bool isFirst; final bool isLast; - final double dotOffset; + final double dotCenter; + final double dotRadius; final double lineX; _TimelineLinePainter({ required this.color, required this.isFirst, required this.isLast, - required this.dotOffset, + required this.dotCenter, + required this.dotRadius, required this.lineX, }); @@ -126,15 +132,15 @@ class _TimelineLinePainter extends CustomPainter { ..color = color ..strokeWidth = 1.5; - // Line above the dot + // Line above the dot/icon if (!isFirst) { - canvas.drawLine(Offset(lineX, 0), Offset(lineX, dotOffset), paint); + canvas.drawLine(Offset(lineX, 0), Offset(lineX, dotCenter - dotRadius), paint); } - // Line below the dot + // Line below the dot/icon if (!isLast) { canvas.drawLine( - Offset(lineX, dotOffset + 10), + Offset(lineX, dotCenter + dotRadius), Offset(lineX, size.height), paint, ); @@ -145,7 +151,8 @@ class _TimelineLinePainter extends CustomPainter { bool shouldRepaint(_TimelineLinePainter oldDelegate) => color != oldDelegate.color || isFirst != oldDelegate.isFirst || - isLast != oldDelegate.isLast; + isLast != oldDelegate.isLast || + dotRadius != oldDelegate.dotRadius; } /// Static colored dot with optional icon for completed/error/idle states. diff --git a/it0_app/pubspec.yaml b/it0_app/pubspec.yaml index 8f76190..0d592ad 100644 --- a/it0_app/pubspec.yaml +++ b/it0_app/pubspec.yaml @@ -37,7 +37,6 @@ dependencies: gpt_markdown: ^1.1.5 flutter_svg: ^2.0.10+1 image_picker: ^1.1.2 - super_clipboard: ^0.8.1 file_picker: ^8.0.0 # Voice