Ver código fonte

Initial import of documentation

Anatoly Pulyaevskiy 6 anos atrás
pai
commit
1df4aebc30
5 arquivos alterados com 437 adições e 0 exclusões
  1. 126
    0
      doc/attributes.md
  2. 134
    0
      doc/data_and_document.md
  3. 17
    0
      doc/faq.md
  4. 89
    0
      doc/heuristics.md
  5. 71
    0
      doc/quick_start.md

+ 126
- 0
doc/attributes.md Ver arquivo

@@ -0,0 +1,126 @@
1
+## Style Attributes
2
+
3
+Style attributes in Zefyr documents are simple key-value pairs, where
4
+keys identify the attribute and value describes the style applied, for
5
+instance, `{ "heading": 1 }` defines heading style for a line of
6
+text with value `1` (equivalent of `h1` in HTML).
7
+
8
+It is important to note
9
+that one attribute can describe multiple different styles depending
10
+on the current value. E.g. attribute with key "heading" can be set to values
11
+`1`, `2` or `3`, equivalents of `h1`, `h2` and `h3` in HTML. This prevents
12
+a line of text from being formatted as `h1` and `h2` heading at the same time,
13
+which is intentional.
14
+
15
+Additionally, each attribute gets assigned one of two scopes. An
16
+attribute can be either *inline-scoped* or *line-scoped*, but not both.
17
+A good example of an inline-scoped attribute is "bold" attribute. Bold
18
+style can be applied to any character within a line, but not the the
19
+line itself. Similarly "heading" style is line-scoped and has effect
20
+only on the line as a whole.
21
+
22
+Below table summarizes information about all currently supported
23
+attributes in Zefyr:
24
+
25
+| Name    | Key       | Scope    | Type     | Valid values                           |
26
+|---------|-----------|----------|----------|----------------------------------------|
27
+| Bold    | `b`       | `inline` | `bool`   | `true`                                 |
28
+| Italic  | `i`       | `inline` | `bool`   | `true`                                 |
29
+| Link    | `a`       | `inline` | `String` | Non-empty string                       |
30
+| Heading | `heading` | `line`   | `int`    | `1`, `2` and `3`                       |
31
+| Block   | `block`   | `line`   | `String` | `"ul"`, `"ol"`, `"code"` and `"quote"` |
32
+
33
+Removing a specific style is as simple as setting corresponding
34
+attribute to `null`.
35
+
36
+Here is an example of applying some styles to a document:
37
+
38
+```dart
39
+import 'package:zefyr/zefyr.dart';
40
+
41
+void makeItPretty(ZefyrDocument document) {
42
+  /// All attributes can be accessed through [ZefyrAttribute] class.
43
+
44
+  // Format 5 characters starting at index 0 as bold.
45
+  document.format(0, 5, ZefyrAttribute.bold);
46
+
47
+  // Similarly for italic.
48
+  document.format(0, 5, ZefyrAttribute.italic);
49
+
50
+  // Format the first line as a heading (level 1).
51
+  // Note that there is no need to specify character range of the whole
52
+  // line. Simply set index position to anywhere within the line and
53
+  // length to 0.
54
+  document.format(0, 0, ZefyrAttribute.heading.level1);
55
+
56
+  // Add a link:
57
+  document.format(10, 15, ZefyrAttribute.link.fromString('https://github.com'));
58
+
59
+  // Format a line as code block. Similarly to heading styles there is no need
60
+  // to specify the whole character range of the line. In following example:
61
+  // whichever line is at character index 23 in the document will get
62
+  // formatted as code block.
63
+  document.format(23, 0, ZefyrAttribute.block.code);
64
+
65
+  // Remove heading style from the first line. All attributes
66
+  // have `unset` property which can be used the same way.
67
+  document.format(0, 0, ZefyrAttribute.heading.unset);
68
+}
69
+```
70
+
71
+### How attributes are stored in Deltas
72
+
73
+As mentioned previously a document delta consists of a sequence of `insert`
74
+operations. Attributes (if any) are stored as metadata on each of the
75
+operations.
76
+
77
+One important detail here is how line and inline-scoped attributes are
78
+handled. Since Deltas are essentially a flat data structure there is
79
+nothing in the format itself to represent a line of text, which is
80
+required to allow storing line-scoped style attributes.
81
+
82
+To solve this issue Zefyr (similarly to Quill.js) reserves the
83
+**newline** character (aka `\n` and `0x0A`) as storage for line-scoped
84
+styles.
85
+
86
+Zefyr's document model is designed to enforce this rule and
87
+prevents malformed changes from being composed into a document. For
88
+instance, an attempt to apply "bold" style to a newline character
89
+will have no effect.
90
+
91
+Below is an example of Zefyr document's Delta with two lines of text.
92
+The first line is formatted as an `h1` heading and on the second line
93
+there is bold-styled word "Flutter":
94
+
95
+```dart
96
+var delta = new Delta();
97
+delta
98
+  ..insert('Zefyr Editor')
99
+  ..insert('\n', attributes: {'heading': 1})
100
+  ..insert('A rich text editor for ');
101
+  ..insert('Flutter', attributes: {'b': true});
102
+  ..insert('\n');
103
+```
104
+
105
+Note that there is no block-level scope for style attributes. Again,
106
+given flat structure of Deltas there is nothing that can represent a
107
+block of lines which share the same style, e.g. bullet list. And we
108
+already reserved newline character for line styles.
109
+
110
+As it turns out, this is not a big issue and it is possible to achieve
111
+a friendly user experience without this extra level in a document model.
112
+
113
+The `block` attribute in Zefyr is line-scoped. To change a group of
114
+lines from "bullet list" to "number list" we need to update block
115
+style on each of the lines individually. Zefyr editor abstracts away
116
+such details with help of [heuristic rules][heuristics].
117
+
118
+### Next up
119
+
120
+* [Heuristics][heuristics]
121
+
122
+[heuristics]: /doc/heuristics.md
123
+
124
+### Previous
125
+
126
+* [Data Format and Document Model](/doc/data_and_document.md)

+ 134
- 0
doc/data_and_document.md Ver arquivo

@@ -0,0 +1,134 @@
1
+## Data Format and Document Model
2
+
3
+Zefyr documents are based on Quill.js [Delta][] format. Deltas are
4
+simple and expressive format of describing rich text data, and is also
5
+suitable for [Operational transformations][ot]. The format is
6
+essentially JSON, and is human readable.
7
+
8
+> Official implementation of Delta format is written in
9
+> [JavaScript][github-delta] but it was ported to Dart and is available
10
+> on [Pub][pub-delta]. Examples in this document use Dart syntax.
11
+
12
+[Delta]: https://quilljs.com/docs/delta/
13
+[ot]: https://en.wikipedia.org/wiki/Operational_transformation
14
+[github-delta]: https://github.com/quilljs/delta
15
+[pub-delta]: https://pub.dartlang.com/packages/quill_delta
16
+
17
+### Deltas quick start
18
+
19
+All Deltas consist of three operations: `insert`, `delete` and `retain`. The
20
+example below describes the string "Karl the Fog" where "Karl" is bolded
21
+and "Fog" is italic:
22
+
23
+```dart
24
+var delta = new Delta();
25
+delta
26
+  ..insert('Karl', {'bold': true})
27
+  ..insert(' the ')
28
+  ..insert('Fog', {'italic': true});
29
+print(json.encode(delta));
30
+// Prints:
31
+// [
32
+//   {"insert":"Karl","attributes":{"bold":true}},
33
+//   {"insert":" the "},
34
+//   {"insert":"Fog","attributes":{"italic":true}}
35
+// ]
36
+```
37
+
38
+Above delta is usually also called "document delta" because it consists
39
+of only `insert` operations.
40
+
41
+Below example describes a *change* where "Fog" gets also styled as bold:
42
+
43
+```dart
44
+var delta = new Delta();
45
+delta..retain(9)..retain(3, {'bold': true});
46
+print(json.encode(delta));
47
+// Prints:
48
+// [{"retain":9},{"retain":3,"attributes":{"bold":true}}]
49
+```
50
+
51
+A simple way to visualize a change is as if it moves an imaginary cursor
52
+and applies modifications on the way. So with the above example, the
53
+first `retain` operation moves the cursor forward 9 characters. Then,
54
+second operation moves cursor additional 3 characters forward but also
55
+applies bold style to each character it passes.
56
+
57
+The Delta library provides a way of composing such changes into documents
58
+or transforming against each other. E.g.:
59
+
60
+```dart
61
+var doc = new Delta();
62
+doc
63
+  ..insert('Karl', {'bold': true})
64
+  ..insert(' the ')
65
+  ..insert('Fog', {'italic': true});
66
+var change = new Delta();
67
+change..retain(9)..retain(3, {'bold': true});
68
+var updatedDoc = doc.compose(change);
69
+print(json.encode(updatedDoc));
70
+// Prints:
71
+//  [
72
+//    {"insert":"Karl","attributes":{"bold":true}},
73
+//    {"insert":" the "},
74
+//    {"insert":"Fog","attributes":{"italic":true,"bold":true}}
75
+//  ]
76
+```
77
+
78
+These are the basics of Deltas. Read [official documentation][delta-docs]
79
+for more details.
80
+
81
+[delta-docs]: https://quilljs.com/docs/delta/
82
+
83
+### Document model
84
+
85
+Zefyr documents are represented as a tree of nodes. There are 3 main types of
86
+nodes:
87
+
88
+* `Text` - a leaf node which represents a segment of styled text within
89
+  a document.
90
+* `Line` - represents an individual line of text within a document.
91
+  Line nodes are containers for leaf nodes.
92
+* `Block` - represents a group of adjacent lines which share the same
93
+  style. Examples of blocks include lists, quotes or code blocks.
94
+
95
+Given above description, here is ASCII-style visualization of a Zefyr
96
+document tree:
97
+
98
+```
99
+root
100
+ ╠═ block
101
+ ║   ╠═ line
102
+ ║   ║   ╠═ text
103
+ ║   ║   ╚═ text
104
+ ║   ╚═ line
105
+ ║       ╚═ text
106
+ ╠═ line
107
+ ║   ╚═ text
108
+ ╚═ block
109
+     ╚═ line
110
+         ╠═ text
111
+         ╚═ text
112
+```
113
+
114
+It is currently not allowed to nest blocks inside other blocks but this
115
+may change in the future. More node types are likely to be added as well.
116
+For example, another leaf node for embedded content, like images.
117
+
118
+All manipulations of Zefyr documents are designed strictly to match
119
+semantics of underlying Delta format. As a result the model itself is
120
+fairly simple and predictable.
121
+
122
+To learn more about consequences of this design read about
123
+[attributes][] and [heuristics][].
124
+
125
+[heuristics]: /doc/heuristics.md
126
+[attributes]: /doc/attributes.md
127
+
128
+### Next
129
+
130
+* [Attributes](/doc/attributes.md)
131
+
132
+### Previous
133
+
134
+* [Usage](/doc/usage.md)

+ 17
- 0
doc/faq.md Ver arquivo

@@ -0,0 +1,17 @@
1
+## Frequently asked questions
2
+
3
+### Are Zefyr documents compatible with Quill documents?
4
+
5
+Short answer is no. Even though Zefyr uses Quill Delta as underlying
6
+representation for its documents there are at least differences in
7
+attribute declarations. For instance heading style in Quill
8
+editor uses "header" as the attribute key, in Zefyr it's "heading".
9
+
10
+There are also semantical differences. In Quill, both list and heading
11
+styles are treated as block styles. This means applying "heading"
12
+style to a list item removes the item from the list. In Zefyr, heading
13
+style is handled separately from block styles like lists and quotes.
14
+As a consequence you can have a heading line inside of a quote block.
15
+This is intentional and inspired by how Markdown handles such scenarios.
16
+In fact, Zefyr format tries to follow Markdown semantics as close as
17
+possible.

+ 89
- 0
doc/heuristics.md Ver arquivo

@@ -0,0 +1,89 @@
1
+## Heuristic rules
2
+
3
+As it turns out, a good rich text editor not only allows the user to
4
+manually apply different styles to text in a document. It can also
5
+automatically apply certain styles based on the context of a user
6
+action.
7
+
8
+Some very common examples include autoformatting of links or inserting
9
+a new list item.
10
+
11
+In Zefyr, such rules are called *heuristic rules*. There are two main
12
+purposes for heuritic rules:
13
+
14
+1. User experience: rules like above-mentioned autoformatting of links
15
+  are here to make editing a nice and pleasant process.
16
+2. Style normalizing: this is mostly invisible for the user but
17
+  is very important nevertheless. There is a set of rules to make sure
18
+  that a document change conforms to the data format and model
19
+  semantics.
20
+
21
+Let's cover the second item in more detail.
22
+
23
+Say, a user is editing following document (cursor position is indicated
24
+by pipe `|` character):
25
+
26
+> ### Document| title styled as h3 heading
27
+> Regular paragraph with **bold** text.
28
+
29
+User decides to change the first line style from `h3` to `h2`. If we
30
+were to apply this change to the document in code it would look like
31
+this:
32
+
33
+```dart
34
+var doc = getDocument();
35
+var cursorPosition = 8; // after the word "Document"
36
+var selectionLength = 0; // selection is collapsed.
37
+var change = doc.format(
38
+  cursorPosition, selectionLength, ZefyrAttribute.heading.level2);
39
+```
40
+
41
+If we try to apply this change as-is it would have no effect or, more
42
+likely, result in an `AssertionError`. This is why all methods in
43
+`ZefyrDocument` have an extra step which applies heuristic rules to
44
+the change (there is one method which skips this step, `compose`,
45
+read more on it later) before actually composing it.
46
+
47
+The `ZefyrDocument.format` method returns an instance of `Delta` which
48
+was actualy applied to the document. For the above scenario it would
49
+look like this:
50
+
51
+```json
52
+[
53
+  {"retain": 35},
54
+  {"retain": 1, "attributes": {"heading": 2} }
55
+]
56
+```
57
+
58
+The above change applies `h2` style to the 36th character in the
59
+document, that's the *newline* character of the first line, exactly
60
+what user intended to do.
61
+
62
+There are more similar scenarios which are covered by heuristic rules
63
+to ensure consistency with the document model and provide better UX.
64
+
65
+### `ZefyrDocument.compose()` and skipping heuristic rules.
66
+
67
+The `compose()` method is the only method which skips the step of
68
+applying heuristic rules and therefore **should be used with great
69
+care** as it can result in corrupted document state.
70
+
71
+Use this method when you sure that the change you are about to compose
72
+conforms to the document model and data format semantics.
73
+
74
+This method exists mostly to enable following use cases:
75
+
76
+* **Collaborative editing**, when a change came from a different site and
77
+  has already been normalized by heuristic rules on that site. Care must
78
+  be taken to ensure that this change is based on the same revision
79
+  of the document, and if not, transformed against any local changes
80
+  before composing.
81
+* **Change history and revisioning**, when a change came from a revision
82
+  history stored on a server or a database. Similarly, care must be
83
+  taken to transform the change against any local (uncommitted) changes
84
+  before composing.
85
+
86
+When composing a change which came from a different site or server make
87
+sure to use `ChangeSource.api` when calling `compose()`. This allows
88
+you to distinguish such changes from local changes made by the user
89
+when listening on `ZefyrDocument.changes` stream.

+ 71
- 0
doc/quick_start.md Ver arquivo

@@ -0,0 +1,71 @@
1
+## Quick Start
2
+
3
+### Installation
4
+
5
+Add `zefyr` package as a dependency to your `pubspec.yaml`:
6
+
7
+```yaml
8
+dependencies:
9
+  zefyr: ^0.1.0
10
+```
11
+
12
+And run `flutter packages get` to install.
13
+
14
+### Usage
15
+
16
+There are 3 main objects you would normally interact with in your code:
17
+
18
+* `ZefyrDocument`, represents a rich text document and provides
19
+  high-level methods for manipulating the document's state, like
20
+  inserting, deleting and formatting of text.
21
+  Read [documentation][data_and_docs] for more details on Zefyr's
22
+  document model and data format.
23
+* `ZefyrEditor`, a Flutter widget responsible for rendering of rich text
24
+  on the screen and reacting to user actions.
25
+* `ZefyrController`, ties the above two objects together.
26
+
27
+Normally you would need to place `ZefyrEditor` inside of a
28
+`StatefulWidget`. Shown below is a minimal setup required to use the
29
+editor:
30
+
31
+```dart
32
+import 'package:flutter/material.dart';
33
+import 'package:zefyr/zefyr.dart';
34
+
35
+class MyWidget extends StatefulWidget {
36
+  @override
37
+  MyWidgetState createState() => MyWidgetState();
38
+}
39
+
40
+class MyWidgetState extends State<MyWidget> {
41
+  final ZefyrController _controller;
42
+  final FocusNode _focusNode;
43
+
44
+  @override
45
+  void initState() {
46
+    super.initState();
47
+    // Create an empty document or load existing if you have one.
48
+    // Here we create an empty document:
49
+    final document = new ZefyrDocument();
50
+    _controller = new ZefyrController(document);
51
+    _focusNode = new FocusNode();
52
+  }
53
+
54
+  @override
55
+  Widget build(BuildContext context) {
56
+    return ZefyrEditor(
57
+      controller: _controller,
58
+      focusNode: _focusNode,
59
+    );
60
+  }
61
+}
62
+```
63
+
64
+In following sections you will learn more about document
65
+model, Deltas, attributes and other aspects of the editor.
66
+
67
+### Next
68
+
69
+* [Data Format and Document Model][data_and_docs]
70
+
71
+[data_and_docs]: /doc/data_and_document.md