import 'dart:async';
import 'dart:math';
import 'dart:convert';
// import 'dart:html' as html;

import 'package:ainoval/screens/editor/controllers/editor_screen_controller.dart';
import 'package:flutter/gestures.dart';

import 'package:ainoval/blocs/editor/editor_bloc.dart' as editor_bloc;
import 'package:ainoval/blocs/setting/setting_bloc.dart';
import 'package:ainoval/utils/quill_helper.dart';
import 'package:ainoval/screens/editor/widgets/selection_toolbar.dart';
import 'package:ainoval/screens/editor/widgets/ai_generation_toolbar.dart';
import 'package:ainoval/utils/web_theme.dart';
import 'package:ainoval/utils/setting_reference_processor.dart';
import 'package:ainoval/utils/ai_generated_content_processor.dart';
import 'package:ainoval/services/api_service/repositories/universal_ai_repository.dart';
import 'package:ainoval/services/api_service/base/api_exception.dart';
import 'package:ainoval/models/ai_request_models.dart';
import 'package:ainoval/models/unified_ai_model.dart';
import 'package:ainoval/models/scene_beat_data.dart';
import 'package:ainoval/screens/editor/components/text_generation_dialogs.dart';
import 'package:ainoval/models/novel_structure.dart';
import 'package:ainoval/widgets/common/top_toast.dart';
import 'package:flutter/material.dart';
import 'package:flutter_quill/flutter_quill.dart';
import 'package:ainoval/utils/logger.dart';
import 'package:ainoval/utils/word_count_analyzer.dart';
import 'package:provider/provider.dart';
import 'package:ainoval/screens/editor/managers/editor_layout_manager.dart';
import 'package:ainoval/screens/editor/widgets/menu_builder.dart';
import 'package:ainoval/screens/editor/widgets/setting_reference_hover.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

import 'package:ainoval/widgets/common/setting_preview_manager.dart';
import 'package:ainoval/models/novel_structure.dart' as novel_models;
import 'package:ainoval/models/novel_setting_item.dart';
import 'package:ainoval/models/setting_group.dart';
import 'package:ainoval/models/novel_snippet.dart';
// import 'package:ainoval/models/user_ai_model_config_model.dart';
import 'package:ainoval/models/editor_settings.dart';
// import 'package:ainoval/models/public_model_config.dart';
import 'package:ainoval/widgets/editor/overlay_scene_beat_manager.dart';
import 'package:ainoval/blocs/credit/credit_bloc.dart';


/// 场景编辑器组件，用于编辑小说中的单个场景
///
/// [title] 场景标题
/// [wordCount] 场景字数统计
/// [isActive] 当前场景是否处于激活状态
/// [actId] 所属篇章ID
/// [chapterId] 所属章节ID
/// [sceneId] 场景ID
/// [isFirst] 是否为章节中的第一个场景
/// [sceneIndex] 场景在章节中的序号，从1开始
/// [controller] 场景内容编辑控制器
/// [summaryController] 场景摘要编辑控制器
/// [editorBloc] 编辑器状态管理
/// [onContentChanged] 内容变更回调
class SceneEditor extends StatefulWidget {
  const SceneEditor({
    super.key,
    required this.title,
    required this.wordCount,
    required this.isActive,
    this.actId,
    this.chapterId,
    this.sceneId,
    this.isFirst = true,
    this.sceneIndex, // 添加场景序号参数
    required this.controller,
    required this.summaryController,
    required this.editorBloc,
    this.onContentChanged, // 添加回调函数
    this.isVisuallyNearby = true, // 新增参数，默认为true以保持当前行为
    // 🚀 新增：SelectionToolbar数据参数
    this.novel,
    this.settings = const [],
    this.settingGroups = const [],
    this.snippets = const [],
    // 编辑器设置
    this.editorSettings,
  });
  final String title;
  final int wordCount;
  final bool isActive;
  final String? actId;
  final String? chapterId;
  final String? sceneId;
  final bool isFirst;
  final int? sceneIndex; // 场景在章节中的序号，从1开始
  final QuillController controller;
  final TextEditingController summaryController;
  final editor_bloc.EditorBloc editorBloc;
  // 添加内容变更回调
  final Function(String content, int wordCount, {bool syncToServer})? onContentChanged;
  final bool isVisuallyNearby; // 新增参数声明

  // 🚀 新增：SelectionToolbar数据参数
  final novel_models.Novel? novel;
  final List<NovelSettingItem> settings;
  final List<SettingGroup> settingGroups;
  final List<NovelSnippet> snippets;
  
  // 编辑器设置
  final EditorSettings? editorSettings;

  @override
  State<SceneEditor> createState() => _SceneEditorState();
}

class _SceneEditorState extends State<SceneEditor> with AutomaticKeepAliveClientMixin {
  final FocusNode _focusNode = FocusNode();
  Timer? _debounceTimer;
  bool _isFocused = false;
  // 为编辑器创建一个Key
  late final Key _editorKey;
  // 内容更新防抖定时器
  Timer? _contentDebounceTimer;
  final FocusNode _summaryFocusNode = FocusNode();
  bool _isSummaryFocused = false;
  // 焦点防抖定时器
  Timer? _focusDebounceTimer;
  
  // 🚀 新增：活动状态设置防抖定时器
  Timer? _activeStateDebounceTimer;
  // 🚀 新增：记录最后设置的活动状态，避免重复设置
  String? _lastSetActiveActId;
  String? _lastSetActiveChapterId;
  String? _lastSetActiveSceneId;

  // 添加文本选择工具栏相关变量
  bool _showToolbar = false;
  final LayerLink _toolbarLayerLink = LayerLink();
  int _selectedTextWordCount = 0;
  Timer? _selectionDebounceTimer;
  bool _showToolbarAbove = false; // 默认在选区下方显示，简化计算
  final GlobalKey _editorContentKey = GlobalKey(); // 编辑器内容区域的key

  // 🚀 AI工具栏相关状态
  bool _showAIToolbar = false;
  final LayerLink _aiToolbarLayerLink = LayerLink();
  bool _isAIGenerating = false;
  String _aiModelName = '';
  String _generatedText = '';
  int _aiGeneratedWordCount = 0;
  int _currentStreamIndex = 0;
  int _lastInsertedOffset = 0;
  int _aiGeneratedStartOffset = 0;
  
  // 🚀 新增：流式生成批量插入缓冲
  String _pendingStreamText = '';

  // 🚀 新增：用于保存重试信息的变量
  UniversalAIRequest? _lastAIRequest;
  // 已移除：UserAIModelConfigModel? _lastAIModel; 现在使用_lastUnifiedModel
  String? _lastSelectedText;
  // 🚀 新增：保存统一模型信息（包含isPublic状态）
  UnifiedAIModel? _lastUnifiedModel;

  // 添加防抖处理
  String _pendingContent = '';
  String _lastSavedContent = ''; // 添加最后保存的内容，用于比较变化
  DateTime _lastChangeTime = DateTime.now(); // 添加最后变更时间
  int _pendingWordCount = 0;
  Timer? _syncTimer;
  final int _minorChangeThreshold = 5; // 定义微小改动的字符数阈值
  
  // 添加内容变化标志，用于在dispose时判断是否需要强制保存
  bool _hasUnsavedChanges = false;
  
  // 🚀 新增：设定引用处理状态标志，避免样式变化触发保存
  bool _isProcessingSettingReferences = false;
  int _lastSettingHash = 0; // 简单文本哈希，避免重复处理

  // 🚀 新增：AI生成状态标志，避免生成过程中触发保存

  // 添加滚动控制器，用于工具栏定位
  late final ScrollController _editorScrollController;
  
  // 设定引用处理相关
  Timer? _settingReferenceProcessTimer;
  String _lastProcessedText = '';
  String _lastProcessedDeltaContent = ''; // 上次处理的完整Delta内容
  DateTime _lastProcessingTime = DateTime(2000); // 上次处理时间
  static const Duration _minProcessingInterval = Duration(milliseconds: 1000); // 最小处理间隔

  // 🚀 新增：摘要组件滚动固定相关变量
  final GlobalKey _sceneContainerKey = GlobalKey(); // 场景容器的key
  final GlobalKey _summaryKey = GlobalKey(); // 摘要组件的key
  // 使用 ValueNotifier 代替频繁 setState
  final ValueNotifier<double> _summaryTopOffsetVN = ValueNotifier<double>(0.0); // 摘要Y偏移
  bool _isSummarySticky = false; // 摘要是否处于sticky状态
  Timer? _scrollPositionTimer; // 滚动位置更新定时器
  ScrollController? _parentScrollController; // 父级滚动控制器
  
  // 🚀 新增：流畅滚动优化变量
  double _lastCalculatedOffset = 0.0; // 上次计算的偏移量
  bool _lastStickyState = false; // 上次的sticky状态
  double _summaryHeight = 200.0; // 摘要组件的实际高度，默认200px
  static const double _positionThreshold = 2.0; // 位置变化阈值，减少闪烁
  
  // 🚀 新增：粘性滚动控制变量
  static const double _minSceneHeightForSticky = 400.0; // 最小场景高度，低于此高度不启用粘性
  static const double _summaryTopMargin = 16.0; // 摘要顶部边距
  static const double _summaryBottomMargin = 24.0; // 摘要底部边距
  static const double _bottomToolbarHeight = 40.0; // 🚀 新增：底部工具栏预留高度

  // 🚀 新增：LayerLink目标的GlobalKey，用于工具栏检测位置
  final GlobalKey _toolbarTargetKey = GlobalKey();

  // 🚀 新增：AI生成状态标志，避免生成过程中触发保存

  // 添加一个延迟初始化标志
  bool _isEditorFullyInitialized = false;
  Timer? _streamingTimer;
  
  // ==================== Controller listeners管理 ====================
  StreamSubscription? _docChangeSub; // 监听 document.changes 的订阅，便于在 controller 切换时取消
  
  /// 获取当前小说ID
  String? _getNovelId() {
    final editorBloc = widget.editorBloc;
    if (editorBloc.state is editor_bloc.EditorLoaded) {
      final state = editorBloc.state as editor_bloc.EditorLoaded;
      return state.novel.id;
    }
    return null;
  }

  @override
  void initState() {
    super.initState();
      
      // 修改初始化Key的方式，确保唯一性
      final String sceneId = widget.sceneId ??
          (widget.actId != null && widget.chapterId != null
              ? '${widget.actId}_${widget.chapterId}'
              : widget.title.replaceAll(' ', '_').toLowerCase());
      // 使用ValueKey代替GlobalObjectKey
      _editorKey = ValueKey('editor_$sceneId');

      // 初始化滚动控制器
      _editorScrollController = ScrollController();

      // 监听焦点变化
      _focusNode.addListener(_onEditorFocusChange);
      _summaryFocusNode.addListener(_onSummaryFocusChange);

      // 添加控制器内容监听器（保存订阅以便后续取消）
      _docChangeSub = widget.controller.document.changes.listen(_onDocumentChange);

      // 添加文本选择变化监听
      widget.controller.addListener(_handleSelectionChange);
      
      // 监听EditorBloc状态变化，确保摘要控制器内容与模型保持同步
      _setupBlocListener();
      
      // 监听设定状态变化，处理设定引用
      _setupSettingBlocListener();
      
      // 监听内容加载完成，重新处理设定引用
      _setupContentLoadListener();
      
      // 初始化最后保存的内容（纯文本用于比较）
      _lastSavedContent = widget.controller.document.toPlainText();
      
      // 🚀 新增：设置摘要滚动固定监听
      _setupSummaryScrollListener();
      
      // 延迟完整初始化，优先显示基础UI
      WidgetsBinding.instance.addPostFrameCallback((_) {
        // 在渲染完成后再初始化复杂功能
        Future.microtask(() {
          if (mounted) {
            setState(() {
              _isEditorFullyInitialized = true;
            });
            
            // 🎯 简化：直接处理设定引用，不再等待DOM
            AppLogger.i('SceneEditor', '🎯 开始设定引用处理: ${widget.sceneId}');
            //_checkAndProcessSettingReferences();
            
            // 🚀 新增：初始化摘要位置
            _updateSummaryPosition();
          }
        });
      });

  }

  void _onEditorFocusChange() {

      // 使用节流控制焦点更新频率
      _focusDebounceTimer?.cancel();
      _focusDebounceTimer = Timer(const Duration(milliseconds: 100), () {
        if (mounted) {
          final newFocusState = _focusNode.hasFocus;
          // 仅当焦点状态真正改变时更新状态
          if (_isFocused != newFocusState) {
            setState(() {
              _isFocused = newFocusState;
              
              // 🎯 当编辑器获得焦点时，处理设定引用（使用防抖）
              if (_isFocused && !_isProcessingSettingReferences) {
                ////AppLogger.d('SceneEditor', '📝 编辑器获得焦点，处理设定引用: ${widget.sceneId}');
                _processSettingReferencesDebounced();
              }
              
              // 🚀 优化：只有当获得焦点且确实需要改变活动状态时才设置活动元素
              if (_isFocused && widget.actId != null && widget.chapterId != null) {
                // 检查当前是否已经是活动状态
                final editorBloc = widget.editorBloc;
                if (editorBloc.state is editor_bloc.EditorLoaded) {
                  final state = editorBloc.state as editor_bloc.EditorLoaded;
                  final isAlreadyActive = state.activeActId == widget.actId &&
                      state.activeChapterId == widget.chapterId &&
                      state.activeSceneId == widget.sceneId;
                  
                  // 只有当不是活动状态时才设置
                  if (!isAlreadyActive) {
                    _setActiveElementsQuietly();
                  }
                  
                  // 如果场景节拍面板已显示且当前场景有sceneId，则切换到当前场景
                  if (widget.sceneId != null && 
                      OverlaySceneBeatManager.instance.isVisible && 
                      OverlaySceneBeatManager.instance.currentSceneId != widget.sceneId) {
                    AppLogger.i('SceneEditor', '🔄 场景获得焦点，切换场景节拍面板到: ${widget.sceneId}');
                    OverlaySceneBeatManager.instance.switchScene(widget.sceneId!);
                  }
                } else {
                  // 状态不明确时才设置
                  _setActiveElementsQuietly();
                }
              }
            });
            

          }
        }
      });

  }

  void _onSummaryFocusChange() {

      // 使用节流控制焦点更新频率
      _focusDebounceTimer?.cancel();
      _focusDebounceTimer = Timer(const Duration(milliseconds: 100), () {
        if (mounted) {
          final newFocusState = _summaryFocusNode.hasFocus;
          // 仅当焦点状态真正改变时更新状态
          if (_isSummaryFocused != newFocusState) {
            setState(() {
              _isSummaryFocused = newFocusState;
              // 🚀 优化：只有当获得焦点且确实需要改变活动状态时才设置活动元素
              if (_isSummaryFocused && widget.actId != null && widget.chapterId != null) {
                // 检查当前是否已经是活动状态
                final editorBloc = widget.editorBloc;
                if (editorBloc.state is editor_bloc.EditorLoaded) {
                  final state = editorBloc.state as editor_bloc.EditorLoaded;
                  final isAlreadyActive = state.activeActId == widget.actId &&
                      state.activeChapterId == widget.chapterId &&
                      state.activeSceneId == widget.sceneId;
                  
                  // 只有当不是活动状态时才设置
                  if (!isAlreadyActive) {
                    _setActiveElementsQuietly();
                  }
                } else {
                  // 状态不明确时才设置
                  _setActiveElementsQuietly();
                }
              }
            });
            

          }
        }
      });

  }

  // 设置活动元素 - 原始方法
  void _setActiveElements() {

      if (widget.actId != null && widget.chapterId != null) {
        widget.editorBloc.add(
            editor_bloc.SetActiveChapter(actId: widget.actId!, chapterId: widget.chapterId!));
        if (widget.sceneId != null) {
          widget.editorBloc.add(editor_bloc.SetActiveScene(
              actId: widget.actId!,
              chapterId: widget.chapterId!,
              sceneId: widget.sceneId!));
        }
      }

  }

  // 设置活动元素但不触发滚动 - 适用于编辑中场景（优化版）
  void _setActiveElementsQuietly() {

      if (widget.actId != null && widget.chapterId != null) {
        // 🚀 优化：检查是否与上次设置的状态相同，避免重复设置
        final bool isSameAsLastSet = _lastSetActiveActId == widget.actId &&
            _lastSetActiveChapterId == widget.chapterId &&
            _lastSetActiveSceneId == widget.sceneId;
        
        if (isSameAsLastSet) {
          AppLogger.v('SceneEditor', '跳过设置活动状态：与上次设置相同 ${widget.actId}/${widget.chapterId}/${widget.sceneId}');
          return;
        }
        
        // 🚀 使用防抖机制，避免短时间内频繁设置
        _activeStateDebounceTimer?.cancel();
        _activeStateDebounceTimer = Timer(const Duration(milliseconds: 100), () {
          if (!mounted) return;
          
          // 直接使用BlocProvider获取EditorBloc实例
          final editorBloc = widget.editorBloc;
          
          // 检查当前活动状态，避免重复设置相同的活动元素
          if (editorBloc.state is editor_bloc.EditorLoaded) {
            final state = editorBloc.state as editor_bloc.EditorLoaded;
            
            // 只有当活动元素确实需要变化时才发出事件
            final needsToUpdateAct = state.activeActId != widget.actId;
            final needsToUpdateChapter = state.activeChapterId != widget.chapterId;
            final needsToUpdateScene = widget.sceneId != null && state.activeSceneId != widget.sceneId;
            
            if (needsToUpdateAct || needsToUpdateChapter) {
              ////AppLogger.d('SceneEditor', '设置活动章节: ${widget.actId}/${widget.chapterId}');
              editorBloc.add(editor_bloc.SetActiveChapter(
                actId: widget.actId!, 
                chapterId: widget.chapterId!,
                silent: true, // 🚀 使用静默模式，避免触发大范围UI刷新
              ));
              
              // 🚀 记录已设置的状态
              _lastSetActiveActId = widget.actId;
              _lastSetActiveChapterId = widget.chapterId;
            }
            
            if (needsToUpdateScene && widget.sceneId != null) {
              ////AppLogger.d('SceneEditor', '设置活动场景: ${widget.sceneId}');
              editorBloc.add(editor_bloc.SetActiveScene(
                actId: widget.actId!,
                chapterId: widget.chapterId!,
                sceneId: widget.sceneId!,
                silent: true, // 🚀 使用静默模式，避免触发大范围UI刷新
              ));
              
              // 🚀 记录已设置的场景状态
              _lastSetActiveSceneId = widget.sceneId;
            }
          } else {
            // 如果状态不是EditorLoaded，则使用原始方法
            _setActiveElements();
            
            // 🚀 记录已设置的状态
            _lastSetActiveActId = widget.actId;
            _lastSetActiveChapterId = widget.chapterId;
            _lastSetActiveSceneId = widget.sceneId;
          }
        });
      }

  }

  // 监听文档变化
  void _onDocumentChange(DocChange change) {

      if (!mounted) return;
      
      // 🚫 生成期间：跳过文档变更的重处理（编码/过滤/保存）
      if (_isAIGenerating) {
        AppLogger.v('SceneEditor', '⏭️ 生成中，跳过文档变更处理: ${widget.sceneId}');
        return;
      }

      // 🚀 关键修复：检查变化是否来源于设定引用样式应用
      final currentText = widget.controller.document.toPlainText();
      final currentDeltaJson = jsonEncode(widget.controller.document.toDelta().toJson());
      
      // 🎯 新增：如果完整内容相等且正在处理设定引用，直接跳过
      if (currentDeltaJson == _lastProcessedDeltaContent && _isProcessingSettingReferences) {
        AppLogger.v('SceneEditor', '⏭️ 场景内容完全相等且正在处理设定引用，跳过保存');
        return;
      }
      
      // 如果是样式变化且文本内容没有变化，则不触发保存
      if (currentText == _lastSavedContent && _isProcessingSettingReferences) {
        AppLogger.v('SceneEditor', '⏭️ 设定引用样式应用不触发保存');
        return;
      }

      // 🎯 新增：检查是否仅为样式变化（不是文本内容变化）
      if (_isOnlyStyleChange(change) && _isProcessingSettingReferences) {
        AppLogger.v('SceneEditor', '⏭️ 仅样式变化且正在处理设定引用，跳过');
        return;
      }

      // 🚀 修复关键问题：提取包含样式信息的完整Delta格式
      // 不再使用 toPlainText() 因为它会丢失所有样式属性
      final rawDeltaJson = currentDeltaJson; // 复用已计算的Delta JSON
      
      // 🧹 过滤设定引用相关的自定义样式，但保留其他样式（如粗体、斜体、下划线等）
      // 🎯 重新启用过滤，确保保存时不包含设定引用样式
      final filteredDeltaJson = SettingReferenceProcessor.filterSettingReferenceStyles(rawDeltaJson, caller: '_onDocumentChange');
      
      //////AppLogger.d('SceneEditor', '文档变化 - 过滤后保存Delta格式，原始长度: ${rawDeltaJson.length}, 过滤后长度: ${filteredDeltaJson.length}');

      // 使用防抖动机制，避免频繁发送保存请求
      _contentDebounceTimer?.cancel();
      _contentDebounceTimer = Timer(const Duration(milliseconds: 800), () {
        // 延长为800毫秒防抖，更好地应对快速输入
        _onTextChanged(filteredDeltaJson);
      });
      
      // 🎯 优化：只在真正的文本内容变化时才处理设定引用
      if (currentText != _lastSavedContent && !_isProcessingSettingReferences && 
          currentDeltaJson != _lastProcessedDeltaContent) {
        // 延迟处理设定引用，避免在文档变化处理过程中立即触发
        Timer(const Duration(milliseconds: 100), () {
          if (mounted) {
            _checkAndProcessSettingReferences();
          }
        });
      }
      

  }

  // 🎯 新增：检查是否仅为样式变化
  bool _isOnlyStyleChange(DocChange change) {
    try {
      // 检查变化是否只涉及格式化而不涉及文本插入/删除
      if (change.change.operations.every((op) {
        // 如果是retain操作且有attributes，说明是样式变化
        if (op.key == 'retain' && op.attributes != null) {
          return true;
        }
        // 如果是insert操作但插入的是空字符串且有attributes，也是样式变化
        if (op.key == 'insert' && op.data is String && (op.data as String).isEmpty && op.attributes != null) {
          return true;
        }
        return false;
      })) {
        return true;
      }
      return false;
    } catch (e) {
      AppLogger.w('SceneEditor', '检查样式变化失败', e);
      return false;
    }
  }

  // 添加防抖处理
  void _onTextChanged(String content) {

      // 🚫 生成期间不进行保存与过滤，等待用户"应用/丢弃"后再处理
      if (_isAIGenerating) {
        AppLogger.v('SceneEditor', '⏭️ 生成中，跳过_onTextChanged: ${widget.sceneId}');
        return;
      }

      // 🚀 修复：避免在设定引用处理时触发保存
      if (_isProcessingSettingReferences) {
        AppLogger.v('SceneEditor', '🛑 设定引用处理中，跳过保存: ${widget.sceneId}');
        return;
      }
      
      // 🚫 如果文本内容未发生变化，直接跳过后续处理，防止重复保存
      final String currentPlainText = QuillHelper.deltaToText(content);
      if (currentPlainText == _lastSavedContent) {
        AppLogger.v('SceneEditor', '⏭️ 文本内容与最后保存内容一致，跳过保存: ${widget.sceneId}');
        return;
      }
      
      // 🆕 新增：如果有隐藏文本，使用过滤后的内容进行保存
      if (AIGeneratedContentProcessor.hasAnyHiddenText(controller: widget.controller)) {
        AppLogger.v('SceneEditor', '🫥 检测到隐藏文本，使用过滤后的内容保存: ${widget.sceneId}');
        // 使用过滤掉隐藏文本的内容
        content = AIGeneratedContentProcessor.getVisibleDeltaJsonOnly(controller: widget.controller);
      }
      
      // 🚀 修复：现在接收的是Delta JSON格式，包含完整样式信息
      // 先提取纯文本用于字数统计和变化检测
      final plainText = currentPlainText;
      final wordCount = WordCountAnalyzer.countWords(plainText);
      
      // 判断是否为微小改动（基于纯文本比较）
      final bool isMinorChange = _isMinorTextChange(plainText);
      
      // 记录变动信息
      AppLogger.v('SceneEditor', '文本变更 - Delta长度: ${content.length}, 字数: $wordCount, 是否微小改动: $isMinorChange');
      
      // 保存到本地变量，避免立即更新
      _pendingContent = content; // 🚀 现在保存的是包含样式的Delta JSON
      _pendingWordCount = wordCount;
      _lastChangeTime = DateTime.now();
      
      // 触发设定引用处理
      _checkAndProcessSettingReferences();
    
    // 标记有未保存的更改（基于纯文本比较）
    _hasUnsavedChanges = true;
    
    // 🚀 新增：通过正则快速检测Delta JSON中是否仍包含 AI 临时属性，避免漏判
    final bool hasTempAIMarks = content.contains('"ai-generated"') ||
        content.contains('"hidden-text"');
    
    // 只有在内容实际发生变化且没有临时标记时，才发送 UpdateSceneContent 事件
    if (widget.actId != null && widget.chapterId != null && widget.sceneId != null && !hasTempAIMarks) {
      // 🧹 确保保存时过滤设定引用样式，避免保存临时样式
      final filteredContent = SettingReferenceProcessor.filterSettingReferenceStylesForSave(_pendingContent, caller: '_onTextChanged');
      
      widget.editorBloc.add(
        editor_bloc.UpdateSceneContent(
          novelId: widget.editorBloc.novelId,
          actId: widget.actId!,
          chapterId: widget.chapterId!,
          sceneId: widget.sceneId!,
          content: filteredContent,
          wordCount: _pendingWordCount.toString(),
          isMinorChange: isMinorChange, // 传递是否为微小改动的标志
        ),
      );
    } else {
      // 如果有临时标记，记录日志并完全跳过该事件，避免任何远端保存
      AppLogger.v('SceneEditor', '🚫 存在临时标记，跳过 UpdateSceneContent: ${widget.sceneId}');
    }
    
    // 无论是否为微小改动，都更新最后保存的内容（纯文本用于比较）
    _lastSavedContent = plainText;
    
    // 重置防抖计时器 - 连续输入时只触发一次保存
    if (_debounceTimer?.isActive ?? false) _debounceTimer!.cancel();
    _debounceTimer = Timer(const Duration(seconds: 2), () {
      // 等待2秒再保存本地，这样可以减少本地保存频率
      _saveLocalOnly();
    });
    
    // 设置同步计时器 - 每5分钟同步一次到服务器，仅当存在未保存更改时
    if (_syncTimer == null || !_syncTimer!.isActive) {
      _syncTimer = Timer(const Duration(minutes: 5), () {
        if (_hasUnsavedChanges) {
          _syncToServer();
        }
      });
    }

}
  
  // 检测是否为微小文本改动
  bool _isMinorTextChange(String plainText) {
    if (_lastSavedContent.isEmpty) return false;
    
    // 1. 检查变化的字符数
    final int lengthDiff = (plainText.length - _lastSavedContent.length).abs();
    
    // 2. 计算编辑距离 (简化版 - 仅考虑长度变化)
    // 对于完整的编辑距离(Levenshtein)需要更复杂的算法，这里简化处理
    final int editDistance = min(lengthDiff, _minorChangeThreshold + 1);
    
    // 3. 检查时间间隔 (如果刚刚保存过，更可能是微小改动)
    final timeSinceLastChange = DateTime.now().difference(_lastChangeTime);
    final bool isRecentChange = timeSinceLastChange < const Duration(seconds: 3);
    
    // 4. 综合判断 (字符变化很小，或者最近刚改过且变化不大)
    final bool isMinor = editDistance <= _minorChangeThreshold || 
                         (isRecentChange && editDistance <= _minorChangeThreshold * 2);
    
    AppLogger.v('SceneEditor', '变更分析 - 字符差异: $lengthDiff, 编辑距离: $editDistance, 时间间隔: ${timeSinceLastChange.inMilliseconds}ms, 判定为${isMinor ? "微小" : "重要"}改动');
    
    return isMinor;
  }

  // 保存到本地
  void _saveLocalOnly() {
        // 🚫 避免在AI生成过程中保存含有临时标记的内容
    if (_pendingContent.contains('"ai-generated"') || _pendingContent.contains('"hidden-text"')) {
      AppLogger.v('SceneEditor', '🚫 _saveLocalOnly 检测到临时AI标记，跳过本地保存: \\${widget.sceneId}');
      return;
    }
    if (widget.actId != null && widget.chapterId != null && widget.sceneId != null) {
      // 🧹 本地保存时过滤设定引用样式，避免保存临时样式
      final filteredContent = SettingReferenceProcessor.filterSettingReferenceStylesForSave(_pendingContent, caller: '_saveLocalOnly');
      
      // 直接调用EditorBloc保存，不触发同步
      widget.editorBloc.add(
        editor_bloc.SaveSceneContent(
          novelId: widget.editorBloc.novelId,
          actId: widget.actId!,
          chapterId: widget.chapterId!,
          sceneId: widget.sceneId!,
          content: filteredContent,
          wordCount: _pendingWordCount.toString(),
          localOnly: true, // 仅保存到本地
        ),
      );
      
      // 更新最后保存的内容（保存纯文本用于比较）
      _lastSavedContent = QuillHelper.deltaToText(_pendingContent);
    } else if (widget.onContentChanged != null) {
      // 🧹 本地保存时过滤设定引用样式，避免保存临时样式
      final filteredContent = SettingReferenceProcessor.filterSettingReferenceStylesForSave(_pendingContent, caller: '_saveLocalOnly_callback');
      
      // 如果提供了回调，使用回调函数
      widget.onContentChanged!(filteredContent, _pendingWordCount, syncToServer: false);
      
      // 更新最后保存的内容（保存纯文本用于比较）
      _lastSavedContent = QuillHelper.deltaToText(_pendingContent);
    }
  }
  
  // 同步到服务器
  void _syncToServer() {
    // 🚫 如果仍包含 AI 临时标记（ai-generated/hidden-text），直接跳过远端同步，避免在生成过程中保存至后端
    if (_pendingContent.contains('"ai-generated"') ||
        _pendingContent.contains('"hidden-text"')) {
      AppLogger.v('SceneEditor', '🚫 存在 AI 临时标记，跳过 _syncToServer');
      // 仍然保留 _hasUnsavedChanges = true ，这样在 Apply 之后可以正常同步
      return;
    }
    
    if (widget.actId != null && widget.chapterId != null && widget.sceneId != null) {
      // 🧹 同步到服务器时过滤设定引用样式，避免保存临时样式
      final filteredContent = SettingReferenceProcessor.filterSettingReferenceStylesForSave(_pendingContent, caller: '_syncToServer');
      
      // 使用EditorBloc同步到服务器
      widget.editorBloc.add(
        editor_bloc.SaveSceneContent(
          novelId: widget.editorBloc.novelId,
          actId: widget.actId!,
          chapterId: widget.chapterId!,
          sceneId: widget.sceneId!,
          content: filteredContent,
          wordCount: _pendingWordCount.toString(),
          localOnly: false, // 同步到服务器
        ),
      );
      
      // 更新最后保存的内容（保存纯文本用于比较）
      _lastSavedContent = QuillHelper.deltaToText(_pendingContent);
    } else if (widget.onContentChanged != null) {
      // 🧹 同步到服务器时过滤设定引用样式，避免保存临时样式
      final filteredContent = SettingReferenceProcessor.filterSettingReferenceStylesForSave(_pendingContent, caller: '_syncToServer_callback');
      
      // 如果提供了回调，使用回调函数
      widget.onContentChanged!(filteredContent, _pendingWordCount, syncToServer: true);
      
      // 更新最后保存的内容（保存纯文本用于比较）
      _lastSavedContent = QuillHelper.deltaToText(_pendingContent);
    }
  }

  // 处理文本选择变化
  void _handleSelectionChange() {

      // 若选区变化太快，跳过更新
      final selection = widget.controller.selection;
      if (selection.isCollapsed) {
        // 如果没有选择文本，隐藏工具栏
        if (_showToolbar) {
          setState(() {
            _showToolbar = false;
            _selectedTextWordCount = 0;
          });
        }
        return;
      }
      
      // 使用更高效的节流控制
      _selectionDebounceTimer?.cancel();
      _selectionDebounceTimer = Timer(const Duration(milliseconds: 250), () {
        if (!mounted) return;
        
        // 高效判断是否需要更新界面
        final selectedText = widget.controller.document
            .getPlainText(selection.start, selection.end - selection.start);
        final wordCount = WordCountAnalyzer.countWords(selectedText);
        
        // 仅当选择内容与上次不同时才更新
        if (!_showToolbar || _selectedTextWordCount != wordCount) {
          setState(() {
            _showToolbar = true;
            _selectedTextWordCount = wordCount;
            // 简化位置计算，使用固定位置
            _showToolbarAbove = false;
          });
          
          // 🚀 关键修复：选择区域变化时，强制重新构建LayerLink目标
          ////AppLogger.d('SceneEditor', '🎯 选择区域变化，触发LayerLink目标重新定位');
          
          // 🚀 强制触发下一帧重新构建，确保LayerLink目标位置更新
          WidgetsBinding.instance.addPostFrameCallback((_) {
            if (mounted) {
              ////AppLogger.d('SceneEditor', '🔄 强制重新构建LayerLink目标位置');
              setState(() {
                // 这个setState专门用于强制重新构建LayerLink目标
              });
            }
          });
        }
        

      });

  }

  // // 简化的选区矩形计算
  // Rect _calculateSelectionRect() {
  //   try {
  //     // 获取编辑器渲染对象
  //     final RenderBox? editorBox =
  //         _editorContentKey.currentContext?.findRenderObject() as RenderBox?;
  //     if (editorBox == null) return Rect.zero;

  //     // 获取编辑器全局坐标
  //     final editorOffset = editorBox.localToGlobal(Offset.zero);
  //     final editorWidth = editorBox.size.width;

  //     // 创建一个固定位置，避免复杂计算
  //     return Rect.fromLTWH(
  //       editorWidth * 0.5 - 50, // 水平居中偏左
  //       50, // 固定在顶部下方50像素
  //       100, // 固定宽度
  //       30, // 固定高度
  //     );
  //   } catch (e) {
  //     return Rect.zero;
  //   }
  // }

  @override
  void dispose() {
    // 页面关闭前确保同步到服务器
    _debounceTimer?.cancel();
    _syncTimer?.cancel();
    _settingReferenceProcessTimer?.cancel(); // 取消设定引用处理定时器
    _scrollPositionTimer?.cancel(); // 🚀 取消摘要位置更新定时器
    
    // 强制保存未保存的更改
    if (_hasUnsavedChanges && 
        widget.actId != null && 
        widget.chapterId != null && 
        widget.sceneId != null &&
        _pendingContent.isNotEmpty) {
      
      AppLogger.i('SceneEditor', '组件销毁前强制保存场景内容: ${widget.sceneId}');
      
      // 🧹 确保保存前过滤设定引用样式
      final filteredContent = SettingReferenceProcessor.filterSettingReferenceStyles(_pendingContent, caller: 'dispose');
      
      // 获取当前摘要内容
      final currentSummary = widget.summaryController.text;
      
      // 立即触发强制保存事件
      widget.editorBloc.add(
        editor_bloc.ForceSaveSceneContent(
          novelId: widget.editorBloc.novelId,
          actId: widget.actId!,
          chapterId: widget.chapterId!,
          sceneId: widget.sceneId!,
          content: filteredContent,
          wordCount: _pendingWordCount.toString(),
          summary: currentSummary.isNotEmpty ? currentSummary : null,
        ),
      );
      
      AppLogger.i('SceneEditor', '强制保存事件已触发: ${widget.sceneId}');
    }
    
    _focusNode.removeListener(_onEditorFocusChange);
    _summaryFocusNode.removeListener(_onSummaryFocusChange);
    _contentDebounceTimer?.cancel(); // 取消内容防抖定时器
    _selectionDebounceTimer?.cancel(); // 取消选择防抖定时器
    _focusDebounceTimer?.cancel(); // 取消焦点防抖定时器
    _activeStateDebounceTimer?.cancel(); // 🚀 取消活动状态防抖定时器
    _streamingTimer?.cancel(); // 取消AI流式输出定时器
    widget.controller.removeListener(_handleSelectionChange); // 移除选择变化监听
    
    // 🚀 移除摘要滚动监听
    _removeSummaryScrollListener();
    
    // 🚀 场景销毁时不需要特别处理，数据管理器会自动处理数据持久化
    if (widget.sceneId != null && 
        OverlaySceneBeatManager.instance.isVisible && 
        OverlaySceneBeatManager.instance.currentSceneId == widget.sceneId) {
      AppLogger.i('SceneEditor', '🔄 场景销毁，场景节拍数据由数据管理器自动管理: ${widget.sceneId}');
    }
    
    _focusNode.dispose();
    _summaryFocusNode.dispose();
    _editorScrollController.dispose(); // 释放滚动控制器
    _summaryTopOffsetVN.dispose();
    super.dispose();

    // 取消 document.changes 订阅，避免泄漏
    _docChangeSub?.cancel();
  }

  @override
  bool get wantKeepAlive => widget.isVisuallyNearby;

  @override
  Widget build(BuildContext context) {
    super.build(context);
  
      final theme = Theme.of(context);
      final bool isEditorOrSummaryFocused = _isFocused || _isSummaryFocused;



      return RepaintBoundary(
        child: _buildOptimizedSceneEditor(theme, isEditorOrSummaryFocused),
      );
 
  }
  
  // 优化后的场景编辑器构建方法
  Widget _buildOptimizedSceneEditor(ThemeData theme, bool isEditorOrSummaryFocused) {

      // 🚀 修改：使用Stack布局来实现摘要滚动固定效果
      return Container(
        key: _sceneContainerKey, // 🚀 添加场景容器key
        decoration: WebTheme.getCleanCardDecoration(context: context),
        // 调整卡片间距，代替之前的 SceneDivider
        margin: EdgeInsets.only(
            bottom: widget.isFirst ? 16.0 : 24.0, top: widget.isFirst ? 0 : 8.0),
        child: GestureDetector(
          onTapDown: (_) {
            // ⚠️ 原因：避免在指针事件分发期间同步重建/状态修改导致 MouseTracker 重入（Flutter Web 断言）
            WidgetsBinding.instance.addPostFrameCallback((_) {
              // 🚀 优化：只在非焦点状态且活动状态确实需要改变时才进行激活操作
              if (!_isFocused && !_isSummaryFocused) {
                // 检查当前是否已经是活动状态
                final editorBloc = widget.editorBloc;
                if (editorBloc.state is editor_bloc.EditorLoaded) {
                  final state = editorBloc.state as editor_bloc.EditorLoaded;
                  final isAlreadyActive = state.activeActId == widget.actId &&
                      state.activeChapterId == widget.chapterId &&
                      state.activeSceneId == widget.sceneId;
                  
                  // 只有当不是活动状态时才设置
                  if (!isAlreadyActive) {
                    _setActiveElementsQuietly();
                  }
                } else {
                  // 状态不明确时才设置
                  _setActiveElementsQuietly();
                }
              }
            });
          },
          // 添加点击处理，但确保不会干扰子控件的焦点
          onTap: () {
            // ⚠️ 原因：同上，避免在指针事件回调里同步更改焦点树
            WidgetsBinding.instance.addPostFrameCallback((_) {
              // 🚀 优化：如果编辑器还没有焦点，尝试获取焦点
              if (!_isFocused && !_isSummaryFocused && mounted) {
                // 只有当没有其他焦点时，才请求焦点
                if (!FocusScope.of(context).hasFocus && _focusNode.canRequestFocus) {
                  _focusNode.requestFocus();
                }
              }
            });
          },
          behavior: HitTestBehavior.translucent, // 确保即使有子组件也能接收手势
          child: Padding(
            padding: const EdgeInsets.all(16.0), // 卡片内部统一内边距
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                // 场景标题和字数统计 (移到卡片内部)
                _buildSceneHeader(
                    theme, isEditorOrSummaryFocused), // 传入 theme 和焦点状态
                const SizedBox(height: 12), // 增加标题和内容间距

                // 🚀 修改：使用Stack布局来实现摘要滚动固定
                Stack(
                  children: [
                    // 编辑器区域 - 现在占用全宽度
                    Row(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: [
                        // 编辑器区域 - 移除flex，让其自由填充
                        Expanded( 
                          child: Stack(
                            children: [
                              // 编辑器（包装在设定引用悬停检测组件中）
                              Stack(
                                children: [
                                  // 主编辑器
                                  _buildEditor(theme, isEditorOrSummaryFocused),
                                  // 动态跟随选择区域的LayerLink目标
                                  if (_showToolbar && _isEditorFullyInitialized)
                                    _buildEmbeddedLayerLinkTarget(),
                                  // AI工具栏的LayerLink目标
                                  if (_showAIToolbar && _isEditorFullyInitialized)
                                    _buildEmbeddedAILayerLinkTarget(),
                                ],
                              ),
                              // 文本选择工具栏
                              if (_showToolbar && _isEditorFullyInitialized)
                                Positioned(
                                  child: SelectionToolbar(
                                  
                                    controller: widget.controller,
                                    layerLink: _toolbarLayerLink,
                                    wordCount: _selectedTextWordCount,
                                    editorSize: _editorContentKey.currentContext
                                            ?.findRenderObject() is RenderBox
                                        ? (_editorContentKey.currentContext!
                                                .findRenderObject() as RenderBox)
                                            .size
                                        : const Size(300, 150),
                                    selectionRect: Rect.zero,
                                    showAbove: _showToolbarAbove,
                                    scrollController: _editorScrollController,
                                    // 🚀 修改：使用从props传递的数据，而不是null值
                                    novel: widget.novel,
                                    settings: widget.settings,
                                    settingGroups: widget.settingGroups,
                                    snippets: widget.snippets,
                                    novelId: _getNovelId(), // 传递小说ID
                                    onClosed: () {
                                      setState(() {
                                        _showToolbar = false;
                                      });
                                    },
                                    onFormatChanged: () {
                                      // 格式变更时可能需要更新选择状态
                                      _handleSelectionChange();
                                    },
                                    onSettingCreated: (settingItem) {
                                      // 处理设定创建成功 - 现在后端保存已在detail组件内部处理
                                      AppLogger.i('SceneEditor', '设定创建成功: ${settingItem.name}');
                                      // 可以在这里刷新侧边栏设定列表或做其他UI更新
                                    },
                                    onSnippetCreated: (snippet) {
                                      // 处理片段创建成功
                                      AppLogger.i('SceneEditor', '片段创建成功: ${snippet.title}');
                                      // 可以在这里刷新片段列表或做其他操作
                                    },
                                    onStreamingGenerationStarted: (request, model) {
                                      // 处理流式生成开始
                                      _handleStreamingGenerationStarted(request, model);
                                    },
                                    targetKey: _toolbarTargetKey,
                                  ),
                                ),
                              // AI生成工具栏
                              if (_showAIToolbar && _isEditorFullyInitialized)
                                Positioned(
                                  child: Builder(
                                    builder: (context) {
                                      // 检测是否位于前三行，参考写作工具栏逻辑
                                      bool isInFirstThreeLines = false;
                                      try {
                                        final selection = widget.controller.selection;
                                        final document = widget.controller.document;
                                        final plainText = document.toPlainText();
                                        final pos = selection.isCollapsed 
                                            ? selection.baseOffset 
                                            : selection.start;
                                        final safePos = pos.clamp(0, plainText.length);
                                        final before = plainText.substring(0, safePos);
                                        final lineBreaks = '\n'.allMatches(before).length;
                                        final lineNumber = lineBreaks + 1; // 1-based
                                        isInFirstThreeLines = lineNumber <= 3;
                                      } catch (_) {
                                        isInFirstThreeLines = false;
                                      }

                                      final bool showAbove = !isInFirstThreeLines; // 前三行强制下方
                                      final double offsetBelow = isInFirstThreeLines ? 180.0 : 30.0; // 参考写作工具栏

                                      return AIGenerationToolbar(
                                        layerLink: _aiToolbarLayerLink,
                                        onApply: _handleApplyGeneration,
                                        onRetry: _handleRetryGeneration,
                                        onDiscard: _handleDiscardGeneration,
                                        onSection: _handleSectionGeneration,
                                        onStop: _handleStopGeneration,
                                        wordCount: _aiGeneratedWordCount,
                                        modelName: _aiModelName,
                                        isGenerating: _isAIGenerating,
                                        showAbove: showAbove,
                                        offsetBelow: offsetBelow,
                                      );
                                    },
                                  ),
                                ),
                            ],
                          ),
                        ),
                        // 固定宽度的占位空间 - 为摘要区域预留空间 (280px摘要 + 16px间距)
                        const SizedBox(width: 296),
                      ],
                    ),
                    
                    // 🚀 新增：摘要区域 - 使用ValueListenableBuilder监听偏移，无需整棵树setState
                    ValueListenableBuilder<double>(
                      valueListenable: _summaryTopOffsetVN,
                      builder: (context, offsetY, child) {
                        return Positioned(
                          top: offsetY,
                          right: 0,
                          width: 280,
                          child: child!,
                        );
                      },
                      child: Container(
                        key: _summaryKey,
                        margin: const EdgeInsets.only(left: 0),
                        constraints: const BoxConstraints(
                          minHeight: 120,
                        ),
                        child: _buildSummaryArea(theme, isEditorOrSummaryFocused),
                      ),
                    ),
                  ],
                ),

                const SizedBox(height: 16), // 内容和底部间距
              ],
            ),
          ),
        ),
      );

  }
  
  Widget _buildSceneHeader(ThemeData theme, bool isFocused) {

      return Padding(
        // 移除底部 padding，由 SizedBox 控制
        padding: const EdgeInsets.only(bottom: 0.0),
        child: Row(
          crossAxisAlignment: CrossAxisAlignment.center, // 确保垂直居中对齐
          children: [
            // 添加场景序号
            if (widget.sceneIndex != null)
              Text(
                _getSceneIndexText(),
                style: WebTheme.getAlignedTextStyle(
                  baseStyle: theme.textTheme.titleSmall?.copyWith(
                    color: isFocused || widget.isActive
                        ? WebTheme.getTextColor(context)
                        : WebTheme.getSecondaryTextColor(context),
                    fontWeight: FontWeight.w600,
                  ) ?? const TextStyle(),
                ),
              ),
            Text(
              widget.title,
              style: WebTheme.getAlignedTextStyle(
                baseStyle: theme.textTheme.titleSmall?.copyWith(
                  color: isFocused || widget.isActive
                      ? WebTheme.getTextColor(context)
                      : WebTheme.getSecondaryTextColor(context),
                  fontWeight: FontWeight.w600,
                ) ?? const TextStyle(),
              ),
            ),
            const Spacer(),
            if (!widget.wordCount.isNaN)
              Text(
                widget.wordCount.toString(),
                style: WebTheme.getAlignedTextStyle(
                  baseStyle: theme.textTheme.bodySmall?.copyWith(
                    color: WebTheme.getSecondaryTextColor(context),
                    fontSize: 11,
                  ) ?? const TextStyle(),
                ),
              ),
          ],
        ),
      );

  }

  // 添加获取场景序号文本的方法
  String _getSceneIndexText() {
    if (widget.sceneIndex == null) return '';
    
    // 使用中文数字表示场景序号
    final List<String> chineseNumbers = ['零', '一', '二', '三', '四', '五', '六', '七', '八', '九', '十'];
    
    if (widget.sceneIndex! <= 10) {
      return '场景${chineseNumbers[widget.sceneIndex!]} · ';
    } else if (widget.sceneIndex! < 20) {
      return '场景十${chineseNumbers[widget.sceneIndex! - 10]} · ';
    } else {
      // 对于更大的数字，直接使用阿拉伯数字
      return '场景${widget.sceneIndex} · ';
    }
  }

  /// 构建动态跟随选择区域的LayerLink目标
  /// 🚀 修复：使用实际的文档位置计算，而不是估算
  Widget _buildEmbeddedLayerLinkTarget() {
    final selection = widget.controller.selection;
    
    // 只有在有选择时才显示目标
    if (selection.isCollapsed) {
      return const SizedBox.shrink();
    }
    
    //////AppLogger.d('SceneEditor', '🎯 构建精确定位LayerLink目标 - 选择范围: ${selection.start}-${selection.end}');
    
    // 🚀 关键修复：计算选择区域的实际位置
    final targetPosition = _calculateSelectionPosition();
    
    return Positioned(
      // 保持同一个 Element，避免同帧出现多个 LeaderLayer
      // (移除动态 ValueKey，可用默认 key 策略)
      left: targetPosition.dx,
      top: targetPosition.dy,
      child: CompositedTransformTarget(
        link: _toolbarLayerLink,
        child: Container(
          key: _toolbarTargetKey,
          width: 4,
          height: 4,
          decoration: BoxDecoration(
            color: Colors.transparent,
            borderRadius: BorderRadius.circular(2),
          ),
        ),
      ),
    );
  }

  /// 构建AI工具栏的LayerLink目标
  Widget _buildEmbeddedAILayerLinkTarget() {
    // 当AI工具栏需要显示时，始终创建目标点
    if (!_showAIToolbar || !_isEditorFullyInitialized) {
      return const SizedBox.shrink();
    }

    final selection = widget.controller.selection;
    
    // 🚀 修复：获取编辑器宽度，X坐标始终保持在中间
    final RenderBox? editorBox = _editorContentKey.currentContext?.findRenderObject() as RenderBox?;
    if (editorBox == null) {
      return const SizedBox.shrink();
    }
    
    // X坐标固定在编辑器中间
    final centerX = editorBox.size.width / 2;
    double targetY;

    if (selection.isCollapsed) {
      // 🚀 当没有文本选择时（光标折叠），只计算Y坐标
      try {
        final document = widget.controller.document;
        final plainText = document.toPlainText();
        final cursorOffset = selection.baseOffset;
        
        // 计算光标前的文本和行数
        final textBeforeCursor = plainText.substring(0, min(cursorOffset, plainText.length));
        final lines = textBeforeCursor.split('\n');
        final lineCount = lines.length - 1;
        
        // 获取编辑器设置
        final editorSettings = widget.editorSettings ?? const EditorSettings();
        final lineHeight = editorSettings.fontSize * editorSettings.lineSpacing;
        
        // 只计算Y坐标，基于光标所在行
        targetY = editorSettings.paddingVertical + (lineCount * lineHeight);
        
        //AppLogger.d('SceneEditor', '🎯 AI工具栏位置: X=$centerX(固定中间), Y=$targetY, 行数=$lineCount');
      } catch (e) {
        AppLogger.e('SceneEditor', '计算光标Y位置失败', e);
        // 回退到编辑器中下部位置
        targetY = editorBox.size.height * 0.8;
      }
    } else {
      // 有文本选择时，计算选择区域的Y坐标
      final selectionPosition = _calculateSelectionPosition();
      targetY = selectionPosition.dy;
    }

    final targetPosition = Offset(centerX, targetY);

    // === 二次修正：如果工具栏不在可视区域内，则强制居中显示 ===
    try {
      final viewportSize = MediaQuery.of(context).size;
      final RenderBox? editorBox2 = _editorContentKey.currentContext?.findRenderObject() as RenderBox?;
      if (editorBox2 != null) {
        final editorGlobal = editorBox2.localToGlobal(Offset.zero);

        // 与 AIGenerationToolbar 的偏移策略保持一致
        bool isInFirstThreeLines = false;
        try {
          final selection2 = widget.controller.selection;
          final document2 = widget.controller.document;
          final plain2 = document2.toPlainText();
          final pos2 = selection2.isCollapsed ? selection2.baseOffset : selection2.start;
          final safe2 = pos2.clamp(0, plain2.length);
          final before2 = plain2.substring(0, safe2);
          final lineBreaks2 = '\n'.allMatches(before2).length;
          final lineNumber2 = lineBreaks2 + 1; // 1-based
          isInFirstThreeLines = lineNumber2 <= 3;
        } catch (_) {
          isInFirstThreeLines = false;
        }

        final bool showAbove = !isInFirstThreeLines; // 与构建处一致
        final double offsetBelow = isInFirstThreeLines ? 180.0 : 30.0; // 与构建处一致
        final double offsetAbove = -60.0; // AIGenerationToolbar 默认
        final double followerOffsetY = showAbove ? offsetAbove : offsetBelow;

        // 估算"工具栏顶部"的全局Y坐标
        final double followerTopGlobalY = editorGlobal.dy + targetPosition.dy + followerOffsetY;

        // 若顶部超出屏幕，或大幅低于屏幕底部，则将其放到屏幕中间
        final double topGuard = 8.0;
        final double bottomGuard = viewportSize.height - 8.0;
        if (followerTopGlobalY < topGuard || followerTopGlobalY > bottomGuard) {
          final double screenCenterY = viewportSize.height / 2;
          // 反推目标点本地Y：editorGlobal + correctedY + followerOffsetY = screenCenterY
          final double correctedLocalY = screenCenterY - editorGlobal.dy - followerOffsetY;
          // 约束在编辑器内容内部
          targetY = correctedLocalY.clamp(0.0, editorBox2.size.height);
        }
      }
    } catch (_) {
      // 忽略修正失败，使用原位置
    }

    return Positioned(
      key: ValueKey('ai_target_${targetPosition.dx}_${targetY}_${selection.baseOffset}_${selection.extentOffset}'),
      left: targetPosition.dx,
      top: targetY,
      child: CompositedTransformTarget(
        link: _aiToolbarLayerLink,
        child: Container(
          width: 4,
          height: 4,
          decoration: BoxDecoration(
            color: Theme.of(context).colorScheme.primary.withOpacity(0.25),
            borderRadius: BorderRadius.circular(2),
          ),
        ),
      ),
    );
  }

  /// 🚀 新增：精确计算选择区域在编辑器中的位置
  Offset _calculateSelectionPosition() {
    try {
      final selection = widget.controller.selection;
      if (selection.isCollapsed) {
        ////AppLogger.d('SceneEditor', '❌ 选择已折叠，返回默认位置');
        return Offset.zero;
      }

      // 获取编辑器的渲染对象
      final RenderBox? editorBox = _editorContentKey.currentContext?.findRenderObject() as RenderBox?;
      if (editorBox == null) {
        ////AppLogger.d('SceneEditor', '❌ 编辑器渲染对象为空，返回默认位置');
        return Offset.zero;
      }

      // 🚀 关键修复：使用基于行数的精确计算，避免TextPainter的累积误差
      final document = widget.controller.document;
      final plainText = document.toPlainText();
      
      // 获取选择开始位置的文本
      final textBeforeSelection = plainText.substring(0, min(selection.start, plainText.length));
      
      // 🚀 使用编辑器设置获取准确的样式信息
      final editorSettings = widget.editorSettings ?? const EditorSettings();
      
      // 🚀 关键修复：计算行数和位置，使用更准确的方法
      final lines = textBeforeSelection.split('\n');
      final lineCount = lines.length - 1; // 减1因为最后一行不算换行
      final lastLineLength = lines.last.length;
      
      // 🚀 计算实际的行高（考虑编辑器的实际渲染）
      final actualLineHeight = editorSettings.fontSize * editorSettings.lineSpacing;
      
      // 🚀 关键修复：使用编辑器实际高度和文本总行数来计算比例因子
      final totalLines = plainText.split('\n').length;
      final actualEditorHeight = editorBox.size.height - (editorSettings.paddingVertical * 2);
      final heightPerLine = actualEditorHeight / totalLines;
      
      // 🚀 使用修正后的行高，在长文档中使用实际渲染的行高
      final correctedLineHeight = max(heightPerLine, actualLineHeight * 0.8); // 使用较小值，但有最小限制
      
      // 🚀 计算Y位置（基于修正的行高）
      final estimatedY = editorSettings.paddingVertical + (lineCount * correctedLineHeight);
      
      // 🚀 计算X位置：始终使用编辑器内容区域的中心，让工具栏水平居中
      final contentWidth = min(editorBox.size.width, editorSettings.maxLineWidth);
      final estimatedX = (contentWidth / 2) + editorSettings.paddingHorizontal;  // 内容区域中心
      final charWidth = editorSettings.fontSize * 0.6; // 仅用于日志
      
      final finalPosition = Offset(estimatedX, estimatedY);
      
      // 🚀 详细日志，包含修正信息
      //////AppLogger.d('SceneEditor', '✅ 修正选择区域位置: ${finalPosition.dx}, ${finalPosition.dy}');
      //////AppLogger.d('SceneEditor', '   选择位置: ${selection.start}-${selection.end}, 文本长度: ${textBeforeSelection.length}');
      //////AppLogger.d('SceneEditor', '   行数统计: 当前行=$lineCount, 总行数=$totalLines, 最后行长度=$lastLineLength');
      //////AppLogger.d('SceneEditor', '   编辑器尺寸: ${editorBox.size}, 实际内容高度: $actualEditorHeight');
      //////AppLogger.d('SceneEditor', '   行高计算: 理论行高=$actualLineHeight, 实际行高=$heightPerLine, 修正行高=$correctedLineHeight');
      //////AppLogger.d('SceneEditor', '   位置计算: X=$estimatedX (字符宽度=$charWidth), Y=$estimatedY');
      
      return finalPosition;
      
    } catch (e) {
      AppLogger.e('SceneEditor', '❌ 精确计算选择区域位置失败: $e');
      return Offset.zero;
    }
  }



  /// 🚀 构建完整的QuillEditorConfig，充分利用编辑器设置
  QuillEditorConfig _buildQuillEditorConfig(EditorSettings editorSettings) {
    return QuillEditorConfig(
      // 基础设置
      minHeight: editorSettings.minEditorHeight < 1200.0 ? 1200.0 : editorSettings.minEditorHeight,
      maxHeight: null, // 让场景编辑器自由扩展
      maxContentWidth: editorSettings.maxLineWidth,
      
      // 占位符和焦点
      placeholder: '开始写作...',
      autoFocus: false, // 禁用自动聚焦以减少不必要的渲染
      
      // 布局和间距
      padding: EdgeInsets.symmetric(
        vertical: editorSettings.paddingVertical,
        horizontal: editorSettings.paddingHorizontal,
      ),
      expands: false, // 不自动扩展，保持控制
      
      // 滚动设置
      scrollable: editorSettings.smoothScrolling,
      scrollPhysics: editorSettings.smoothScrolling 
          ? const BouncingScrollPhysics() 
          : const ClampingScrollPhysics(),
      
      // 文本设置
      textCapitalization: TextCapitalization.sentences,
      
      // 光标和选择
      showCursor: true,
      paintCursorAboveText: editorSettings.highlightActiveLine,
      enableInteractiveSelection: true,
      enableSelectionToolbar: true,
      
      // 键盘设置
      keyboardAppearance: editorSettings.darkModeEnabled 
          ? Brightness.dark 
          : Brightness.light,
      
      // 自定义样式和交互
      customStyles: _buildCustomStyles(editorSettings),
      customStyleBuilder: _buildCombinedCustomStyleBuilder(),
      customRecognizerBuilder: SettingReferenceInteractionMixin.getCustomRecognizerBuilder(
        onSettingReferenceClicked: (settingId) {
          AppLogger.i('SceneEditor', '🖱️ 设定引用被点击: $settingId');
          _handleSettingReferenceClicked(settingId);
        },
        onSettingReferenceHovered: null,
        onSettingReferenceHoverEnd: null,
      ),
      
      // 行为设置
      detectWordBoundary: true,
      enableAlwaysIndentOnTab: false,
      floatingCursorDisabled: !editorSettings.useTypewriterMode,
      
      // 其他高级设置
      onTapOutsideEnabled: true,
      disableClipboard: false,
      enableScribble: false, // 暂时禁用涂鸦功能
    );
  }

  // 为编辑器添加焦点处理
  Widget _buildEditor(ThemeData theme, bool isFocused) {

      // 获取编辑器设置
      final editorSettings = widget.editorSettings ?? const EditorSettings();
      
      // 在编辑器区域添加MouseRegion
      return MouseRegion(
        //cursor: SystemMouseCursors.text, // 在编辑器区域显示文本光标
        hitTestBehavior: HitTestBehavior.deferToChild, // 优先让子组件处理事件
        child: Container(
          key: _editorContentKey,
          constraints: BoxConstraints(
            maxWidth: editorSettings.maxLineWidth,
            minHeight: editorSettings.minEditorHeight < 1200.0 ? 1200.0 : editorSettings.minEditorHeight,
          ),
          // 使用动态背景色，兼容暗黑 / 亮色主题
          color: WebTheme.getSurfaceColor(context),
          child: Theme(
            data: theme.copyWith(
              // 确保QuillEditor的占位符没有下划线
              inputDecorationTheme: const InputDecorationTheme(
                border: InputBorder.none,
                enabledBorder: InputBorder.none,
                focusedBorder: InputBorder.none,
                errorBorder: InputBorder.none,
                disabledBorder: InputBorder.none,
                focusedErrorBorder: InputBorder.none,
                filled: false,
                hintStyle: TextStyle(
                  color: Colors.grey,
                  decoration: TextDecoration.none, // 明确去掉下划线
                ),
              ),
            ),
            child: QuillEditor.basic(
              // 关键修复：使用依赖 editorSettings 的动态 Key，确保编辑器设置更新后立即重建
              key: ValueKey('editor_${widget.sceneId}_${widget.editorSettings?.hashCode ?? 0}'),
              controller: widget.controller,
              focusNode: _focusNode, // 使用编辑器的 FocusNode
              scrollController: _editorScrollController, // 使用实例变量的滚动控制器
              config: _buildQuillEditorConfig(editorSettings),
            ),
          ),
        ),
      );

  }

  /// 根据编辑器设置构建自定义样式
  DefaultStyles _buildCustomStyles(EditorSettings settings) {
    final baseTextStyle = TextStyle(
      color: WebTheme.getTextColor(context),
      fontSize: settings.fontSize,
      fontFamily: settings.fontFamily,
      fontWeight: settings.fontWeight,
      height: settings.lineSpacing,
      letterSpacing: settings.letterSpacing,
      decoration: TextDecoration.none,
    );

    return DefaultStyles(
      // 段落样式 - 🚀 修复：移除默认左缩进，避免大空白
      paragraph: DefaultTextBlockStyle(
        baseTextStyle,
        HorizontalSpacing.zero, // 不使用默认缩进
        settings.paragraphSpacing > 0 
            ? VerticalSpacing(settings.paragraphSpacing, 0) 
            : VerticalSpacing.zero, // 🚀 修复：段落间距为0时使用zero
        VerticalSpacing.zero, // 🚀 修复：确保行间距也为zero
        null,
      ),
      // 占位符样式 - 🚀 修复：移除默认左缩进
      placeHolder: DefaultTextBlockStyle(
        baseTextStyle.copyWith(
          color: WebTheme.getSecondaryTextColor(context),
        ),
        HorizontalSpacing.zero, // 不使用默认缩进
        settings.paragraphSpacing > 0 
            ? VerticalSpacing(settings.paragraphSpacing, 0) 
            : VerticalSpacing.zero, // 🚀 修复：段落间距为0时使用zero
        VerticalSpacing.zero, // 🚀 修复：确保行间距也为zero
        null,
      ),
      // 粗体样式
      bold: baseTextStyle.copyWith(
        fontWeight: FontWeight.bold,
      ),
      // 斜体样式
      italic: baseTextStyle.copyWith(
        fontStyle: FontStyle.italic,
      ),
      // 下划线样式
      underline: baseTextStyle.copyWith(
        decoration: TextDecoration.underline,
      ),
      // 删除线样式
      strikeThrough: baseTextStyle.copyWith(
        decoration: TextDecoration.lineThrough,
      ),
      // 链接样式
      link: baseTextStyle.copyWith(
        color: settings.darkModeEnabled ? Colors.lightBlue : Colors.blue,
        decoration: TextDecoration.underline,
      ),
      // 标题样式 - 🚀 修复：移除默认左缩进
      h1: DefaultTextBlockStyle(
        baseTextStyle.copyWith(
          fontSize: settings.fontSize * 2.0,
          fontWeight: FontWeight.bold,
        ),
        HorizontalSpacing.zero, // 不使用默认缩进
        settings.paragraphSpacing > 0 
            ? VerticalSpacing(settings.paragraphSpacing * 2, 0) 
            : VerticalSpacing.zero, // 🚀 修复：段落间距为0时使用zero
        VerticalSpacing.zero, // 🚀 修复：确保行间距也为zero
        null,
      ),
      h2: DefaultTextBlockStyle(
        baseTextStyle.copyWith(
          fontSize: settings.fontSize * 1.5,
          fontWeight: FontWeight.bold,
        ),
        HorizontalSpacing.zero, // 不使用默认缩进
        settings.paragraphSpacing > 0 
            ? VerticalSpacing(settings.paragraphSpacing * 1.5, 0) 
            : VerticalSpacing.zero, // 🚀 修复：段落间距为0时使用zero
        VerticalSpacing.zero, // 🚀 修复：确保行间距也为zero
        null,
      ),
      h3: DefaultTextBlockStyle(
        baseTextStyle.copyWith(
          fontSize: settings.fontSize * 1.25,
          fontWeight: FontWeight.bold,
        ),
        HorizontalSpacing.zero, // 不使用默认缩进
        settings.paragraphSpacing > 0 
            ? VerticalSpacing(settings.paragraphSpacing, 0) 
            : VerticalSpacing.zero, // 🚀 修复：段落间距为0时使用zero
        VerticalSpacing.zero, // 🚀 修复：确保行间距也为zero
        null,
      ),
      // 内联代码样式
      inlineCode: InlineCodeStyle(
        backgroundColor: Colors.transparent,
        radius: const Radius.circular(3),
        style: baseTextStyle.copyWith(
          fontFamily: 'monospace',
        ),
      ),
      // 列表样式 - 🚀 保留缩进：列表项需要缩进来显示层级
      lists: DefaultListBlockStyle(
        baseTextStyle,
        HorizontalSpacing(settings.indentSize, 0), // 列表项保持缩进
        VerticalSpacing(settings.paragraphSpacing / 2, 0),
        VerticalSpacing(0, 0),
        null,
        null,
      ),
      // 引用样式 - 🚀 保留缩进：引用通常需要视觉上的缩进
      quote: DefaultTextBlockStyle(
        baseTextStyle.copyWith(
          color: WebTheme.getSecondaryTextColor(context),
          fontStyle: FontStyle.italic,
        ),
        HorizontalSpacing(settings.indentSize, 0), // 引用保持缩进
        VerticalSpacing(settings.paragraphSpacing, 0),
        VerticalSpacing(0, 0),
        BoxDecoration(
          border: Border(
            left: BorderSide(
              width: 4,
              color: WebTheme.getSecondaryTextColor(context),
            ),
          ),
        ),
      ),
    );
  }
  
  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    
    // 🎯 优化：只在真正需要时处理设定引用，避免频繁调用
    // 检查是否有实质性的依赖变化
    final hasSignificantChange = _hasSignificantDependencyChange();
    if (hasSignificantChange) {
      WidgetsBinding.instance.addPostFrameCallback((_) {
        if (mounted && !_isProcessingSettingReferences) {
          ////AppLogger.d('SceneEditor', '🔄 依赖变化触发设定引用处理: ${widget.sceneId}');
          _processSettingReferencesDebounced(); // 使用防抖版本
        }
      });
    }
  }

  @override
  void didUpdateWidget(SceneEditor oldWidget) {
    super.didUpdateWidget(oldWidget);
    
    // 🎯 优化：只在组件内容真正更新时处理设定引用
    final hasContentChange = oldWidget.sceneId != widget.sceneId ||
                           oldWidget.controller != widget.controller;
    
    if (hasContentChange && mounted) {
      WidgetsBinding.instance.addPostFrameCallback((_) {
        if (mounted && !_isProcessingSettingReferences) {
          ////AppLogger.d('SceneEditor', '🔄 组件更新触发设定引用处理: ${widget.sceneId}');
          _processSettingReferencesDebounced(); // 使用防抖版本
        }
      });
    }

    // 🛠️ 当父组件替换了 controller（例如占位控制器异步解析完成后），
    // 需要把监听器从旧 controller 上移除并绑定到新的 controller，
    // 否则选区变化和文档变化都不会再触发当前组件的回调，
    // 从而导致 SelectionToolbar 无法弹出。
    if (oldWidget.controller != widget.controller) {
      // 移除旧 controller 的监听
      oldWidget.controller.removeListener(_handleSelectionChange);

      // 取消旧 controller 的 document 订阅
      _docChangeSub?.cancel();

      // 绑定新 controller 的监听
      widget.controller.addListener(_handleSelectionChange);

      // 重新订阅 document.changes 并保存引用
      _docChangeSub = widget.controller.document.changes.listen(_onDocumentChange);
    }
  }

  // 🎯 新增：检查是否有实质性的依赖变化
  bool _hasSignificantDependencyChange() {
    // 可以根据需要检查具体的依赖变化
    // 目前简化处理，减少不必要的触发
    return true; // 暂时保持原有行为，后续可以进一步优化
  }

  Widget _buildSummaryArea(ThemeData theme, bool isFocused) {
    // 🚀 优化：使用自适应高度的布局
    return Container(
      // 移除 margin，由 Row 的 SizedBox 控制
      padding: const EdgeInsets.all(12), // 调整摘要区内边距
      decoration: BoxDecoration(
        color: WebTheme.getSurfaceColor(context), // 使用动态表面色
        borderRadius: BorderRadius.circular(8), // 给摘要区本身加圆角
        // 🚀 新增：添加微妙的阴影效果，当摘要处于sticky状态时更明显
        boxShadow: _isSummarySticky ? [
          BoxShadow(
            color: WebTheme.getShadowColor(context, opacity: 0.08),
            blurRadius: 8,
            offset: const Offset(0, 2),
          ),
        ] : [
          BoxShadow(
            color: WebTheme.getShadowColor(context, opacity: 0.03),
            blurRadius: 4,
            offset: const Offset(0, 1),
          ),
        ],
      ),
      child: IntrinsicHeight( // 🚀 使用IntrinsicHeight让整个摘要区域自适应内容高度
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          mainAxisSize: MainAxisSize.min, // 🚀 优化：最小化占用空间
          children: [
          // 摘要标题和右上角按钮
          Row(
            crossAxisAlignment: CrossAxisAlignment.center, // 确保垂直居中对齐
            children: [
              Expanded(
                child: Text(
                  '摘要',
                  style: WebTheme.getAlignedTextStyle(
                    baseStyle: theme.textTheme.titleSmall?.copyWith(
                      fontSize: 13,
                      fontWeight: FontWeight.w600,
                      color: isFocused || widget.isActive
                          ? WebTheme.getTextColor(context)
                          : WebTheme.getSecondaryTextColor(context),
                    ) ?? const TextStyle(),
                  ),
                ),
              ),
              // 摘要操作按钮（刷新、AI生成） - 移到右上角
              _buildSummaryActionButtons(theme, isFocused),
            ],
          ),

          const SizedBox(height: 8),

          // 🚀 优化：摘要内容 - 使用自适应高度，统一背景色，保证最小高度
          Container(
            padding: const EdgeInsets.all(12), // 🚀 保持统一的内边距
            constraints: const BoxConstraints(
              minHeight: 60, // 🚀 新增：确保最小高度，即使空内容也有一行文字的高度
            ),
            // 🚀 修复：设置正确的背景色
            decoration: BoxDecoration(
              color: WebTheme.getSurfaceColor(context),
              borderRadius: BorderRadius.circular(12),
            ),
            child: MouseRegion(
              cursor: SystemMouseCursors.text, // 在摘要区域显示文本光标
              child: Material(
                type: MaterialType.transparency, // 使用透明Material类型避免黄色下划线
                child: IntrinsicHeight(
                                      child: TextField(
                      controller: widget.summaryController,
                      focusNode: _summaryFocusNode,
                      style: WebTheme.getAlignedTextStyle(
                        baseStyle: theme.textTheme.bodyMedium?.copyWith(
                          color: WebTheme.getTextColor(context), // 改为主要文字颜色
                          fontSize: 13,
                          height: 1.4,
                        ) ?? const TextStyle(),
                      ),
                      // 🚀 改为自适应高度：不限制最大行数
                      maxLines: null,
                      minLines: 2,
                      keyboardType: TextInputType.multiline, // 支持多行输入
                      textInputAction: TextInputAction.newline, // 支持换行
                    decoration: WebTheme.getBorderlessInputDecoration(
                      hintText: '添加场景摘要...',
                      context: context, // 传递context以设置正确的hintStyle
                    ),
                    // 🚀 自适应模式下禁用内部滚动，让外层滚动容器接管
                    scrollPhysics: const NeverScrollableScrollPhysics(),
                    onChanged: (value) {
                      _debounceTimer?.cancel();
                      _debounceTimer = Timer(const Duration(milliseconds: 1200), () {
                        // 🚀 新增：检查控制器是否仍然有效
                        if (!mounted || widget.summaryController.text != value) {
                          AppLogger.v('SceneEditor', '摘要控制器已失效或内容已变化，跳过保存: ${widget.sceneId}');
                          return;
                        }
                        
                        if (mounted &&
                            widget.actId != null &&
                            widget.chapterId != null &&
                            widget.sceneId != null) {
                          AppLogger.i('SceneEditor', '通过onChange保存摘要: ${widget.sceneId}');
                          widget.editorBloc.add(editor_bloc.UpdateSummary(
                            novelId: widget.editorBloc.novelId,
                            actId: widget.actId!,
                            chapterId: widget.chapterId!,
                            sceneId: widget.sceneId!,
                            summary: value,
                            shouldRebuild: true, // 改为true，确保UI更新和完整保存
                          ));
                        }
                        
                        // 🚀 新增：内容变化时更新摘要高度
                        WidgetsBinding.instance.addPostFrameCallback((_) {
                          if (mounted) {
                            _updateSummaryHeight();
                            _updateSummaryPosition();
                          }
                        });
                      });
                    },
                  ),
                ),
              ),
            ),
          ),

          const SizedBox(height: 12), // 🚀 新增：摘要内容和操作按钮之间的间距

          // 🚀 新增：摘要操作按钮区域
          _buildSummaryBottomActions(theme, isFocused),
        ],
        ),
      ),
    );
  }

  // 🚀 新增：摘要底部操作按钮区域
  Widget _buildSummaryBottomActions(ThemeData theme, bool isFocused) {
    return Row(
      mainAxisAlignment: MainAxisAlignment.start, // 🚀 改为左对齐，避免空间分散
      children: [
        // 🚀 最左边：更多操作按钮（三点菜单）
        if (widget.actId != null && widget.chapterId != null && widget.sceneId != null)
          MenuBuilder.buildSceneMenu(
            context: context,
            editorBloc: widget.editorBloc,
            actId: widget.actId!,
            chapterId: widget.chapterId!,
            sceneId: widget.sceneId!,
          ),
        
        if (widget.actId != null && widget.chapterId != null && widget.sceneId != null)
          const SizedBox(width: 4), // 🚀 减小间距
        
        // // 标签按钮
        // _SummaryActionButton(
        //   icon: Icons.label_outline,
        //   label: '标签',
        //   tooltip: '添加标签',
        //   onPressed: () {/* TODO */},
        // ),
        
        // const SizedBox(width: 4), // 🚀 减小间距
        
        // // Codex按钮
        // _SummaryActionButton(
        //   icon: Icons.lan_outlined,
        //   label: 'Codex',
        //   tooltip: '关联 Codex',
        //   onPressed: () {/* TODO */},
        // ),
        
        // 场景节拍按钮
        const SizedBox(width: 4), // 🚀 减小间距
        _SummaryActionButton(
          icon: Icons.auto_fix_high,
          label: '节拍',
          tooltip: '场景节拍生成',
          onPressed: () {
            if (widget.actId != null && 
                widget.chapterId != null && 
                widget.sceneId != null) {
              _showSceneBeatPanel();
            }
          },
        ),
        
        // AI生成场景按钮（仅在有摘要内容时显示）
        if (widget.summaryController.text.isNotEmpty) ...[
          const SizedBox(width: 4), // 🚀 减小间距
          _SummaryActionButton(
            icon: Icons.auto_stories,
            label: 'AI生成',
            tooltip: '从摘要生成场景内容',
            onPressed: () {
              if (widget.actId != null && 
                  widget.chapterId != null && 
                  widget.sceneId != null) {
                // 获取布局管理器并打开AI生成面板
                final layoutManager = Provider.of<EditorLayoutManager>(context, listen: false);
                
                // 保存当前摘要到EditorBloc中，以便AI生成面板可以获取到
                widget.editorBloc.add(
                  editor_bloc.SetPendingSummary(
                    summary: widget.summaryController.text,
                  ),
                );
                
                // 显示AI生成面板
                layoutManager.toggleAISceneGenerationPanel();
              }
            },
          ),
        ],
      ],
    );
  }

  // 新增：摘要区域右上角的操作按钮
  Widget _buildSummaryActionButtons(ThemeData theme, bool isFocused) {
    // 使用 Row + IconButton 实现
    return Row(
      mainAxisSize: MainAxisSize.min, // 重要：避免 Row 占用过多空间
      children: [
        IconButton(
          icon: Icon(Icons.refresh, size: 18, color: WebTheme.getSecondaryTextColor(context)),
          tooltip: '刷新摘要',
          onPressed: () {
            // 实现刷新摘要逻辑
            if (widget.summaryController.text.isNotEmpty &&
                widget.actId != null &&
                widget.chapterId != null &&
                widget.sceneId != null &&
                mounted) {
              // 🚀 新增：检查控制器是否仍然有效
              try {
                // 尝试访问控制器文本以验证其有效性
                final currentText = widget.summaryController.text;
                
                AppLogger.i('SceneEditor', '通过刷新按钮保存摘要: ${widget.sceneId}');
                widget.editorBloc.add(editor_bloc.UpdateSummary(
                  novelId: widget.editorBloc.novelId,
                  actId: widget.actId!,
                  chapterId: widget.chapterId!,
                  sceneId: widget.sceneId!,
                  summary: currentText,
                  shouldRebuild: true, // 修改为true，确保完整保存到后端
                ));
              } catch (e) {
                AppLogger.w('SceneEditor', '摘要控制器已失效，跳过刷新保存: ${widget.sceneId}', e);
              }
            }
          },
          splashRadius: 18,
          constraints: const BoxConstraints(),
          padding: const EdgeInsets.symmetric(horizontal: 4),
          visualDensity: VisualDensity.compact,
          // 添加悬停效果
          hoverColor: WebTheme.getSurfaceColor(context),
        ),
        IconButton(
          icon: Icon(Icons.auto_awesome, size: 18, color: WebTheme.getSecondaryTextColor(context)),
          tooltip: 'AI 生成摘要',
          onPressed: () {
            // 使用新的摘要生成器
            if (widget.actId != null && 
                widget.chapterId != null && 
                widget.sceneId != null) {
              _showSummaryGenerator();
            }
          },
          splashRadius: 18,
          constraints: const BoxConstraints(),
          padding: const EdgeInsets.symmetric(horizontal: 4),
          visualDensity: VisualDensity.compact,
          // 添加悬停效果
          hoverColor: WebTheme.getSurfaceColor(context),
        ),
      ],
    );
  }



  // 🚀 优化：添加SettingBloc状态监听，处理设定引用
  void _setupSettingBlocListener() {
    final novelId = _getNovelId();
    if (novelId == null) {
      AppLogger.w('SceneEditor', '⚠️ 无法获取小说ID，跳过设定引用监听设置');
      return;
    }
    
    AppLogger.i('SceneEditor', '🎯 设置SettingBloc监听器 - 场景: ${widget.sceneId}, 小说: $novelId');
    
    // 🚀 新增：立即检查当前状态，如果数据已存在则直接处理
    final currentState = context.read<SettingBloc>().state;
    if (currentState.itemsStatus == SettingStatus.success && currentState.items.isNotEmpty) {
      AppLogger.i('SceneEditor', '✅ 设定数据已就绪，立即处理引用 - 条目数量: ${currentState.items.length}');
      // 延迟一帧执行，确保组件已完全初始化
      WidgetsBinding.instance.addPostFrameCallback((_) {
        if (mounted) {
          _checkAndProcessSettingReferences();
        }
      });
    } else {
      AppLogger.i('SceneEditor', '⏳ 设定数据尚未就绪 - 状态: ${currentState.itemsStatus}, 条目数量: ${currentState.items.length}');
    }
    
    // 🚀 优化：设置流监听器，响应后续的数据更新
    context.read<SettingBloc>().stream.listen((state) {
      if (!mounted) return;
      
      // 当设定项目加载完成时，处理设定引用
      if (state.itemsStatus == SettingStatus.success && state.items.isNotEmpty) {
        AppLogger.i('SceneEditor', '🔄 设定数据更新，重新处理引用 - 场景: ${widget.sceneId}, 条目数量: ${state.items.length}');
        _checkAndProcessSettingReferences();
      }
    });
  }
  
  // 🎯 优化：防抖处理设定引用，避免频繁调用
  void _processSettingReferencesDebounced() {
    //if (true) return;
    // 如果正在处理设定引用，跳过新的请求
    if (_isProcessingSettingReferences) {
      AppLogger.v('SceneEditor', '⏭️ 正在处理设定引用，跳过新的请求: ${widget.sceneId}');
      return;
    }
    // 生成期间不处理设定引用，避免与流式变更抢占主线程
    if (_isAIGenerating) {
      AppLogger.v('SceneEditor', '⏭️ 生成中，跳过设定引用处理请求: ${widget.sceneId}');
      return;
    }
    
    // 🎯 新增：检查距离上次处理的时间间隔
    final now = DateTime.now();
    final timeSinceLastProcessing = now.difference(_lastProcessingTime);
    if (timeSinceLastProcessing < _minProcessingInterval) {
      AppLogger.v('SceneEditor', '⏭️ 处理间隔过短，跳过设定引用处理: ${widget.sceneId}');
      return;
    }
    
    // _settingReferenceProcessTimer?.cancel();
    // _settingReferenceProcessTimer = Timer(const Duration(milliseconds: 800), () {
    //   if (mounted && !_isProcessingSettingReferences) {
    //     _lastProcessingTime = DateTime.now();
    //     _processSettingReferences();
    //   }
    // });
  }
  
  // 🎯 优化：智能处理设定引用（使用防抖和状态检查）
  void _checkAndProcessSettingReferences() {
    if (!mounted || _isProcessingSettingReferences || _isAIGenerating) {
      return;
    }
    
    //AppLogger.i('SceneEditor', '🎯 智能处理设定引用: ${widget.sceneId}');
    
    try {
      // 使用防抖机制避免频繁调用
      _processSettingReferencesDebounced();
    } catch (e) {
      AppLogger.w('SceneEditor', '处理设定引用失败', e);
    }
  }
  

  // 🚀 新增：检查元素是否在视口中可见
 
  
  // 处理设定引用 - Flutter原生实现
  void _processSettingReferences() {
    try {
      if (!mounted) return;
      
      // 🎯 新增：完整内容相等判断，包括样式信息
      final currentDeltaContent = jsonEncode(widget.controller.document.toDelta().toJson());
      final currentText = widget.controller.document.toPlainText();
      
      final int textHash = currentText.hashCode;
      if (textHash == _lastSettingHash) {
        // 文本无实质改动，跳过
        _isProcessingSettingReferences = false;
        return;
      }
      
      // 首先检查完整Delta内容是否相等（包含样式）
      if (currentDeltaContent == _lastProcessedDeltaContent) {
        ////AppLogger.d('SceneEditor', '⏭️ 场景内容完全相等，跳过设定引用处理');
        return;
      }
      
      // 其次检查纯文本内容是否相等（向后兼容）
      if (currentText == _lastProcessedText) {
        ////AppLogger.d('SceneEditor', '⏭️ 文本内容未变化，跳过设定引用处理');
        return;
      }
      
      // 🚀 关键修复：设置处理标志，避免样式变化触发保存
      _isProcessingSettingReferences = true;
      
      ////AppLogger.d('SceneEditor', '🔍 开始Flutter原生设定引用处理，文本长度: ${currentText.length}');
      ////AppLogger.d('SceneEditor', '📝 文本内容预览: ${currentText.length > 100 ? currentText.substring(0, 100) + "..." : currentText}');
      
      final settingState = context.read<SettingBloc>().state;
      final settingItems = settingState.items;
      
      AppLogger.i('SceneEditor', '📚 当前设定条目数量: ${settingItems.length}');
      // if (settingItems.isNotEmpty) {
      //   final validNames = settingItems.where((item) => item.name != null).map((item) => item.name!).join(', ');
      // }
      
      // 🚀 使用Flutter Quill原生Attribute系统处理设定引用
      SettingReferenceProcessor.processSettingReferences(
        document: widget.controller.document,
        settingItems: settingItems,
        controller: widget.controller,
      );
      
      // 🎯 更新：记录处理过的内容
      _lastProcessedText = currentText;
      _lastProcessedDeltaContent = currentDeltaContent;
      _lastSettingHash = textHash;
      
    } catch (e) {
      AppLogger.e('SceneEditor', 'Flutter原生设定引用处理失败', e);
    } finally {
      // 🚀 关键修复：无论成功失败都重置处理标志
      _isProcessingSettingReferences = false;
    }
  }
   

   
   // 处理设定引用点击
   void _handleSettingReferenceClicked(String settingId) {
     AppLogger.i('SceneEditor', '🖱️ 设定引用被点击: $settingId');
     
     final novelId = _getNovelId();
     if (novelId == null) {
       AppLogger.w('SceneEditor', '无法显示设定预览：缺少小说ID');
       return;
     }
     
     AppLogger.i('SceneEditor', '📋 设定引用详情: ID=$settingId, 小说=$novelId');
     
     // 🎯 显示设定预览卡片
     _showSettingPreviewCard(settingId, novelId);
     
     // 触发设定悬停回调
     //_handleSettingReferenceHovered(settingId);
   }

   /// 🎯 构建组合的自定义样式构建器
   /// 同时支持设定引用样式和AI生成内容样式
   TextStyle Function(Attribute) _buildCombinedCustomStyleBuilder() {
     return (Attribute attribute) {
       // 1. 处理设定引用样式
       final settingReferenceStyle = SettingReferenceInteractionMixin
           .getCustomStyleBuilderWithHover(hoveredSettingId: null)(attribute);
       
       // 2. 处理AI生成内容样式
       final aiGeneratedStyle = AIGeneratedContentProcessor
           .getCustomStyleBuilder()(attribute);
       
       // 3. 处理背景色属性（保持原有逻辑）
       if (attribute.key == 'background' && attribute.value != null) {
         final colorValue = attribute.value as String;
         
         try {
           // 解析颜色值（支持#FFF3CD格式）
           Color? backgroundColor;
           if (colorValue.startsWith('#')) {
             final hexColor = colorValue.substring(1);
             if (hexColor.length == 6) {
               backgroundColor = Color(int.parse('FF$hexColor', radix: 16));
             }
           }
           
           if (backgroundColor != null) {
             return TextStyle(backgroundColor: backgroundColor);
           }
         } catch (e) {
           AppLogger.w('SceneEditor', '解析背景色失败: $colorValue', e);
         }
       }
       
       // 4. 合并样式（优先级：AI生成 > 设定引用 > 其他）
       if (aiGeneratedStyle.color != null) {
         return aiGeneratedStyle;
       } else if (settingReferenceStyle.decoration != null) {
         return settingReferenceStyle;
       }
       
       // 返回空的TextStyle表示使用默认样式
       return const TextStyle();
     };
   }
   
   /// 显示设定预览卡片 - 使用通用管理器
   /// 
   /// 🎨 采用全局样式和主题的统一设定预览卡片
   /// 🚀 修复了Provider传递问题，确保详情卡片正常打开
   void _showSettingPreviewCard(String settingId, String novelId) {
     try {
       // 获取当前屏幕中心位置
       final screenSize = MediaQuery.of(context).size;
       final position = Offset(
         screenSize.width * 0.5, // 屏幕中心
         screenSize.height * 0.3, // 靠上一些
       );
       
       AppLogger.i('SceneEditor', '📍 显示设定预览卡片: $settingId');
       
       // 🚀 使用通用设定预览管理器，自动处理Provider传递问题
       SettingPreviewManager.show(
         context: context,
         settingId: settingId,
         novelId: novelId,
         position: position,
         onClose: () {
           ////AppLogger.d('SceneEditor', '设定预览卡片已关闭');
         },
         onDetailOpened: () {
           AppLogger.i('SceneEditor', '设定详情卡片已打开');
         },
       );
       
       AppLogger.i('SceneEditor', '✅ 设定预览卡片已显示');
       
     } catch (e) {
       AppLogger.e('SceneEditor', '显示设定预览卡片失败', e);
     }
   }

   /// 处理流式生成开始 - 支持统一AI模型
   void _handleStreamingGenerationStarted(UniversalAIRequest request, UnifiedAIModel model) {
     AppLogger.i('SceneEditor', '🚀 开始流式生成: ${request.requestType}, 模型: ${model.displayName} (公共:${model.isPublic})');
     // 🚀 若存在未应用的AI生成内容或隐藏文本，先自动应用为正文，避免并发生成导致上下文缺失
     try {
       final bool hasAIGen = AIGeneratedContentProcessor.hasAnyAIGeneratedContent(
         controller: widget.controller,
       );
       final bool hasHidden = AIGeneratedContentProcessor.hasAnyHiddenText(
         controller: widget.controller,
       );
       if (hasAIGen || hasHidden) {
         if (_isAIGenerating) {
           _handleStopGeneration();
         }
         _handleApplyGeneration();
       }
     } catch (_) {}
     
     // 🚀 新增：保存请求和统一模型配置，用于重试
     _lastAIRequest = request;
     _lastUnifiedModel = model;
     
     // 已移除 UserAIModelConfigModel 相关逻辑，现在使用 UnifiedAIModel
     
     AppLogger.i('SceneEditor', '💾 保存模型信息: ${model.displayName} (公共模型: ${model.isPublic})');
     
     // 获取当前选择范围
     final selection = widget.controller.selection;
     final selectedText = selection.isCollapsed ? '' : 
         widget.controller.document.toPlainText().substring(selection.start, selection.end);
     
     // 🚀 保存选中的文本，用于返回表单
     _lastSelectedText = selectedText;
     
     // 🆕 根据请求类型决定处理方式
     if ((request.requestType == AIRequestType.refactor || request.requestType == AIRequestType.summary) && !selection.isCollapsed) {
       // 重构或缩写：使用隐藏文本属性标记原选中的文本
       final mode = request.requestType == AIRequestType.refactor ? '重构' : '缩写';
       AppLogger.i('SceneEditor', '🫥 ${mode}模式：隐藏原选中文本 (${selectedText.length}字符)');
       AIGeneratedContentProcessor.markAsHidden(
         controller: widget.controller,
         startOffset: selection.start,
         length: selection.end - selection.start,
       );
       _lastInsertedOffset = selection.end; // 在隐藏文本后插入新内容
     } else {
       // 扩写或其他：在选中范围末尾插入新内容
       AppLogger.i('SceneEditor', '📝 扩写模式：在选中文本后插入新内容');
       _lastInsertedOffset = selection.end;
     }
     
     // 隐藏选择工具栏
     setState(() {
       _showToolbar = false;
       _showAIToolbar = true;
       _isAIGenerating = true;
       _aiModelName = model.displayName;
       _generatedText = '';
       _aiGeneratedWordCount = 0;
       _currentStreamIndex = 0;
       _pendingStreamText = '';
     });

     _aiGeneratedStartOffset = _lastInsertedOffset; // 记录AI生成内容的起始位置

     // 开始流式生成
     _startStreamingGeneration(request);
   }

   /// 开始流式生成
   Future<void> _startStreamingGeneration(UniversalAIRequest request) async {
     try {
       final universalAIRepository = context.read<UniversalAIRepository>();
       
       AppLogger.i('SceneEditor', '📡 发送流式AI请求');
       
       // 同步：如果是场景节拍生成，请先把浮动面板状态置为生成中
       try {
         final bool isSceneBeat = request.requestType == AIRequestType.sceneBeat;
         final String? sid = request.sceneId ?? widget.sceneId;
         if (isSceneBeat && sid != null && sid.isNotEmpty) {
           SceneBeatDataManager.instance.updateSceneStatus(sid, SceneBeatStatus.generating);
         }
       } catch (e) {
         AppLogger.w('SceneEditor', '同步场景节拍状态为生成中失败', e);
       }
       
       // 发送流式请求
       final stream = universalAIRepository.streamRequest(request);
       
       await for (final chunk in stream) {
         if (!mounted || !_isAIGenerating) {
           ////AppLogger.d('SceneEditor', '🛑 流式生成被中断: mounted=$mounted, _isAIGenerating=$_isAIGenerating');
           break;
         }
         
         // 🚀 修复：检查是否收到结束信号
         if (chunk.finishReason != null) {
           AppLogger.i('SceneEditor', '✅ 收到流式生成结束信号: ${chunk.finishReason}');
           // 立即停止生成状态
           setState(() {
             _isAIGenerating = false;
           });

           // 同步：如果是场景节拍生成，将面板状态置为已生成
           try {
             final bool isSceneBeat = request.requestType == AIRequestType.sceneBeat;
             final String? sid = request.sceneId ?? widget.sceneId;
             if (isSceneBeat && sid != null && sid.isNotEmpty) {
               SceneBeatDataManager.instance.updateSceneStatus(sid, SceneBeatStatus.generated);
             }
           } catch (e) {
             AppLogger.w('SceneEditor', '同步场景节拍完成状态失败', e);
           }
           // 🚀 扩写/重构/缩写等流式生成完成：刷新积分
           try {
             // ignore: use_build_context_synchronously
             context.read<CreditBloc>().add(const RefreshUserCredits());
           } catch (_) {}
           break;
         }
         
         if (chunk.content.isNotEmpty) {
           // 🚀 修复：使用同步方式逐字符显示，避免异步延迟导致的状态不一致
           await _appendTextCharByCharSync(chunk.content);
         }
         
         // 更新模型信息
         if (chunk.model != null) {
           setState(() {
             _aiModelName = chunk.model!;
           });
         }
       }
       
       // 🚀 确保在流结束时状态被正确重置
       if (mounted) {
         setState(() {
           _isAIGenerating = false;
         });
         AppLogger.i('SceneEditor', '✅ 流式生成完成，状态已重置');

         // 兜底：如果是场景节拍生成，确保面板状态为已生成
         try {
           final bool isSceneBeat = request.requestType == AIRequestType.sceneBeat;
           final String? sid = request.sceneId ?? widget.sceneId;
           if (isSceneBeat && sid != null && sid.isNotEmpty) {
             SceneBeatDataManager.instance.updateSceneStatus(sid, SceneBeatStatus.generated);
           }
         } catch (e) {
           AppLogger.w('SceneEditor', '兜底同步场景节拍完成状态失败', e);
         }

         // 🚀 触发生成完成回调（如果存在）
         if (_onSceneBeatGenerationComplete != null) {
           try {
             _onSceneBeatGenerationComplete!.call();
           } catch (e) {
             AppLogger.w('SceneEditor', '生成完成回调执行失败', e);
           }
           _onSceneBeatGenerationComplete = null; // 清理引用
         }
       }
       
     } catch (e) {
       AppLogger.e('SceneEditor', '流式生成失败', e);
       
       // 🚀 立即恢复隐藏的文本样式（重构/缩写的横杠样式）
       _restoreHiddenTextOnError();
       
       // 🚀 专门处理积分不足错误
       if (e is InsufficientCreditsException) {
         AppLogger.w('SceneEditor', '积分不足: ${e.message}');
         if (mounted) {
           _showInsufficientCreditsDialog(e, onReturnToForm: _returnToLastForm);
         }
       } else {
         AppLogger.e('SceneEditor', '流式生成其他错误', e);
       }
       
       // 异常情况下也要重置状态
       if (mounted) {
         setState(() {
           _isAIGenerating = false;
         });
       }
       
       // 同步：如果是场景节拍生成，错误时将状态置为 error，以恢复按钮可用
       try {
         final bool isSceneBeat = request.requestType == AIRequestType.sceneBeat;
         final String? sid = request.sceneId ?? widget.sceneId;
         if (isSceneBeat && sid != null && sid.isNotEmpty) {
           SceneBeatDataManager.instance.updateSceneStatus(sid, SceneBeatStatus.error);
         }
       } catch (e2) {
         AppLogger.w('SceneEditor', '同步场景节拍错误状态失败', e2);
       }
       
     } finally {
       // 最终确保状态被重置
       if (mounted && _isAIGenerating) {
         setState(() {
           _isAIGenerating = false;
         });
         AppLogger.i('SceneEditor', '🔄 最终重置AI生成状态');
       }
     }
   }

   /// 🚀 新增：同步的逐字符追加文本方法，避免异步延迟
   Future<void> _appendTextCharByCharSync(String text) async {
     try {
       // 合并当前收到的内容，帧级批量插入，避免字符级频繁更新
       _pendingStreamText += text;
       await Future<void>.delayed(Duration.zero);
       if (!mounted || !_isAIGenerating || _pendingStreamText.isEmpty) return;

       final String batch = _pendingStreamText;
       _pendingStreamText = '';

       // 插入整段文本
       widget.controller.document.insert(_lastInsertedOffset, batch);

       // 🎨 为新插入的文本整体添加AI生成标识
       AIGeneratedContentProcessor.markAsAIGenerated(
         controller: widget.controller,
         startOffset: _lastInsertedOffset,
         length: batch.length,
       );

       _generatedText += batch;
       _lastInsertedOffset += batch.length;
       _aiGeneratedWordCount = _generatedText.length;

       if (mounted) {
         setState(() {});
       }
     } catch (e) {
       AppLogger.e('SceneEditor', '批量插入过程中出错', e);

       // 🚀 恢复隐藏的文本样式
       _restoreHiddenTextOnError();

       // 如果出错，确保停止生成状态
       if (mounted) {
         setState(() {
           _isAIGenerating = false;
         });
       }
     }
   }

   /// 逐字符追加文本（保留原方法以防其他地方调用）
   Future<void> _appendTextCharByChar(String text) async {
     // 🚀 直接调用同步版本
     await _appendTextCharByCharSync(text);
   }

   /// 应用生成的文本
   void _handleApplyGeneration() {
     AppLogger.i('SceneEditor', '✅ 应用AI生成的文本');
     
     // 🎨 移除AI生成标识，将内容转为正常文本
     if (_generatedText.isNotEmpty) {
       final startOffset = _lastInsertedOffset - _generatedText.length;
       AIGeneratedContentProcessor.removeAIGeneratedMarks(
         controller: widget.controller,
         startOffset: startOffset,
         length: _generatedText.length,
       );
     }
     
     // 🆕 同时移除所有隐藏文本标识（如果是重构，隐藏的原文本将被永久删除）
     AIGeneratedContentProcessor.clearAllAIGeneratedMarks(controller: widget.controller);
     // 🗑️ 清除所有隐藏文本标识并物理删除被隐藏的文本
     _removeAllHiddenText();
     
     // 隐藏AI工具栏并重置状态
     setState(() {
       _showAIToolbar = false;
       _isAIGenerating = false;
       _generatedText = '';
       _aiGeneratedWordCount = 0;
       _pendingStreamText = '';
     });
     
     AppLogger.i('SceneEditor', '🎯 AI生成内容已应用为正常文本');
     
     // 📝 现在保存（隐藏文本已被自动过滤掉）
     _onTextChanged(jsonEncode(widget.controller.document.toDelta().toJson()));
   }

   /// 🆕 移除所有隐藏文本（物理删除）
   void _removeAllHiddenText() {
     try {
       final hiddenRanges = AIGeneratedContentProcessor.getHiddenTextRanges(
         controller: widget.controller,
       );
       
       if (hiddenRanges.isEmpty) return;
       
       AppLogger.i('SceneEditor', '🗑️ 物理删除 ${hiddenRanges.length} 个隐藏文本段落');
       
       // 从后往前删除，避免位置偏移问题
       final sortedRanges = hiddenRanges.toList()..sort((a, b) => b.start.compareTo(a.start));
       
       for (final range in sortedRanges) {
         widget.controller.document.delete(range.start, range.length);
         ////AppLogger.d('SceneEditor', '删除隐藏文本: 位置${range.start}, 长度${range.length}');
       }
       
       AppLogger.i('SceneEditor', '✅ 所有隐藏文本已物理删除');
       
     } catch (e) {
       AppLogger.e('SceneEditor', '删除隐藏文本失败', e);
     }
   }

   /// 重新生成
   void _handleRetryGeneration() {
     AppLogger.i('SceneEditor', '🔄 重新生成AI文本');
     
     // 删除已生成的文本
     if (_generatedText.isNotEmpty) {
       final startOffset = _lastInsertedOffset - _generatedText.length;
       widget.controller.document.delete(startOffset, _generatedText.length);
       _lastInsertedOffset = startOffset;
     }
     
     // 🆕 如果有隐藏文本，保持隐藏状态（重构模式重试时不恢复原文本）
     if (AIGeneratedContentProcessor.hasAnyHiddenText(controller: widget.controller)) {
       AppLogger.i('SceneEditor', '🔄 重构模式：检测到隐藏文本，保持隐藏状态准备重新生成');
     }
     
     // 重置状态并重新开始生成
     setState(() {
       _generatedText = '';
       _aiGeneratedWordCount = 0;
       _currentStreamIndex = 0;
       _isAIGenerating = true;
     });
     
     // 🚀 修改：检查是否有保存的请求，有则重新发起，没有则使用模拟数据
     if (_lastAIRequest != null && _lastUnifiedModel != null) {
       AppLogger.i('SceneEditor', '📡 重新发起AI请求: ${_lastAIRequest!.requestType.value}');
       _startStreamingGeneration(_lastAIRequest!);
     } else {
       AppLogger.w('SceneEditor', '⚠️ 没有保存的请求，使用模拟数据');
       _simulateStreamingGeneration();
     }
   }

   /// 模拟流式生成（用于测试）
   void _simulateStreamingGeneration() {
     AppLogger.i('SceneEditor', '🧪 模拟流式生成测试');
     
     const testText = '这是一段AI生成的测试文本，用于演示流式输出功能。文字会一个个地出现，营造出AI正在思考和写作的感觉。每个字符都会有一定的延迟，让用户感受到AI的创作过程。';
     
     // 逐字符显示文本
     _appendTextCharByChar(testText).then((_) {
       // 生成完成
       setState(() {
         _isAIGenerating = false;
       });
       AppLogger.i('SceneEditor', '✅ 模拟流式生成完成');
     });
   }

   /// 丢弃生成的文本
   void _handleDiscardGeneration() {
     AppLogger.i('SceneEditor', '❌ 丢弃AI生成的文本');
     
     // 首先停止生成（如果正在生成中）
     final wasGenerating = _isAIGenerating;
     
     // 删除已生成的文本
     if (_generatedText.isNotEmpty) {
       final startOffset = _lastInsertedOffset - _generatedText.length;
       widget.controller.document.delete(startOffset, _generatedText.length);
     }
     
     // 🆕 恢复所有隐藏文本（移除隐藏标识，让原文本重新显示）
     AIGeneratedContentProcessor.removeHiddenMarks(controller: widget.controller);
     AppLogger.i('SceneEditor', '👁️ 已恢复所有隐藏的原文本');
     
     // 隐藏AI工具栏并重置状态
     setState(() {
       _showAIToolbar = false;
       _isAIGenerating = false;
       _generatedText = '';
       _aiGeneratedWordCount = 0;
       _pendingStreamText = '';
     });
     
     if (wasGenerating) {
       AppLogger.i('SceneEditor', '🛑 AI生成已停止并丢弃');
     } else {
       AppLogger.i('SceneEditor', '🗑️ AI生成的文本已丢弃');
     }
   }

   /// 分段处理
   void _handleSectionGeneration() {
     AppLogger.i('SceneEditor', '📝 处理分段');
     // TODO: 实现分段功能
   }

   /// 停止生成
   void _handleStopGeneration() {
     AppLogger.i('SceneEditor', '🛑 停止AI生成');
     
     // 立即停止生成状态
     setState(() {
       _isAIGenerating = false;
     });
     
     AppLogger.i('SceneEditor', '✅ AI生成已手动停止');
   }

   /// 🚀 新增：在错误发生时恢复隐藏的文本样式
   void _restoreHiddenTextOnError() {
     try {
       // 检查是否有隐藏文本（重构/缩写时应用的横杠样式）
       if (AIGeneratedContentProcessor.hasAnyHiddenText(controller: widget.controller)) {
         AppLogger.i('SceneEditor', '🔄 检测到隐藏文本，恢复原文本样式（移除横杠）');
         
         // 移除隐藏标识，恢复原文本显示
         AIGeneratedContentProcessor.removeHiddenMarks(controller: widget.controller);
         
         AppLogger.i('SceneEditor', '✅ 隐藏文本样式已恢复');
       }
     } catch (e) {
       AppLogger.e('SceneEditor', '恢复隐藏文本样式失败', e);
     }
   }

   /// 🚀 新增：返回表单回调
   void _returnToLastForm() {
     if (_lastAIRequest == null || _lastSelectedText == null) {
       AppLogger.w('SceneEditor', '没有保存的请求信息，无法返回表单');
       return;
     }

     AppLogger.i('SceneEditor', '返回表单: ${_lastAIRequest!.requestType}, 文本长度: ${_lastSelectedText!.length}');

     // 🚀 获取必要的数据（从EditorBloc中获取）
     Novel? novel;
     List<NovelSettingItem> settings = [];
     List<SettingGroup> settingGroups = [];
     List<NovelSnippet> snippets = [];

     final editorBloc = widget.editorBloc;
     if (editorBloc.state is editor_bloc.EditorLoaded) {
       final state = editorBloc.state as editor_bloc.EditorLoaded;
       novel = state.novel;
       // TODO: 从状态中获取 settings, settingGroups, snippets
       // 暂时使用空列表，后续可以完善
     }

     // 🚀 从保存的请求中提取表单参数
     final lastRequest = _lastAIRequest!;
     final instructions = lastRequest.instructions;
     final enableSmartContext = lastRequest.enableSmartContext;
     final contextSelections = lastRequest.contextSelections;
     
     // 🚀 从参数中提取长度/风格等特定设置
     String? length;
     String? style;
     if (lastRequest.parameters != null) {
       length = lastRequest.parameters!['length']?.toString();
       style = lastRequest.parameters!['style']?.toString();
     }

     // 🚀 根据请求类型显示对应的表单，传递保存的参数
     switch (lastRequest.requestType) {
       case AIRequestType.expansion:
         showExpansionDialog(
           context,
           selectedText: _lastSelectedText!,
           // selectedModel: _lastAIModel,  // 已废弃，使用initialSelectedUnifiedModel
           novel: novel,
           settings: settings,
           settingGroups: settingGroups,
           snippets: snippets,
           // 🚀 恢复之前的设置
           initialInstructions: instructions,
           initialLength: length,
           initialEnableSmartContext: enableSmartContext,
           initialContextSelections: contextSelections,
           initialSelectedUnifiedModel: _lastUnifiedModel,
           onStreamingGenerate: (request, model) {
             _handleStreamingGenerationStarted(request, model);
           },
         );
         break;
       case AIRequestType.refactor:
         showRefactorDialog(
           context,
           selectedText: _lastSelectedText!,
           // selectedModel: _lastAIModel,  // 已废弃，使用initialSelectedUnifiedModel
           novel: novel,
           settings: settings,
           settingGroups: settingGroups,
           snippets: snippets,
           // 🚀 恢复之前的设置
           initialInstructions: instructions,
           initialStyle: style,
           initialEnableSmartContext: enableSmartContext,
           initialContextSelections: contextSelections,
           initialSelectedUnifiedModel: _lastUnifiedModel,
           onStreamingGenerate: (request, model) {
             _handleStreamingGenerationStarted(request, model);
           },
         );
         break;
       case AIRequestType.summary:
         showSummaryDialog(
           context,
           selectedText: _lastSelectedText!,
           // selectedModel: _lastAIModel,  // 已废弃，使用initialSelectedUnifiedModel
           novel: novel,
           settings: settings,
           settingGroups: settingGroups,
           snippets: snippets,
           // 🚀 恢复之前的设置
           initialInstructions: instructions,
           initialLength: length,
           initialEnableSmartContext: enableSmartContext,
           initialContextSelections: contextSelections,
           initialSelectedUnifiedModel: _lastUnifiedModel,
           onStreamingGenerate: (request, model) {
             _handleStreamingGenerationStarted(request, model);
           },
         );
         break;
       default:
         AppLogger.w('SceneEditor', '不支持的请求类型: ${lastRequest.requestType}');
         TopToast.error(context, '不支持的请求类型');
     }
   }

   /// 🚀 修改：显示积分不足对话框，支持返回表单
   void _showInsufficientCreditsDialog(InsufficientCreditsException ex, {VoidCallback? onReturnToForm}) {
     showDialog(
       context: context,
       barrierDismissible: false,
       builder: (BuildContext dialogContext) {
         return AlertDialog(
           title: Row(
             children: [
               Icon(
                 Icons.account_balance_wallet,
                 color: Theme.of(context).colorScheme.error,
               ),
               const SizedBox(width: 8),
               const Text('积分余额不足'),
             ],
           ),
           content: Column(
             mainAxisSize: MainAxisSize.min,
             crossAxisAlignment: CrossAxisAlignment.start,
             children: [
               Text(ex.message),
               const SizedBox(height: 16),
               if (ex.requiredCredits != null) ...[
                 Container(
                   padding: const EdgeInsets.all(12),
                   decoration: BoxDecoration(
                     color: Theme.of(context).colorScheme.errorContainer.withOpacity(0.3),
                     borderRadius: BorderRadius.circular(8),
                   ),
                   child: Row(
                     children: [
                       Icon(
                         Icons.info_outline,
                         color: Theme.of(context).colorScheme.error,
                         size: 16,
                       ),
                       const SizedBox(width: 8),
                       Expanded(
                         child: Text(
                           '本次操作需要 ${ex.requiredCredits} 积分',
                           style: TextStyle(
                             color: Theme.of(context).colorScheme.onErrorContainer,
                             fontSize: 14,
                           ),
                         ),
                       ),
                     ],
                   ),
                 ),
                 const SizedBox(height: 16),
               ],
               const Text(
                 '您可以：',
                 style: TextStyle(fontWeight: FontWeight.w500),
               ),
               const SizedBox(height: 8),
               const Text('• 充值积分以继续使用公共模型'),
               const Text('• 配置私有模型（使用自己的API Key）'),
               const Text('• 选择其他更便宜的模型'),
             ],
           ),
           actions: [
             TextButton(
               onPressed: () {
                 Navigator.of(dialogContext).pop();
                 // 🚀 恢复隐藏的文本样式
                 _restoreHiddenTextOnError();
                 // 重置AI工具栏状态
                 setState(() {
                   _showAIToolbar = false;
                   _isAIGenerating = false;
                 });
               },
               child: const Text('取消'),
             ),
             if (onReturnToForm != null) // 🚀 只有当有返回表单回调时才显示
               TextButton(
                 onPressed: () {
                   Navigator.of(dialogContext).pop();
                   // 🚀 恢复隐藏的文本样式
                   _restoreHiddenTextOnError();
                   // 🚀 重新显示选择工具栏
                   setState(() {
                     _showToolbar = true;
                     _showAIToolbar = false;
                     _isAIGenerating = false;
                   });
                   // 🚀 调用返回表单回调
                   onReturnToForm();
                 },
                 style: TextButton.styleFrom(
                   foregroundColor: Theme.of(context).colorScheme.primary,
                 ),
                 child: const Text('返回表单'),
               ),
             ElevatedButton(
               onPressed: () {
                 Navigator.of(dialogContext).pop();
                 // TODO: 跳转到充值页面或设置页面
                 // Navigator.pushNamed(context, '/settings/credits');
                 ScaffoldMessenger.of(context).showSnackBar(
                   const SnackBar(content: Text('跳转到积分充值页面（功能开发中）')),
                 );
               },
               child: const Text('去充值'),
             ),
           ],
         );
       },
     );
   }

   


  // 监听内容加载完成，重新处理设定引用
  void _setupContentLoadListener() {
    widget.editorBloc.stream.listen((state) {
      if (!mounted) return;
      
      // 当内容发生变化时，重新处理设定引用
      if (state is editor_bloc.EditorLoaded) {
        // 延迟执行，确保UI已更新
        WidgetsBinding.instance.addPostFrameCallback((_) {
          if (mounted) {
            ////AppLogger.d('SceneEditor', '📝 内容加载完成，重新处理设定引用: ${widget.sceneId}');
            _checkAndProcessSettingReferences();
          }
        });
      }
    });
  }

  // 添加EditorBloc状态监听，确保摘要控制器内容与模型保持同步
  void _setupBlocListener() {
    widget.editorBloc.stream.listen((state) {
      if (!mounted) return;
      
      if (state is editor_bloc.EditorLoaded && 
          widget.sceneId != null && 
          widget.actId != null && 
          widget.chapterId != null) {
        try {
          // 使用更安全的查找方式
          bool found = false;
          String? modelSummaryContent;
          
          // 遍历所有元素查找指定场景
          for (final act in state.novel.acts) {
            if (act.id == widget.actId) {
              for (final chapter in act.chapters) {
                if (chapter.id == widget.chapterId) {
                  for (final scene in chapter.scenes) {
                    if (scene.id == widget.sceneId) {
                      found = true;
                      modelSummaryContent = scene.summary.content ?? '';
                      break;
                    }
                  }
                  if (found) break;
                }
              }
              if (found) break;
            }
          }
          
          // 如果场景不存在，则提前返回
          if (!found) {
            ////AppLogger.d('SceneEditor', '跳过摘要同步：场景不存在或已被删除: ${widget.sceneId}');
            return;
          }
          
          // 如果用户正在编辑摘要，避免用模型内容覆盖用户输入
          if (_summaryFocusNode.hasFocus) {
            return;
          }

          // 当前控制器中的文本
          final currentControllerText = widget.summaryController.text;
          
          // 仅当摘要控制器内容与模型不同时更新
          if (currentControllerText != modelSummaryContent) {
            // 判断变更方向
            if (currentControllerText.isNotEmpty && (modelSummaryContent == null || modelSummaryContent.isEmpty)) {
              // 如果控制器有内容但模型为空，说明是用户刚输入了内容但可能未保存成功
              // 重新触发保存操作确保内容被保存
              AppLogger.i('SceneEditor', '检测到摘要未同步到模型，重新保存: ${widget.sceneId}');
              
              // 将更新放在下一帧执行，避免在build过程中修改
              Future.microtask(() {
                if (mounted) {
                  // 触发摘要保存并强制重建UI以确保更新成功
                  widget.editorBloc.add(editor_bloc.UpdateSummary(
                    novelId: widget.editorBloc.novelId,
                    actId: widget.actId!,
                    chapterId: widget.chapterId!,
                    sceneId: widget.sceneId!,
                    summary: currentControllerText,
                    shouldRebuild: true, // 强制重建UI
                  ));
                }
              });
            } else if (modelSummaryContent != null && modelSummaryContent.isNotEmpty) {
              // 模型中有内容但控制器不同，更新控制器
              AppLogger.i('SceneEditor', '摘要内容从模型同步到控制器: ${widget.sceneId}');
              
              // 将更新放在下一帧执行，避免在build过程中修改
              Future.microtask(() {
                if (mounted) {
                  widget.summaryController.text = modelSummaryContent!;
                }
              });
            }
          }
        } catch (e, stackTrace) {
          // 记录详细错误信息但不抛出异常
          AppLogger.i('SceneEditor', '同步摘要控制器失败，可能是场景已被删除: ${widget.sceneId}');
          AppLogger.v('SceneEditor', '同步摘要控制器详细错误: ${e.toString()}', e, stackTrace);
        }
      }
    });
  }

  // 🚀 新增：设置摘要滚动固定监听
  void _setupSummaryScrollListener() {
    // 查找父级滚动控制器
    WidgetsBinding.instance.addPostFrameCallback((_) {
      _findParentScrollController();
    });
  }

  // 🚀 新增：查找父级滚动控制器
  void _findParentScrollController() {
    try {
      // 通过context查找最近的Scrollable
      final scrollableState = Scrollable.maybeOf(context);
      if (scrollableState != null) {
        _parentScrollController = scrollableState.widget.controller;
        if (_parentScrollController != null) {
          _parentScrollController!.addListener(_onParentScroll);
          ////AppLogger.d('SceneEditor', '已找到并监听父级滚动控制器: ${widget.sceneId}');
        }
      }
    } catch (e) {
      AppLogger.w('SceneEditor', '查找父级滚动控制器失败: ${widget.sceneId}', e);
    }
  }

  // 🚀 新增：父级滚动监听
  void _onParentScroll() {
    if (!mounted || _parentScrollController == null) return;
    
    // 🚀 优化：使用requestAnimationFrame的思路，在下一帧更新位置
    _scrollPositionTimer?.cancel();
    _scrollPositionTimer = Timer(Duration.zero, () {
      if (mounted) {
        // 使用WidgetsBinding.instance.addPostFrameCallback确保在下一帧执行
        WidgetsBinding.instance.addPostFrameCallback((_) {
          if (mounted) {
            _updateSummaryPosition();
          }
        });
      }
    });
  }

  // 🚀 新增：更新摘要位置
  void _updateSummaryPosition() {
    if (!mounted) return;
    
    try {
      // 🚀 优化：首先更新摘要组件的实际高度
      _updateSummaryHeight();
      
      // 获取场景容器的位置信息
      final sceneRenderBox = _sceneContainerKey.currentContext?.findRenderObject() as RenderBox?;
      if (sceneRenderBox == null) return;
      
      // 获取场景容器在屏幕中的位置
      final scenePosition = sceneRenderBox.localToGlobal(Offset.zero);
      final sceneSize = sceneRenderBox.size;
      
      // 🚀 新增：检查场景高度，如果太小则不启用粘性滚动
      if (sceneSize.height < _minSceneHeightForSticky) {
        // 🚀 获取场景内容长度用于日志
        final contentLength = widget.controller.document.toPlainText().trim().length;
        //AppLogger.v('SceneEditor', '场景高度过小(${sceneSize.height}px < $_minSceneHeightForSticky)，内容长度: $contentLength，跳过粘性滚动: ${widget.sceneId}');
        
        // 重置为非粘性状态
        if (_isSummarySticky || _summaryTopOffsetVN.value != 0.0) {
          WidgetsBinding.instance.addPostFrameCallback((_) {
            if (mounted) {
              setState(() {
                _summaryTopOffsetVN.value = 0.0;
                _isSummarySticky = false;
              });
              _lastCalculatedOffset = 0.0;
              _lastStickyState = false;
            }
          });
        }
        return;
      }
      
      // 获取屏幕可视区域
      final mediaQuery = MediaQuery.of(context);
      final screenHeight = mediaQuery.size.height;
      final topPadding = mediaQuery.padding.top;
      final viewportTop = topPadding;
      final viewportBottom = screenHeight;
      
      // 计算场景在视口中的位置
      final sceneTop = scenePosition.dy;
      final sceneBottom = sceneTop + sceneSize.height;
      
      double newTopOffset = 0.0;
      bool newStickyState = false;
      
      // 🚀 优化：计算安全的最大偏移，包含更多边距和底部工具栏高度
      const totalMargin = _summaryTopMargin + _summaryBottomMargin + _bottomToolbarHeight;
      final maxOffset = (sceneSize.height - _summaryHeight - totalMargin).clamp(0.0, sceneSize.height - totalMargin);
      
      // 🚀 优化：添加顶部边距到视口计算
      final adjustedViewportTop = viewportTop + _summaryTopMargin;
      
      // 场景顶部在视口上方，底部在视口内 - 摘要固定在视口顶部
      if (sceneTop < adjustedViewportTop && sceneBottom > adjustedViewportTop) {
        newTopOffset = (adjustedViewportTop - sceneTop).clamp(0.0, maxOffset);
        newStickyState = true;
      }
      // 场景完全在视口内 - 摘要跟随场景顶部
      else if (sceneTop >= adjustedViewportTop && sceneBottom <= viewportBottom) {
        newTopOffset = _summaryTopMargin; // 🚀 保持顶部边距
        newStickyState = false;
      }
      // 场景顶部在视口内，底部在视口下方 - 摘要固定但不超出场景底部
      else if (sceneTop < viewportBottom && sceneBottom > viewportBottom) {
        // 🚀 优化：考虑边距，确保摘要不会超出场景底部
        final idealOffset = adjustedViewportTop - sceneTop;
        newTopOffset = idealOffset.clamp(_summaryTopMargin, maxOffset);
        newStickyState = true;
      }
      // 场景完全在视口外 - 摘要跟随场景
      else {
        newTopOffset = _summaryTopMargin; // 🚀 保持顶部边距
        newStickyState = false;
      }
      
      // 🚀 优化：使用更大的阈值减少闪烁，并检查状态变化
      final offsetChanged = (_lastCalculatedOffset - newTopOffset).abs() > _positionThreshold;
      final stickyChanged = _lastStickyState != newStickyState;
      
      if (offsetChanged || stickyChanged) {
        // 🚀 优化：使用WidgetsBinding.instance.addPostFrameCallback确保UI更新的平滑性
        WidgetsBinding.instance.addPostFrameCallback((_) {
          if (mounted) {
            setState(() {
              _summaryTopOffsetVN.value = newTopOffset;
              _isSummarySticky = newStickyState;
            });
            
            // 更新缓存的值
            _lastCalculatedOffset = newTopOffset;
            _lastStickyState = newStickyState;
            
            //AppLogger.v('SceneEditor', '摘要位置更新: offset=$newTopOffset, sticky=$newStickyState, height=$_summaryHeight, sceneHeight=${sceneSize.height}, 场景=${widget.sceneId}');
          }
        });
      }
      
    } catch (e) {
      AppLogger.w('SceneEditor', '更新摘要位置失败: ${widget.sceneId}', e);
    }
  }

  // 🚀 新增：更新摘要组件的实际高度
  void _updateSummaryHeight() {
    try {
      final summaryRenderBox = _summaryKey.currentContext?.findRenderObject() as RenderBox?;
      if (summaryRenderBox != null) {
        final actualHeight = summaryRenderBox.size.height;
        if ((actualHeight - _summaryHeight).abs() > 5.0) { // 只在高度变化超过5px时更新
          _summaryHeight = actualHeight;
          AppLogger.v('SceneEditor', '摘要高度更新: $_summaryHeight, 场景=${widget.sceneId}');
        }
      }
    } catch (e) {
      AppLogger.v('SceneEditor', '获取摘要高度失败，使用默认值: ${widget.sceneId}', e);
    }
  }

  // 🚀 新增：移除摘要滚动监听
  void _removeSummaryScrollListener() {
    if (_parentScrollController != null) {
      _parentScrollController!.removeListener(_onParentScroll);
      ////AppLogger.d('SceneEditor', '已移除父级滚动监听: ${widget.sceneId}');
    }
  }

  // 🚀 新增：显示摘要生成器
  void _showSummaryGenerator() {
    // 显示AI摘要面板（使用侧边栏方式）
    final layoutManager = context.read<EditorLayoutManager>();
    layoutManager.showAISummaryPanel();
  }

  // 🚀 新增：显示场景节拍面板
  void _showSceneBeatPanel() {
    if (widget.sceneId == null) return;
    
    AppLogger.i('SceneEditor', '🎯 显示场景节拍面板: ${widget.sceneId}');
    
    // 🚀 新增：获取编辑器状态管理器
    EditorScreenController? editorController;
    EditorLayoutManager? layoutManager;
    
    try {
      editorController = Provider.of<EditorScreenController>(context, listen: false);
      layoutManager = Provider.of<EditorLayoutManager>(context, listen: false);
      AppLogger.d('SceneEditor', '✅ 成功获取编辑器状态管理器');
    } catch (e) {
      AppLogger.w('SceneEditor', '⚠️ 获取编辑器状态管理器失败: $e');
    }
    
    // 使用Overlay场景节拍管理器显示面板
    OverlaySceneBeatManager.instance.show(
      context: context,
      sceneId: widget.sceneId!,
      novel: widget.novel,
      settings: widget.settings,
      settingGroups: widget.settingGroups,
      snippets: widget.snippets,
      // 🚀 新增：传递编辑器状态管理器
      editorController: editorController,
      layoutManager: layoutManager,
      onGenerate: (sceneId, request, model) {
        // 触发场景节拍生成
        AppLogger.i('SceneEditor', '🚀 触发场景节拍生成: $sceneId, 模型: ${model.displayName}');
        startSceneBeatGeneration(
          request: request,
          model: model,
          onGenerationComplete: () {
            AppLogger.i('SceneEditor', '✅ 场景节拍生成完成: $sceneId');
          },
        );
      },
    );
  }

  /// 🚀 新增：公开方法，用于从外部触发场景节拍的AI生成
  void startSceneBeatGeneration({
    required UniversalAIRequest request,
    required UnifiedAIModel model,
    VoidCallback? onGenerationComplete,
  }) {
    AppLogger.i('SceneEditor', '🎯 接收到场景节拍生成请求: ${model.displayName}');
    // 🚀 若存在未应用的AI生成内容或隐藏文本，先自动应用为正文，确保新请求包含最新上下文
    try {
      final bool hasAIGen = AIGeneratedContentProcessor.hasAnyAIGeneratedContent(
        controller: widget.controller,
      );
      final bool hasHidden = AIGeneratedContentProcessor.hasAnyHiddenText(
        controller: widget.controller,
      );
      if (hasAIGen || hasHidden) {
        if (_isAIGenerating) {
          _handleStopGeneration();
        }
        _handleApplyGeneration();
      }
    } catch (_) {}
    
    // 🚀 新增：保存请求和统一模型配置，用于重试
    _lastAIRequest = request;
    _lastUnifiedModel = model;
    _lastSelectedText = ''; // 场景节拍没有选中文本

    _aiGeneratedStartOffset = _lastInsertedOffset; // 记录AI生成内容的起始位置
    
    AppLogger.i('SceneEditor', '🚀 开始场景节拍流式生成，插入位置: $_lastInsertedOffset');
    
    // 🚀 修复：延迟一帧显示AI工具栏，确保光标位置和LayerLink目标正确计算
    WidgetsBinding.instance.addPostFrameCallback((_) {
      if (!mounted) return;
      
      // 显示AI工具栏并设置生成状态
      setState(() {
        _showToolbar = false;
        _showAIToolbar = true;
        _isAIGenerating = true;
        _aiModelName = model.displayName;
        _generatedText = '';
        _aiGeneratedWordCount = 0;
        _currentStreamIndex = 0;
        _pendingStreamText = '';
      });
      
      AppLogger.i('SceneEditor', '✅ AI工具栏已显示，LayerLink目标应该已正确定位');
      
      // 🚀 滚动到光标位置，确保AI工具栏可见
      WidgetsBinding.instance.addPostFrameCallback((_) {
        _scrollToCursorPosition();
      });
      
      // 保存回调
      _onSceneBeatGenerationComplete = onGenerationComplete;
      
      // 开始流式生成
      _startStreamingGeneration(request);
    });
  }

  /// 🚀 新增：滚动到光标位置，确保AI工具栏可见
  void _scrollToCursorPosition() {
    try {
      if (_editorContentKey.currentContext != null) {
        Scrollable.ensureVisible(
          _editorContentKey.currentContext!,
          alignment: 1.0, // 将目标放在视口底部
          duration: const Duration(milliseconds: 300),
          curve: Curves.easeInOut,
        );
      }
    } catch (e) {
      AppLogger.e('SceneEditor', '滚动到光标位置失败', e);
    }
  }

  // 🚀 新增：保存生成完成回调
  VoidCallback? _onSceneBeatGenerationComplete;
}

/// 🚀 新增：摘要操作按钮组件
class _SummaryActionButton extends StatelessWidget {
  const _SummaryActionButton({
    required this.icon,
    required this.label,
    this.tooltip,
    this.onPressed,
  });
  
  final IconData icon;
  final String label;
  final String? tooltip;
  final VoidCallback? onPressed;

  @override
  Widget build(BuildContext context) {
    return Tooltip(
      message: tooltip ?? label,
      child: TextButton.icon(
        onPressed: onPressed ?? () {},
        icon: Icon(icon, size: 12, color: WebTheme.getSecondaryTextColor(context)), // 🚀 减小图标尺寸
        label: Text(
          label, 
          style: TextStyle(
            fontSize: 10, // 🚀 减小字体尺寸
            color: WebTheme.getSecondaryTextColor(context),
            fontWeight: FontWeight.w500,
          ),
        ),
        style: TextButton.styleFrom(
          foregroundColor: WebTheme.getSecondaryTextColor(context),
          padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 2), // 🚀 减小内边距
          minimumSize: const Size(0, 24), // 🚀 减小最小尺寸
          tapTargetSize: MaterialTapTargetSize.shrinkWrap, // 🚀 收缩点击目标尺寸
          shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(3)), // 🚀 减小圆角
          visualDensity: VisualDensity.compact,
        ).copyWith(
          overlayColor: WidgetStateProperty.resolveWith<Color?>(
            (Set<WidgetState> states) {
              if (states.contains(WidgetState.hovered)) {
                return WebTheme.getSurfaceColor(context).withOpacity(0.8);
              }
              return null;
            },
          ),
        ),
      ),
    );
  }
}
