Skip to content

Fade-In content for AI GPT (Resolves #2648) #2673

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 12 commits into
base: main
Choose a base branch
from

Conversation

matthew-carroll
Copy link
Contributor

@matthew-carroll matthew-carroll commented May 16, 2025

Fade-In content for AI GPT (Resolves #2648)

This PR adds a few tools to facilitate piece-by-piece fade-in of text and blocks generated by an LLM.

This PR adds an attribution called CreatedAtAttribution which can be applied to text segments and nodes.

This PR adds a FadeInStyler, which looks for CreatedAtAttributions, and then sets the opacity of the text segment, or the whole node. The FadeInStyler users a Ticker to keep marking itself as dirty until it no longer sees any content that needs to fade in.

For text, the opacity is applied by an OpacityAttribution. The FadeInStyler applies OpacityAttributions as needed, during a given frame.

Added an opacity property to all document component view models.

I added a metadata property to view models, even though I didn't really want to. I wasn't sure where else to stick the "createdAt" property. I could have declared an explicit "createdAt" property on each Node. Maybe that's a better move. I'm not sure. Using an explicit property requires that we define it for every node and include it in the copy mechanism. However, creating a metadata property now creates confusion between view model metadata and document node metadata. I'm open to thoughts on that.

Fixes

We seemed to be missing some calls to copy() on AttributedText within some view models. This resulted in one styler mutating the text appearance in another styler.

Unrelated New Things

This PR adds a few new edit requests to insert content at the end of a document. They're a matter of a convenience. LLMs will want to insert at the end of a document, instead of using the caret, because the user might drag a selection while the content is generating, which would disrupt any insertions based on caret position.

Breaking Changes

This PR also changes SuperReader to take an Editor instead of a Document and a SelectionNotifier. This is because LLMs will need to continuously feed content into the document. Rather than expect developers to use two different modes of input between SuperEditor and SuperReader, we'll use an Editor inside of both, but simply prevent user interactions from altering the document within a SuperReader.

Example

The PR code can be used as follows to achieve the fade-in effect.

Setup the SuperReader with a FadeInStyler.

class MyState extends State<MyWidget> {
  late final MutableDocument _document;
  late final MutableDocumentComposer _composer;
  late final Editor _editor;
  late final FadeInStyler _fadeInStylePhase;

  @override
  void initState() {
    super.initState();

    _document = MutableDocument.empty();
    _composer = MutableDocumentComposer(
      initialSelection: DocumentSelection.collapsed(
        position: DocumentPosition(
          nodeId: _document.first.id,
          nodePosition: TextNodePosition(offset: 0),
        ),
      ),
    );
    _editor = createDefaultDocumentEditor(document: _document, composer: _composer);
    _fadeInStylePhase = FadeInStyler(this);
  }

  @override
  void dispose() {
    _fadeInStylePhase.dispose();
    _composer.dispose();
    _editor.dispose();
    _document.dispose();

    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return SuperReader(
      editor: _editor,
      customStylePhases: [
        _fadeInStylePhase,
      ],
    );
  }
}

Insert content, as desired, and make sure to provide a createdAt value.

// Insert plain text.
_editor.execute([
  InsertPlainTextAtEndOfDocumentRequest(
    "...this is a snippet from the LLM...",
    createdAt: DateTime.now(),
  ),
]);

// Insert styled text.
_editor.execute([
  InsertStyledTextAtEndOfDocumentRequest(
    AttributedText(
      "...this is a bold snippet...",
      AttributedSpans(attributions: [
        const SpanMarker(
          attribution: boldAttribution,
          offset: 0,
          markerType: SpanMarkerType.start,
        ),
        const SpanMarker(
          attribution: boldAttribution,
          offset: 27,
          markerType: SpanMarkerType.end,
        ),
      ]),
    ),
    createdAt: DateTime.now(),
  ),
]);

// Insert a block node.
_editor.execute([
  InsertNodeAtEndOfDocumentRequest(
    ImageNode( 
      id: Editor.createNodeId(),
      imageUrl: "https://somewhere.com/image.png",
      metadata: {
        NodeMetadata.createdAt: DateTime.now(),
      },
    ),
  ),
]);

@angelosilvestre
Copy link
Collaborator

I think that the view model metadata might create too much confusion. At first look it might appear that it will be always the same as the node's metadata. I think that using a property might make it much easier to understand.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[SuperEditor][AI] - Add support for fading in text inserted in document
2 participants