jjagg / boustro Goto Github PK
View Code? Open in Web Editor NEWA rich text editor for Flutter.
Home Page: https://jjagg.dev/boustro
License: MIT License
A rich text editor for Flutter.
Home Page: https://jjagg.dev/boustro
License: MIT License
This tracks the components we need to implement to support markdown (CommonMark, except for HTML):
LineParagraphModifier.modify
) #12Additionally it'd be nice to support GitHub Flavored Markdown:
And we should have an easy system for auto-applying formatting as well (autodetect links, tags, mentions, etc.) -> #16. We might be able to use this autodetection system for syntax highlighting in code blocks if there's no comprehensive lib for it.
Image widget should be customizable, for example so it can support caching.
Desperately need to write some tests for this :)
Highest priority is DocumentController
.
Need to finish Quill delta serialization.
I'd also like a custom serialization format that maps closely to a boustro document. Should at least support a json format. Binary would be nice too, but low priority.
Should also support markdown conversion with boustro_starter components.
Blocked by flutter/flutter#30688.
In depth docs in docs/ should be hosted.
Very low priority for now, of course ๐ฌ
It'd be nice to have a bidirectional iterator that can iterate paragraphs of both a BoustroDocument
and a DocumentController
. A copy of the iterator can then be passed when building a paragraph.
I think that'd be the cleanest way to add numbered list support. This system would mean every numbered list paragraph would have to count all items before it (O(N^2)
). Though that's fine considering the low number of lines we're dealing with.
Alternatively we could have some state object that we pass through to every line for them to track state during the build step. This is less flexible, but more efficient.
Either way we really don't want to bake the line counting into boustro's core systems. It should be handled by the component itself.
Attributes do not currently support multiple gestures (e.g. tap + long press).
One way to get support in would be to wrap segment TextSpans
in a GestureRecognizer
with a WidgetSpan
, but there's a blocking issue for selectable text.
The specific case of tap + long press can be hacked with a custom GestureRecognizer
.
Support WidgetSpan
in SelectableText
: flutter/flutter#38474
Syntax highlighting should use TextMate language grammar to easily support a bunch of languages. Should port or bind https://github.com/microsoft/vscode-textmate to Dart (separate package).
I ported the twitter-text regular expression for the autoformatter (#16). We could add the conformance tests to make sure there no bugs in the port. It's probably valuable to split the port into a separate library and put it in another repo.
We need a system or at least guidelines for theming custom components. I think there's broadly 2 paths we can take.
I'm leaning strongly towards 2 because it prevents deeply nested widgets when users theme a lot of different components and it lets us define an interface for themeable properties so we can have them be lerpable.
Maybe something like the following (example for max image height):
class BoustroThemeData {
// ...
final BuiltMap<String, Lerpable<dynamic>> customProperties;
}
abstract class Lerpable<T> {
// used for nullable lerping
T get defaultValue;
T lerp(T other, double t);
}
class LerpableDouble implements Lerpable<double> {
LerpableDouble(this.value);
final double value;
@override
dynamic get defaultValue => 0;
@override
dynamic lerp(dynamic other, double t);
}
extension ImageEmbedTheme on BoustroThemeData {
double get maxHeight => customProperties['embed_image_maxHeight']?.value as double;
}
extension ImageEmbedThemeBuilder on BoustroThemeDataBuilder {
set maxHeight(double value) {
customProperties['embed_image_maxHeight'] = LerpableDouble(value);
}
}
Something else to consider is that currently it's not possible to depend on a theme or other scoped widgets from TextAttribute
because it's just a value class. I would prefer having them support theming. This does complicate equality of attributes, but maybe type or identity equality works for this case.
There's some English text hardcoded in the starter components. Ideally these would be localizable.
It might be nice to have a system to determine the order in which to apply line modifiers or text attributes, and have some components disallow the presence of others.
This can be done with some checks in user code and I've only come across a few use cases in boustro_starter
, so priority for this is low.
DocumentController
creates the SpannedTextEditingControllers
and they in turn apply TextAttribute
to build the text spans. SpannedTextEditingController
can't access the BuildContext
when building spans to pass it to TextAttribute
, so right now, instead, DocumentController
takes the theme in its constructor and passes it to each text editing controller it creates through their constructors. Because of this users won't be able to just put an AttributeTheme
in the widget tree.
In the example project:
Lines 132 to 135 in 33b14d8
Where it's a lot more intuitive to and in line with Flutter's theme to put it in the widget tree. See the other themes in the example project:
Lines 153 to 166 in 33b14d8
I don't think we can improve this without a change in Flutter, so I opened an issue in the Flutter repo: flutter/flutter#72343.
Note that this is only an issue for the BoustroEditor
and not for the readonly BoustroView
which calls buildTextSpan
itself and so can pass the AttributeTheme
from its context.
Related to #7, but we'll want a separate system to customize gesture handling for embeds. Perhaps some InheritedWidget that holds the GestureDetector
information? It's overkill for styles, but there won't be as many embeds as overall components and with good default gesture handling behavior users won't have to customize the gesture handling often.
We need a system to automatically apply formatting to patterns, probably based on regex matches.
The link toolbar item from boustro_starter
does not change based on whether or not the user can actually apply a link (i.e. text is selected). That can cause confusion. It should be greyed out when there is no selection.
Need some a builder for BoustroDocument and SpannedText so we can have some nice DSL to construct them. Tentative API:
class SpanTemplate {
TextAttribute attribute;
InsertBehavior startBehavior;
InsertBehavior endBehavior;
}
class DocumentBuilder {
DocumentBuilder([Iterable<BoustroParagraph> initialParagraphs]);
void line(void Function(SpannedTextBuilder) build, Iterable<LineModifier> modifiers);
void embed(BoustroParagraphEmbed embed);
}
class SpannedTextBuilder {
SpannedTextBuilder({SpannedText initial, bool continueInitialSpans});
void start(SpanTemplate style);
void end(SpanTemplate style);
void text(String text)
void textf(String text, SpanTemplate style);
void lineStyle(TextAttribute);
}
Example in root should be showcase with multiple pages (let's use Navigator 2.0); some ideas:
We should add short single-file examples to each package too and put a comment at the top that links to the showcase.
Would be nice to have, especially for non-mobile platforms.
Fairly straightforward to implement, but a bit of a chore.
Right now we just use UTF-16 indexing for strings. That sucks because it allows invalid state like spans starting in the middle of a grapheme cluster.
We should use characters to index with grapheme clusters. This will guarantee a valid state where formatting is applied in units of grapheme clusters.
We can keep this representation when serializing because it's what makes the most sense for the data we're modelling. I initially planned to convert to UTF-8 because it's the standard encoding for web, but grapheme clusters fit the data better and there's great support for it in most programming languages nowadays.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.