If you want to learn more about the specs, all tickets are available by following the hashtag links.
- Moved
DocumentController
inEditorController
#222 - Fixed missing placeholder. When generating the doc tree we were no longer reading from the document param. It was bypassed by reading straight form the state store. This param provided the placeholder doc in case the user doc was empty. We had to separate the placeholder document generation logic to a dedicated service
PlaceholderService
. - Renamed
DocumentM
toDeltaDocM
. This change was made to prevent model collisions with apps that useDocumentM
internally. - Fixed issue with
EditorController
getters. They were returning the initial stale value instead of the latest value. - Added new param
emitEvent
forcontroller.update()
. It defaults to true which means that by default any change (update) made to the document will fire the callbacksonReplaceText()
andonReplaceTextCompleted()
. In certain scenarios when the editor is used in a combination with the state store we might want to disable the emission of a new event. If the state store update the document then we want to prevent the callbacks from being fired. That's when we useemitEvent: false
. If the editor has been changed by the user via typing then we want to push this change to the state store. Then we prefer to emit events (call callbacks) as per default behaviour. - Added new public method
getHeadingsByType()
on the controller. Useful for listing the headings of the document in a separate index component. - More documentation improvements. Added an overview of the "Hierarchy of Code Flow". As a new dev in the codebase you can get quite confused when trying to understand why you can find 3
compose()
and 3replace()
methods in the codebase. There's a very solid explanation behind this architecture. - Fixed a bug that kept adding a new whitespace each time the document was updated using
update()
. #255 It's a silly combination of factors. But in summary:update()
callsclear()
and compose().clear()
produces a document:[{"insert":"\n"}]
compose()
then comes and adds:[{"insert":"A\n"}]
- \n + \n = \n\n
- The result is:
[{"insert":"A\n\n"}]
- Rinse and repeat
- Renamed
replaceText()
back toreplace()
. Technically speaking this is the correct name. It was change some months ago due to poor understanding on our side of the Quill API. We wanted a name that made it simpler to understand whatupdate()
,replace()
,compose()
do. These methods were very poorly documented. However the naming is correct. They are short names mostly because these are public API methods. Now why they are duplicated is explained in the updated documentation. - Improved docs. #204
- Toolbar - Disable Styling Options For Code Blocks, Cleanup #230 - Some very bad design choices have been made in the state store architecture and in the callstack overall. Especially calling runBuild twice because the delayed btn enabled state bug was not properly fixed at the root.
- Upgraded to Latest Flutter and to Dart 3. Fixed several migration issues. Updated to latest libs.
Overlay - Custom overlay #209
- Removed the _onBuildComplete() method which was causing a lot of refreshes on the editor, and affecting the performance.
- Added docs for links.md
- Added overlay service in order to insert the link menu and for future menus to be added.
- Removed stack from the main method inside the editor, in order to place widgets on the screen inside the editor, we are now using overlay service.
- Moved some methods from manipulating links in controller.
Actions - Extract document manipulation logic from actions #208
- Made the copy to clipboard button from the link menu to work properly.
- Extracted code that manipulates the document from some of the actions and placed it into services.
- Disable adding styling in code blocks 185
- Added
DisabledButtonsState
in order to toggle the buttons color and disable applying different attrs to selection based on different selection attrs. - Disabled styling and most of the buttons in the toolbar when selection is code block or inline code.
- Added
enableSelectionCodeButtons()
anddisableSelectionCodeButtons()
inSelectionService
andEditorController
in order to toggle enable/disable buttons when selection is code.
- Added
Blocks - Editable links #10
- Added link menu, now when tapping on a link, a menu opens, displaying the url of the link, and 3 buttons (edit link, remove link from text/url, and copy link to clipboard)
- Improved Readme.md, added screen captures of the new features
- Added custom fonts (RobotoMono) for code blocks and inline code
- Replaced code block icon from toolbar with a proper one, before it was using the same icon as inline code, which was not a proper UX.
- Selection - Can't select the first whitespace after any text #176
- Highlights - Controller method to remove highlights by id #178
- Demos - Demo page for multiple editors in scrollable parent #161
Inputs - Keyboard Shortcuts #163
- Added hotkeys for toolbar actions (e.g.: CTRL + B makes the selected text bold).
- Added ordered list on key combination (1. + space) at the beginning of a line and (- + space) creates bullet list.
- Fixed the wrong ordering number bug in ordered lists and code block. #158
- Added nested bullets with TAB key. #11
Improved file/folders structure #174
- Sys - Improve file/folders structure
- Add demo images in readme.
- Grouped demo pages navigation menu in categories.
- Grouped demo pages into modules.
- Removed
defaultToggleStyleButtonBuilder
fromToggleStyleButton
and fromToggleCheckListButton
. - Created
DocumentEditingService
, MovedformatText
formatTextStyle
toTextStylesService
. - Renamed
SelectionActionsService
toSelectionHandlesService
. - Moved controller callbacks type definitions to standalone file.
- Removed local cached reference of the document from the controller. We are now reading the document only from the state store.
- Moved
keepStyleOnNewLine
to the state store inEditorConfigM
. There was no need to separate this property from the main config. - Moved toggledStyle to state store
- The controller was getting too big with too many methods. Maintenance was becoming difficult due to unclear code boundaries. We moved most of the methods to dedicated services.
- Moved selection to the state store
- Remove rectangles param from the
onSelectionChanged
. - When it was first introduced we though we are going to use the param to get the rectangles and display the selection menu position. However in the meantime we developed the
onBuildComplete
callback. Since the rectangles data is stale we decided to remove it rather than refactor the whole code flow. - (CANCELLED) Slice out refs from state. It simplifies the mental model needed to understand the state store. Abandoned, creates too much boilerplate in the buttons.
- Move controller callbacks into the config model.
- Remove useless setter getters from the state store. There's no point in having them if all they do is read and write.
- Move the remaining params as well:
selection
,highlights
,markerTypes
, - We are no longer constrained to call methods defined only in the controller.
- Separate marker types and highlights as methods in the demo pages
- Update documentation to reflect the editor config params change. Updated the state store documentation.
- Remove EditorController.basic. It is no longer needed.
- Removed
editorWidget
from state no longer required - Sliced
CoordinatesService
fromLinesAndBlocsService
. - One is concerned with retrieving coordinates of elements in the document. The other is concerned with rendering the document lines and blocs.
- Created
StylesService
&DocumentService
- Rename
editorController
to controller - Rename
editorWidgetState
towidget
- Move markers and highlights methods from controller to services
- Merge
CursorService
andCaretService
- Move
toolbarButtonToggler
andcopiedImageUrl
to the state store - Review if all state branches are relevant
- Removed the additional layer from
config.config
even if it brakes the state store pattern. It's simply too much nesting repetition in the code base for no benefit. - Replace all
refs.controller.selection
with the state/service versionselection.selection
- Rename
TextValueService
toPreBuildService
to better reflect what it really does. - Rename
value
inTextEditingValue
value toplainText
. - Rename
refreshEditor
torunBuild
. - Rename /documents module to /document.
- Rename /blocks to /doc-tree.
- Merge
DocumentRenderService
intoDocTreeService
. - Rename
setState
to voidcacheStateStore
to avoid confusion with the widgetsetState
. - Rename
userUpdateTextEditingValue
toremoveSpecialCharsAndUpdateDocTextAndStyle
. - Merge
EditorTextService
intoDocumentService
(defragmentation). - Rename all listeners to short notation:
runBuild$L
. - Replace all internal controller calls
refs.controller
with service calls. - Rename
TextSelectionService
toSelectionService
- Improved management for embed builders list
- Wrapped state store access in the services. Improves code readability (more awareness)
- Replaced singleton services with services that have the state initialised. This means each instance of the editor gets a new set of services with the correct state store initialised. More details in the state-store.md document. This technique is useful because now we no longer need prop drilling to pass the state.
- Simplified the controller code further. It is currently a nice index of methods form services. No more wrapping.
- Moved document logic from the toolbar buttons to services. Now more methods are available in the controller for manipulating the document programmatically.
- Added
EmbedsService
, has methods for inserting embeds programatically. - Wrapped DeltaUtils in a class
- In
InputConnectionService
RenameupdateEditingValue
todiffPlainTextAndUpdateDocumentModel
- Added
InputState
to cache temporarily theplainText
value during - Added clarifications what is the difference between
InputConnectionService
and theKeyboardConnectionService
. - Renamed
PreBuildService
toGuiService
to better indicate it's role. - Merged
runBuild
andrunBuildIfMounted
- Merged
EditorKeyboardListener
intoVisualEditor
. It had nothing useful to add in the widget tree. - Moved focus related update handler from
EditorService
intoGuiService
. - Renamed
EditorService
toRunBuildService
- Rename
_emitPressedKeyHandler
,state.pressedKeys.emitPressedKeys()
. - Remove
_pressedKeysChanged
. It is useless since we use the state store.metaOrControlPressed
can be read directly sync from the state store. - Cleanup in
TextGesturesService
- Expose selection service methods in controller.
- Remove rectangles param from
OnSelectionChangedCallback
. They are outdated because the callback is invoked before the build cycle can compute the new rectangles. - Move all cached vars from services to the state store (we no longer use singletons for services).
- Move
TextGestures
service and widget to /inputs - Convert stateful utils to services
- There are 2
StylesService
. One of them was renamed toStylesCfgService
. - Renamed params in delta:
other
innew
,this
incurr
- Improved doc comments in
HistoryM
. It appears we have support for coop but it is not exposed/enabled publicly. - Refactored
DocumentM
. Extracted most methods in a dedicated serviceDocumentNodesService
. Improved doc comments. - One of the challenges of understanding how
DocumentM
works was recognising if methods are pure or impure. I've updated the comments to reflect if methods are pure or impure (purity indicators). Having increased awareness of the purity of code improves the developers ability to predict what will happen. Thus makes the debugging process far easier. - Rename
DocumentService
toEditorService
. RenameDocumentNodesService
toDocumentService
. This reflects the reality of what happens in codebase.EditorService
mutates doc, updates all systems and triggers build.DocumentService
only performs the mutations on the doc. - Refactored
DeltaM
andOperationM
. Extracted most methods in a dedicated serviceDeltaService
. Improved doc comments. - Remove basic constructor
- Moved all the code from
HistoryM
intoHistoryService
, since this is an internal model never exposed in the public. - We need to keep changes for multiple docs, we can't have a central one. _history stays in document model.
- Move changes$ from
DocumentM
in theEditorController
. Changes are not operated and broadcasted from an idle document without the user being aware via the GUI. Therefore it does not make sense to keep the changes stream in the document model. We want to have the document model pure data. - Move
HistoryService
andDocumentService
in the controller. This means we can start migrating the document models to pure data. Also it means all services are now state store aware. TheHistoryService
andDocumentService
were document aware only. We did not want to pass the state store to the document to avoid complicated architecture. Now that we are migrating all data from the models means we no longer risk exposing the state store in the public. More explanations about our architecture choices in editor.md . - Convert
DocumentService
toDocumentController
,DeltaService
toDeltaUtils
. This enables us to keep the models as pure data. It also enables advanced client developers to edit documents that are not cached in theEditorController
. - Moved _rules form
DocumentM
toDocumentController
. MovedcustomRules
tostateStore
(one list per editor). - Created
DocumentUtils
to segregate some simple utils that are used also when initialising the Document. - Restored the old state one instance state store. No more shallow cloning. Shallow cloning the
EditorState
when passing it to theToolbar
buttons meant that we lost the reference to theDocumentController
. Fixed theCusrsorController.dispose()
issue the correct way by caching the prev instance in the state store. Detailed explanation in state-store.md. Now the state store is back again a simple object, easy to understand. - Convert
HistoryService
toHistoryController
. We need a controller independent of the state store to be able to process changes also when the document model manipulated from outside. - Move
HistoryController
inDocumentController
. Edits running outside of the editor will update history as well. - Merge all caching code of the editor in one method. No need to have so many small methods. Improved sorting of methods in
main.dart
. - Rename
SelectionActionsController
toSelectionHandlesController
. - Convert
DeltaService
toDeltaUtils
. - Renamed node
length
intocharsNum
. - Renamed node
adjust
intomergeSimilarNodes
. - Created
EditableTextPaintService
.EditableTextLineBoxRenderer
has many overrides concerned with computing the layout dimensions. Therefore the painting logic for selection/highlight boxes is better separated here. Separating the layout dimensions logic and painting logic helps improves readability and maintainability. - Move code that is closely related to other modules to the respective modules
- Renamed
EditorRendererInner
toEditorTextAreaRenderer
following the conventions for editable text line. - Replaced header style buttons with a dropdown #15
Custom Embeds #157
- Demos - Demo page for adding new items in document.
- Headings - Added text selection for headers #195
- Embeds - Separated embed builders based on the type of embed.
- Embeds - created
defaultEmbedBuilders
to supply standard embeds builders for images and videos, makes it easier to override standard embeds. - Embeds - Added custom embeds. #157
- Created
EmbedBuilderController
to handle selection of the embed builder. - Removed
BlockEmbedM
and all block embed implementations. - All embeddable objects now extend
EmbedM
unifying embed insertion into the document. - Created standard embeddable objects for images (
ImageEmbedM
) and videos (VideoEmbedM
).
- Created
- Demos - Created
Custom Embeds Page
.- Created
custom-embeds.json
. #157
- Created
- Markers - Added new method in the editor controller
toggleMarkersByTypes()
. It toggles just certain types of markers.- Added new method in the editor controller
getMarkersVisibilityByTypes()
. It queries if certain types of markers are disabled #120
- Added new method in the editor controller
- Demos - Delta Sandbox, Adaptive layout for maximum screen area on mobiles. #128
- Demos - Aligned the navigation to the left, increased padding for better UI. #162
- Demos - Demo page for adding new items in document.#195
- Headings - Added text selection for headers #197
- Demos - Demo page for limited length headings. #199
Headings List #140
- Created a new demo page for showcasing the custom styles. Demo custom styles can pe altered by modifying the parameters found in 'demo-custom-styles.const.dart'. #95
- Exposed public method to access the headings after rendering headings from the document. The client code can read the text and the position (rectangles) of every heading. Similar to markers and highlights, we are storing this information in the internal state store after rendering. #135
- Improved the null safety for the operation attributes. The getter was guaranteeing that the array will contain at least one AttributeM. Which is not true.
- Added demo page to demonstrate how to render an index panel using the headings.
- Implemented a scroll to feature. Tapping the headings will scroll ot the corresponding text.
- Blocks - Indenting does not work properly. Fixed issue with indenting not updating the line padding. This was a small mistake from the refactoring process because we misunderstood the
updateRenderObject()
method on theEditableTextLineWidgetRenderer
#88 - Added
onReplaceTextComplete()
a callback for detecting when the document plain text has changed but timed to be triggered after the build. Such that we can extract the latest rectangles as well. #155 - Markers - Fixed: Extracting markers on non-scrollable editor yields global position instead of document position for the text line. #160
Markers #69
- Added custom markers. Multiple marker types can be defined. The client app can define callbacks for hovering the markers.
- Split the AttributeM static properties in methods in multiple files
- Refactored the
DropdownButton
in Toolbar to make it more generic - Added demo page for the markers and highlights features
- Various small code cleanups
- Added new method in the editor controller
toggleMarkers()
. It toggles all markers at once. #111 - Markers - Initial visibility config parameter #116
- Markers - Marker attachments, Position widgets in sync with the markers found in the text #117
- Retrieved the pixel coordinates of the markers rectangles that are drawn in
EditableTextLine
. Also retrieved the global position of each line of text. This information is essential for rendering attached widgets. - Added unique ids to markers to facilitate precise targeting when deleting or attaching widgets.
- Added a dedicated
MarkersState
where the rendered markers and their pixel coordinates are stored. - Fixed an issue with the conversion from json data to
NodeM
of markers attributes. When the markers were first implemented, by mistake, we stored the json data instead of the convertedNodeM
models when initialising the document andStyleM
. Because of this error all markers were passed as json data, therefore some of the code is harder to read. This was fixed and now we are receivingMarkerM
instead of json data. - Exposed 2 callbacks:
onBuildComplete()
,onScrooll()
. These callbacks are essential for synchronising the attached widgets positions to the markers themselves. - Added new demo page to demonstrate how to attach arbitrary widgets to the markers.
- Retrieved the pixel coordinates of the markers rectangles that are drawn in
- Markers - Paragraphs with markers don't get rendered if the editor is not scrollable (bugfix) #118
scrollController
is no longer mandatory. It is possible for the editor to have the scroll disabled, so no scroll controller needed.
- Highlights - Add highlights from the controller. Improve the highlights page #145
- Highlights - Restore highlights hovering #134
- Added ids to highlights. If we are using a pure functional approach in our code we can no longer rely on references to search for highlights in the state store. We need ids to be able to track which highlights are hovered.
- Markers - Render attachments for the selection (for quick menu) #135
- Upgraded the highlights hovering service to use pixel coordinates instead of text position to detect the hovering.
- Added a markers hovering service.
- Added demo page to demonstrate how to attach menus or random widgets when tapping on highlights and markers or when changing the selection.
- Exported the rectangles data for the selection. Now we have two ways of attaching widgets to the text selection. Read Selection documentation for more details.
- Documents - Delta Sandbox does not trigger update of the json input when changing styles. Fixed by using the newly added
onBuildComplete()
callback. The sandbox json preview now updates on any change including style changes and adding markers/highlights. #93 - Added delete markers buttons. They are triggered by the tapping on a marker. All markers with the same id will be deleted. Markers can be hidden or deleted.
- Added scroll behavior to the majority of toolbars and created a new page to differentiate between a toolbar with a horizontal scroll and a wrapping one. #129
- Fixed issue with
EditorController
being reinitialised on setState(). Usually setState() should not be used. However there are scenarios when the host code might request the entire editor to update (for example when changing styles). Prior to this fix the editor was crashing completely. When a client app triggerssetState()
it also rebuilds theEditorController
if the first controller instance is not cached by the developer. When a new controller is created a new internal state store object is created as well. In this state store we also keep references towards several important classes: ScrollController, FocusNode, EditorRenderer. The issue came from the fact that these references were not properly renewed. In many places of the code base we have code snippets that depend on the latest refs being available. The fix was to patch the newly created state store with the proper refs. In the old Quill Repo this was present but due to the lack of documentation this code got discarded. Now this fix restores this functionality but with the necessary changes to make it work within the refactored codebase of Visual editor. #77 - Added delta json preview page. In this page you can see the json output as you type in the editor. Very helpful for learning quickly how the delta format works. #66
- Fixes related to the architecture cleanup refactoring. During the refactoring, despite our best efforts, some bugs showed up:
- Tapping the toolbar buttons does not update their state (Post Refactoring) #84
- Dual editors - Text is inserted in the wrong editor instance (Post Refactoring) - Added unique focusNodes. #85
- After setState() in parent the selection no longer works + selecting text before setState() yields missing doc after setState() (Post Refactoring) #86
- After controller reset, indenting, the bullet and number lists don't work (fails) (Post Refactor) #87
- Improved the toolbar documentation. #86
- Added horizontal mouse scroll for toolbar.
- Fixed the scroll controller which overlays over the toolbar buttons.
- Fixed the toolbar stretching and irregular distance between buttons.
Architecture Refactoring #1
- Cleaning up editor.dart
- Improved docs #2
- Break editor.dart in multiple files
- Split source code in modules
- Updated linting options (removed some restrictions to improve readability)
- Moved all legacy files in /core to prepare for new modules
- Renamed to Visual Editor
- Bumped to Flutter 3.0.0 #25
- Removed the mixins from RawEditor
- Separated code into services.
- Refactored many methods to avoid reading/manipulating values straight from the RawEditor context. This reduces the coupling, enabling us to isolate code into services that can be unit tested.
- Merged Editor and RawEditor.
- Replaced ChangeNotifiers with state streams.
- Added migration guide #60
- Updated demo pages #63
- Migrated state architecture from singleton to one state per editor controller instance. #61
- Exported the Toolbar buttons so that custom toolbars can be build from scratch #65
- Delta documents sandbox page, preview the json (development aid)
- Remove tuple dependency #45
- Fix the placeholder is not displayed bug #70
- The caret is no longer displayed in readonly mode. The
showCursor
configuration option forVisualEditor
was removed. It makes no sense to have the cared showing up in readonly mode. Or the caret missing in editable mode. #73 - Added first automatic test #3
- Renamed class that was colliding with the `Text class from Flutter #33
- Custom highlights. This feature was initially developed for Quill. However we had major issues with the existing architecture. Therefore we decided to fork and run a major refactoring.
- Rich text editor forked from Flutter Quill and based on the Delta format.