diff --git a/super_clones/slack/lib/chat_thread.dart b/super_clones/slack/lib/chat_thread.dart index 8b0e70629..3605f5dc3 100644 --- a/super_clones/slack/lib/chat_thread.dart +++ b/super_clones/slack/lib/chat_thread.dart @@ -305,22 +305,18 @@ class _MessageTileState extends State<_MessageTile> { } Widget _buildMessageContent() { - return IntrinsicWidth( - child: IgnorePointer( - child: SuperEditorDryLayout( - superEditor: SuperReader( - editor: _editor, - documentLayoutKey: _documentLayoutKey, - stylesheet: defaultStylesheet.copyWith( - documentPadding: const EdgeInsets.symmetric( - horizontal: 10.0, - vertical: 0.0, - ), - selectedTextColorStrategy: makeSelectedTextBlack, - addRulesAfter: messageListStyles, - inlineTextStyler: _inlineStyler, - ), + return IgnorePointer( + child: SuperChatBubble( + editor: _editor, + documentLayoutKey: _documentLayoutKey, + stylesheet: defaultStylesheet.copyWith( + documentPadding: const EdgeInsets.symmetric( + horizontal: 10.0, + vertical: 0.0, ), + selectedTextColorStrategy: makeSelectedTextBlack, + addRulesAfter: messageListStyles, + inlineTextStyler: _inlineStyler, ), ), ); diff --git a/super_clones/slack/macos/Runner/DebugProfile.entitlements b/super_clones/slack/macos/Runner/DebugProfile.entitlements index dddb8a30c..0bfcd596f 100644 --- a/super_clones/slack/macos/Runner/DebugProfile.entitlements +++ b/super_clones/slack/macos/Runner/DebugProfile.entitlements @@ -1,12 +1,14 @@ - - com.apple.security.app-sandbox - - com.apple.security.cs.allow-jit - - com.apple.security.network.server - - - + + com.apple.security.app-sandbox + + com.apple.security.cs.allow-jit + + com.apple.security.network.server + + com.apple.security.network.client + + + \ No newline at end of file diff --git a/super_clones/slack/macos/Runner/Release.entitlements b/super_clones/slack/macos/Runner/Release.entitlements index 852fa1a47..4e4055320 100644 --- a/super_clones/slack/macos/Runner/Release.entitlements +++ b/super_clones/slack/macos/Runner/Release.entitlements @@ -1,8 +1,10 @@ - - com.apple.security.app-sandbox - - - + + com.apple.security.app-sandbox + + com.apple.security.network.client + + + \ No newline at end of file diff --git a/super_editor/lib/src/chat/super_chat_bubble.dart b/super_editor/lib/src/chat/super_chat_bubble.dart new file mode 100644 index 000000000..8d7a61dc6 --- /dev/null +++ b/super_editor/lib/src/chat/super_chat_bubble.dart @@ -0,0 +1,491 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:super_editor/src/core/document.dart'; +import 'package:super_editor/src/core/document_debug_paint.dart'; +import 'package:super_editor/src/core/document_interaction.dart'; +import 'package:super_editor/src/core/document_layout.dart'; +import 'package:super_editor/src/core/editor.dart'; +import 'package:super_editor/src/core/styles.dart'; +import 'package:super_editor/src/default_editor/layout_single_column/_layout.dart'; +import 'package:super_editor/src/default_editor/layout_single_column/_presenter.dart'; +import 'package:super_editor/src/default_editor/layout_single_column/_styler_per_component.dart'; +import 'package:super_editor/src/default_editor/layout_single_column/_styler_shylesheet.dart'; +import 'package:super_editor/src/default_editor/layout_single_column/_styler_user_selection.dart'; +import 'package:super_editor/src/default_editor/layout_single_column/super_editor_dry_layout.dart'; +import 'package:super_editor/src/default_editor/text/custom_underlines.dart'; +import 'package:super_editor/src/default_editor/unknown_component.dart'; +import 'package:super_editor/src/infrastructure/content_layers.dart'; +import 'package:super_editor/src/infrastructure/document_gestures_interaction_overrides.dart'; +import 'package:super_editor/src/infrastructure/documents/document_scroller.dart'; +import 'package:super_editor/src/infrastructure/documents/selection_leader_document_layer.dart'; +import 'package:super_editor/src/infrastructure/flutter/build_context.dart'; +import 'package:super_editor/src/infrastructure/platforms/mobile_documents.dart'; +import 'package:super_editor/src/super_reader/read_only_document_android_touch_interactor.dart'; +import 'package:super_editor/src/super_reader/read_only_document_ios_touch_interactor.dart'; +import 'package:super_editor/src/super_reader/read_only_document_keyboard_interactor.dart'; +import 'package:super_editor/src/super_reader/read_only_document_mouse_interactor.dart'; +import 'package:super_editor/src/super_reader/reader_context.dart'; +import 'package:super_editor/src/super_reader/super_reader.dart'; + +class SuperChatBubble extends StatefulWidget { + SuperChatBubble({ + Key? key, + this.focusNode, + this.tapRegionGroupId, + required this.editor, + this.documentLayoutKey, + this.selectionLayerLinks, + Stylesheet? stylesheet, + this.customStylePhases = const [], + this.documentUnderlayBuilders = const [], + this.documentOverlayBuilders = defaultSuperReaderDocumentOverlayBuilders, + List? componentBuilders, + List? keyboardActions, + SelectionStyles? selectionStyle, + this.gestureMode, + this.contentTapDelegateFactory = superReaderLaunchLinkTapHandlerFactory, + this.overlayController, + this.androidHandleColor, + this.androidToolbarBuilder, + this.createOverlayControlsClipper, + this.debugPaint = const DebugPaintConfig(), + }) : stylesheet = stylesheet ?? readOnlyDefaultStylesheet, + selectionStyles = selectionStyle ?? readOnlyDefaultSelectionStyle, + keyboardActions = keyboardActions ?? readOnlyDefaultKeyboardActions, + componentBuilders = componentBuilders != null + ? [...componentBuilders, const UnknownComponentBuilder()] + : [...readOnlyDefaultComponentBuilders, const UnknownComponentBuilder()], + super(key: key); + + final FocusNode? focusNode; + + /// {@macro super_reader_tap_region_group_id} + final String? tapRegionGroupId; + + /// The [Editor] whose [Document] displayed in this [SuperChatBubble]. + /// + /// [SuperChatBubble] prevents users from interacting with, and altering the [Document]. + /// However, [SuperChatBubble] takes an [Editor] so that developers can alter the [Document] + /// through code, such as contributing new content from an AI GPT. + final Editor editor; + + /// [GlobalKey] that's bound to the [DocumentLayout] within + /// this [SuperChatBubble]. + /// + /// This key can be used to lookup visual components in the document + /// layout within this [SuperChatBubble]. + final GlobalKey? documentLayoutKey; + + /// Leader links that connect leader widgets near the user's selection + /// to carets, handles, and other things that want to follow the selection. + /// + /// These links are always created and used within [SuperChatBubble]. By providing + /// an explicit [selectionLayerLinks], external widgets can also follow the + /// user's selection. + final SelectionLayerLinks? selectionLayerLinks; + + /// Style rules applied through the document presentation. + final Stylesheet stylesheet; + + /// Styles applied to selected content. + final SelectionStyles selectionStyles; + + /// Custom style phases that are added to the standard style phases. + /// + /// Documents are styled in a series of phases. A number of such + /// phases are applied, automatically, e.g., text styles, per-component + /// styles, and content selection styles. + /// + /// [customStylePhases] are added after the standard style phases. You can + /// use custom style phases to apply styles that aren't supported with + /// [stylesheet]s. + /// + /// You can also use them to apply styles to your custom [DocumentNode] + /// types that aren't supported by [SuperChatBubble]. For example, [SuperChatBubble] + /// doesn't include support for tables within documents, but you could + /// implement a `TableNode` for that purpose. You may then want to make your + /// table styleable. To accomplish this, you add a custom style phase that + /// knows how to interpret and apply table styles for your visual table component. + final List customStylePhases; + + /// Layers that are displayed beneath the document layout, aligned + /// with the location and size of the document layout. + final List documentUnderlayBuilders; + + /// Layers that are displayed on top of the document layout, aligned + /// with the location and size of the document layout. + final List documentOverlayBuilders; + + /// Priority list of widget factories that create instances of + /// each visual component displayed in the document layout, e.g., + /// paragraph component, image component, horizontal rule component, etc. + final List componentBuilders; + + /// All actions that this editor takes in response to key + /// events, e.g., text entry, newlines, character deletion, + /// copy, paste, etc. + /// + /// These actions are only used when in [TextInputSource.keyboard] + /// mode. + final List keyboardActions; + + /// The [SuperChatBubble] gesture mode, e.g., mouse or touch. + final DocumentGestureMode? gestureMode; + + /// Factory that creates a [ContentTapDelegate], which is given an + /// opportunity to respond to taps on content before the editor, itself. + /// + /// A [ContentTapDelegate] might be used, for example, to launch a URL + /// when a user taps on a link. + final SuperReaderContentTapDelegateFactory? contentTapDelegateFactory; + + /// Shows, hides, and positions a floating toolbar and magnifier. + final MagnifierAndToolbarController? overlayController; + + /// Color of the text selection drag handles on Android. + final Color? androidHandleColor; + + /// Builder that creates a floating toolbar when running on Android. + final WidgetBuilder? androidToolbarBuilder; + + /// Creates a clipper that applies to overlay controls, like drag + /// handles, magnifiers, and popover toolbars, preventing the overlay + /// controls from appearing outside the given clipping region. + /// + /// If no clipper factory method is provided, then the overlay controls + /// will be allowed to appear anywhere in the overlay in which they sit + /// (probably the entire screen). + final CustomClipper Function(BuildContext overlayContext)? createOverlayControlsClipper; + + /// Paints some extra visual ornamentation to help with + /// debugging. + final DebugPaintConfig debugPaint; + + @override + State createState() => _SuperChatBubbleState(); +} + +class _SuperChatBubbleState extends State { + // GlobalKey used to access the [DocumentLayoutState] to figure + // out where in the document the user taps or drags. + late GlobalKey _docLayoutKey; + SingleColumnLayoutPresenter? _docLayoutPresenter; + late SingleColumnStylesheetStyler _docStylesheetStyler; + final _customUnderlineStyler = CustomUnderlineStyler(); + late SingleColumnLayoutCustomComponentStyler _docLayoutPerComponentBlockStyler; + late SingleColumnLayoutSelectionStyler _docLayoutSelectionStyler; + + ContentTapDelegate? _contentTapDelegate; + + late SuperReaderContext _readerContext; + + @visibleForTesting + FocusNode get focusNode => _focusNode; + late FocusNode _focusNode; + + // Leader links that connect leader widgets near the user's selection + // to carets, handles, and other things that want to follow the selection. + late SelectionLayerLinks _selectionLinks; + + // GlobalKey for the iOS editor controls context so that the context data doesn't + // continuously replace itself every time we rebuild. We want to retain the same + // controls because they're shared throughout a number of disconnected widgets. + final _iosControlsContextKey = GlobalKey(); + final _iosControlsController = SuperReaderIosControlsController(); + + @override + void initState() { + super.initState(); + _focusNode = (widget.focusNode ?? FocusNode())..addListener(_onFocusChange); + + _selectionLinks = widget.selectionLayerLinks ?? SelectionLayerLinks(); + + _docLayoutKey = widget.documentLayoutKey ?? GlobalKey(); + + _createReaderContext(); + + _createLayoutPresenter(); + } + + @override + void didUpdateWidget(SuperChatBubble oldWidget) { + super.didUpdateWidget(oldWidget); + + if (widget.selectionLayerLinks != oldWidget.selectionLayerLinks) { + _selectionLinks = widget.selectionLayerLinks ?? SelectionLayerLinks(); + } + + if (widget.editor.document != oldWidget.editor.document) { + _createReaderContext(); + } + + if (widget.stylesheet != oldWidget.stylesheet) { + _createLayoutPresenter(); + } + } + + @override + void dispose() { + _contentTapDelegate?.dispose(); + + _focusNode.removeListener(_onFocusChange); + if (widget.focusNode == null) { + // We are using our own private FocusNode. Dispose it. + _focusNode.dispose(); + } + + super.dispose(); + } + + void _createReaderContext() { + _readerContext = SuperReaderContext( + editor: widget.editor, + getDocumentLayout: () => _docLayoutKey.currentState as DocumentLayout, + scroller: DocumentScroller(), + ); + + _contentTapDelegate?.dispose(); + _contentTapDelegate = widget.contentTapDelegateFactory?.call(_readerContext); + } + + void _createLayoutPresenter() { + if (_docLayoutPresenter != null) { + _docLayoutPresenter!.dispose(); + } + + _docStylesheetStyler = SingleColumnStylesheetStyler( + stylesheet: widget.stylesheet, + ); + + _docLayoutPerComponentBlockStyler = SingleColumnLayoutCustomComponentStyler(); + + _docLayoutSelectionStyler = SingleColumnLayoutSelectionStyler( + document: widget.editor.document, + selection: widget.editor.composer.selectionNotifier, + selectionStyles: widget.selectionStyles, + selectedTextColorStrategy: widget.stylesheet.selectedTextColorStrategy, + ); + + _docLayoutPresenter = SingleColumnLayoutPresenter( + document: widget.editor.document, + componentBuilders: widget.componentBuilders, + pipeline: [ + _docStylesheetStyler, + _docLayoutPerComponentBlockStyler, + _customUnderlineStyler, + ...widget.customStylePhases, + // Selection changes are very volatile. Put that phase last + // to minimize view model recalculations. + _docLayoutSelectionStyler, + ], + ); + + _recomputeIfLayoutShouldShowCaret(); + } + + void _onFocusChange() { + _recomputeIfLayoutShouldShowCaret(); + } + + void _recomputeIfLayoutShouldShowCaret() { + _docLayoutSelectionStyler.shouldDocumentShowCaret = + _focusNode.hasFocus && _gestureMode == DocumentGestureMode.mouse; + } + + DocumentGestureMode get _gestureMode { + if (widget.gestureMode != null) { + return widget.gestureMode!; + } + switch (defaultTargetPlatform) { + case TargetPlatform.android: + return DocumentGestureMode.android; + case TargetPlatform.iOS: + return DocumentGestureMode.iOS; + default: + return DocumentGestureMode.mouse; + } + } + + @override + Widget build(BuildContext context) { + return IntrinsicWidth( + child: SuperEditorDryLayout( + superEditor: _buildGestureControlsScope( + // We add a Builder immediately beneath the gesture controls scope so that + // all descendant widgets built within SuperChatBubble can access that scope. + child: Builder(builder: (controlsScopeContext) { + return ReadOnlyDocumentKeyboardInteractor( + // In a read-only document, we don't expect the software keyboard + // to ever be open. Therefore, we only respond to key presses, such + // as arrow keys. + focusNode: _focusNode, + readerContext: _readerContext, + keyboardActions: widget.keyboardActions, + child: _buildGestureInteractor( + child: _buildPlatformSpecificViewportDecorations( + child: _buildDocumentLayout(), + ), + ), + ); + }), + ), + ), + ); + } + + /// Builds an [InheritedWidget] that holds a shared context for editor controls, + /// e.g., caret, handles, magnifier, toolbar. + /// + /// This context may be shared by multiple widgets within [SuperChatBubble]. It's also + /// possible that a client app has wrapped [SuperChatBubble] with its own context + /// [InheritedWidget], in which case the context is shared with widgets inside + /// of [SuperChatBubble], and widgets outside of [SuperChatBubble]. + Widget _buildGestureControlsScope({ + required Widget child, + }) { + switch (_gestureMode) { + // case DocumentGestureMode.mouse: + // TODO: create context for mouse mode (#1533) + // case DocumentGestureMode.android: + // TODO: create context for Android (#1509) + case DocumentGestureMode.iOS: + default: + return SuperReaderIosControlsScope( + key: _iosControlsContextKey, + controller: _iosControlsController, + child: child, + ); + } + } + + /// Builds any widgets that a platform wants to wrap around the editor viewport, + /// e.g., reader toolbar. + Widget _buildPlatformSpecificViewportDecorations({ + required Widget child, + }) { + switch (_gestureMode) { + case DocumentGestureMode.iOS: + return SuperReaderIosToolbarOverlayManager( + tapRegionGroupId: widget.tapRegionGroupId, + defaultToolbarBuilder: (overlayContext, mobileToolbarKey, focalPoint) => defaultIosReaderToolbarBuilder( + overlayContext, + mobileToolbarKey, + focalPoint, + widget.editor.document, + widget.editor.composer.selectionNotifier, + SuperReaderIosControlsScope.rootOf(context), + ), + child: SuperReaderIosMagnifierOverlayManager( + child: child, + ), + ); + case DocumentGestureMode.mouse: + case DocumentGestureMode.android: + return child; + } + } + + Widget _buildGestureInteractor({required Widget child}) { + // Ensure that gesture object fill entire viewport when not being + // in user specified scrollable. + final fillViewport = context.findAncestorScrollableWithVerticalScroll == null; + switch (_gestureMode) { + case DocumentGestureMode.mouse: + return ReadOnlyDocumentMouseInteractor( + focusNode: _focusNode, + readerContext: _readerContext, + contentTapHandler: _contentTapDelegate, + fillViewport: fillViewport, + showDebugPaint: widget.debugPaint.gestures, + child: child, + ); + case DocumentGestureMode.android: + return ReadOnlyAndroidDocumentTouchInteractor( + focusNode: _focusNode, + tapRegionGroupId: widget.tapRegionGroupId, + readerContext: _readerContext, + documentKey: _docLayoutKey, + getDocumentLayout: () => _readerContext.documentLayout, + selectionLinks: _selectionLinks, + contentTapHandler: _contentTapDelegate, + handleColor: widget.androidHandleColor ?? Theme.of(context).primaryColor, + popoverToolbarBuilder: widget.androidToolbarBuilder ?? (_) => const SizedBox(), + createOverlayControlsClipper: widget.createOverlayControlsClipper, + showDebugPaint: widget.debugPaint.gestures, + overlayController: widget.overlayController, + fillViewport: fillViewport, + child: child, + ); + case DocumentGestureMode.iOS: + return SuperReaderIosDocumentTouchInteractor( + focusNode: _focusNode, + readerContext: _readerContext, + documentKey: _docLayoutKey, + getDocumentLayout: () => _readerContext.documentLayout, + contentTapHandler: _contentTapDelegate, + fillViewport: fillViewport, + showDebugPaint: widget.debugPaint.gestures, + child: child, + ); + } + } + + Widget _buildDocumentLayout() { + return ContentLayers( + content: (onBuildScheduled) => SingleColumnDocumentLayout( + key: widget.documentLayoutKey, + presenter: _docLayoutPresenter!, + componentBuilders: widget.componentBuilders, + onBuildScheduled: onBuildScheduled, + showDebugPaint: widget.debugPaint.layout, + ), + underlays: [ + // Add any underlays that were provided by the client. + for (final underlayBuilder in widget.documentUnderlayBuilders) // + (context) => underlayBuilder.build(context, _readerContext), + ], + overlays: [ + // Layer that positions and sizes leader widgets at the bounds + // of the users selection so that carets, handles, toolbars, and + // other things can follow the selection. + (context) => _SelectionLeadersDocumentLayerBuilder( + links: _selectionLinks, + ).build(context, _readerContext), + // Add any overlays that were provided by the client. + for (final overlayBuilder in widget.documentOverlayBuilders) // + (context) => overlayBuilder.build(context, _readerContext), + ], + ); + } +} + +/// A [SuperReaderDocumentLayerBuilder] that builds a [SelectionLeadersDocumentLayer], which positions +/// leader widgets at the base and extent of the user's selection, so that other widgets +/// can position themselves relative to the user's selection. +class _SelectionLeadersDocumentLayerBuilder implements SuperReaderDocumentLayerBuilder { + const _SelectionLeadersDocumentLayerBuilder({ + required this.links, + // TODO(srawlins): `unused_element`, when reporting a parameter, is being + // renamed to `unused_element_parameter`. For now, ignore each; when the SDK + // constraint is >= 3.6.0, just ignore `unused_element_parameter`. + // ignore: unused_element, unused_element_parameter + this.showDebugLeaderBounds = false, + }); + + /// Collections of [LayerLink]s, which are given to leader widgets that are + /// positioned at the selection bounds, and around the full selection. + final SelectionLayerLinks links; + + /// Whether to paint colorful bounds around the leader widgets, for debugging purposes. + final bool showDebugLeaderBounds; + + @override + ContentLayerWidget build(BuildContext context, SuperReaderContext readerContext) { + return SelectionLeadersDocumentLayer( + document: readerContext.document, + selection: readerContext.composer.selectionNotifier, + links: links, + showDebugLeaderBounds: showDebugLeaderBounds, + ); + } +} diff --git a/super_editor/lib/src/super_reader/read_only_document_android_touch_interactor.dart b/super_editor/lib/src/super_reader/read_only_document_android_touch_interactor.dart index b860382a8..7283ce0bd 100644 --- a/super_editor/lib/src/super_reader/read_only_document_android_touch_interactor.dart +++ b/super_editor/lib/src/super_reader/read_only_document_android_touch_interactor.dart @@ -1,7 +1,6 @@ import 'dart:async'; import 'dart:math'; -import 'package:flutter/foundation.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; @@ -38,8 +37,7 @@ import 'package:super_editor/src/super_reader/reader_context.dart'; import 'package:super_editor/src/super_textfield/metrics.dart'; import 'package:super_text_layout/super_text_layout.dart'; -import '../core/editor.dart'; -import '../default_editor/text_tools.dart'; +import 'package:super_editor/src/default_editor/text_tools.dart'; /// Read-only document gesture interactor that's designed for Android touch input, e.g., /// drag to scroll, and handles to control selection. @@ -58,7 +56,7 @@ class ReadOnlyAndroidDocumentTouchInteractor extends StatefulWidget { required this.documentKey, required this.getDocumentLayout, required this.selectionLinks, - required this.scrollController, + this.scrollController, this.contentTapHandler, this.dragAutoScrollBoundary = const AxisOffset.symmetric(54), required this.handleColor, @@ -86,7 +84,7 @@ class ReadOnlyAndroidDocumentTouchInteractor extends StatefulWidget { /// a link when the user taps on text with a link attribution. final ContentTapDelegate? contentTapHandler; - final ScrollController scrollController; + final ScrollController? scrollController; /// The closest that the user's selection drag gesture can get to the /// document boundary before auto-scrolling. @@ -168,7 +166,7 @@ class _ReadOnlyAndroidDocumentTouchInteractorState extends State scrollPosition, + getScrollPosition: () => scrollPosition!, getViewportBox: () => viewportBox, ); @@ -312,11 +310,11 @@ class _ReadOnlyAndroidDocumentTouchInteractorState extends State _ancestorScrollPosition ?? widget.scrollController.position; + ScrollPosition? get scrollPosition => _ancestorScrollPosition ?? widget.scrollController?.position; /// Returns the `RenderBox` for the scrolling viewport. /// @@ -691,7 +689,7 @@ class _ReadOnlyAndroidDocumentTouchInteractorState extends State scrollPosition, + getScrollPosition: () => scrollPosition!, getViewportBox: () => viewportBox, ); @@ -393,7 +393,7 @@ class _SuperReaderIosDocumentTouchInteractorState extends State _ancestorScrollPosition ?? widget.scrollController.position; + ScrollPosition? get scrollPosition => _ancestorScrollPosition ?? widget.scrollController?.position; /// Returns the `RenderBox` for the scrolling viewport. /// @@ -432,7 +432,7 @@ class _SuperReaderIosDocumentTouchInteractorState extends State 1) { + if (widget.scrollController?.hasClients == true) { + if (widget.scrollController!.positions.length > 1) { // During Hot Reload, if the gesture mode was changed, // the widget might be built while the old gesture interactor // scroller is still attached to the _scrollController. diff --git a/super_editor/lib/src/super_reader/read_only_document_mouse_interactor.dart b/super_editor/lib/src/super_reader/read_only_document_mouse_interactor.dart index 861f15360..f1f1a5e4a 100644 --- a/super_editor/lib/src/super_reader/read_only_document_mouse_interactor.dart +++ b/super_editor/lib/src/super_reader/read_only_document_mouse_interactor.dart @@ -42,7 +42,7 @@ class ReadOnlyDocumentMouseInteractor extends StatefulWidget { this.focusNode, required this.readerContext, this.contentTapHandler, - required this.autoScroller, + this.autoScroller, required this.fillViewport, this.showDebugPaint = false, required this.child, @@ -58,7 +58,7 @@ class ReadOnlyDocumentMouseInteractor extends StatefulWidget { final ContentTapDelegate? contentTapHandler; /// Auto-scrolling delegate. - final AutoScrollController autoScroller; + final AutoScrollController? autoScroller; /// Whether the document gesture detector should fill the entire viewport /// even if the actual content is smaller. @@ -99,7 +99,7 @@ class _ReadOnlyDocumentMouseInteractorState extends State _buildDebugPaintInDocSpace() { final dragStartInDoc = _dragStartGlobal != null - ? _getDocOffsetFromGlobalOffset(_dragStartGlobal!) + Offset(0, widget.autoScroller.deltaWhileAutoScrolling) + ? _getDocOffsetFromGlobalOffset(_dragStartGlobal!) + + Offset(0, widget.autoScroller?.deltaWhileAutoScrolling ?? 0) : null; final dragEndInDoc = _dragEndGlobal != null ? _getDocOffsetFromGlobalOffset(_dragEndGlobal!) : null; diff --git a/super_editor/lib/super_editor.dart b/super_editor/lib/super_editor.dart index 1d16ec4ef..9742b5147 100644 --- a/super_editor/lib/super_editor.dart +++ b/super_editor/lib/super_editor.dart @@ -16,6 +16,7 @@ export 'src/core/styles.dart'; // Chat export 'src/chat/message_page_scaffold.dart'; +export 'src/chat/super_chat_bubble.dart'; // Super Editor export 'src/default_editor/ai/content_fading.dart';