@0m0g1/griot 0.1.1 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/griot.css ADDED
@@ -0,0 +1,353 @@
1
+ /* ─── griot.css ───────────────────────────────────────────────────────────────
2
+ All styles for both editor and viewer.
3
+ Import once; scope is via .griot-editor and .griot-viewer.
4
+ CSS variables let the host app theme everything.
5
+ ─────────────────────────────────────────────────────────────────────────── */
6
+
7
+ :root {
8
+ --griot-bg: #060918;
9
+ --griot-surface: rgba(255,255,255,0.03);
10
+ --griot-surface-hover: rgba(255,255,255,0.055);
11
+ --griot-border: rgba(255,255,255,0.07);
12
+ --griot-border-focus: rgba(99,102,241,0.5);
13
+ --griot-accent: #6366f1;
14
+ --griot-accent-soft: rgba(99,102,241,0.12);
15
+ --griot-accent-text: #a5b4fc;
16
+ --griot-text: #e2e8f0;
17
+ --griot-text-muted: #64748b;
18
+ --griot-text-faint: #334155;
19
+ --griot-code-bg: rgba(0,0,0,0.45);
20
+ --griot-code-color: #a5f3fc;
21
+ --griot-chip-event-bg: rgba(99,102,241,0.15);
22
+ --griot-chip-cite-bg: rgba(34,197,94,0.10);
23
+ --griot-font-body: system-ui, -apple-system, sans-serif;
24
+ --griot-font-mono: 'Fira Code', 'Cascadia Code', 'Consolas', monospace;
25
+ --griot-font-serif: 'Georgia', 'Times New Roman', serif;
26
+ --griot-radius: 8px;
27
+ --griot-radius-sm: 5px;
28
+ --griot-transition: 0.15s ease;
29
+ }
30
+
31
+ /* ── Shared base ───────────────────────────────────────────────────────────── */
32
+ .griot-editor,
33
+ .griot-viewer {
34
+ font-family: var(--griot-font-body);
35
+ color: var(--griot-text);
36
+ background: transparent;
37
+ line-height: 1.7;
38
+ }
39
+
40
+ /* ── Inline elements ────────────────────────────────────────────────────────── */
41
+ .griot-editor strong, .griot-viewer strong { color: #f1f5f9; font-weight: 700; }
42
+ .griot-editor em, .griot-viewer em { color: #94a3b8; }
43
+
44
+ .griot-inline-code {
45
+ background: rgba(139,92,246,0.15);
46
+ border: 1px solid rgba(139,92,246,0.25);
47
+ border-radius: 4px;
48
+ padding: 1px 6px;
49
+ font-family: var(--griot-font-mono);
50
+ font-size: 0.875em;
51
+ color: #c4b5fd;
52
+ }
53
+
54
+ .griot-link {
55
+ color: #7dd3fc;
56
+ text-decoration: none;
57
+ border-bottom: 1px solid rgba(125,211,252,0.3);
58
+ transition: border-color var(--griot-transition);
59
+ }
60
+ .griot-link:hover { border-bottom-color: #7dd3fc; }
61
+
62
+ /* Chips */
63
+ .griot-chip {
64
+ display: inline-flex;
65
+ align-items: center;
66
+ gap: 4px;
67
+ border-radius: var(--griot-radius-sm);
68
+ padding: 1px 8px;
69
+ font-family: var(--griot-font-body);
70
+ font-size: 0.85em;
71
+ cursor: pointer;
72
+ border: 1px solid;
73
+ vertical-align: middle;
74
+ transition: opacity var(--griot-transition);
75
+ line-height: 1.6;
76
+ }
77
+ .griot-chip:hover { opacity: 0.8; }
78
+ .griot-chip--event {
79
+ background: var(--griot-chip-event-bg);
80
+ border-color: rgba(99,102,241,0.35);
81
+ color: var(--griot-accent-text);
82
+ }
83
+ .griot-chip--cite {
84
+ background: var(--griot-chip-cite-bg);
85
+ border-color: rgba(34,197,94,0.25);
86
+ color: #86efac;
87
+ }
88
+ .griot-chip__icon { font-size: 0.85em; }
89
+
90
+ /* ── Viewer blocks ──────────────────────────────────────────────────────────── */
91
+ .griot-viewer .griot-block { scroll-margin-top: 80px; }
92
+
93
+ .griot-viewer .griot-paragraph {
94
+ color: var(--griot-text-muted);
95
+ margin: 0 0 14px;
96
+ font-size: 0.97em;
97
+ }
98
+
99
+ .griot-viewer .griot-heading { font-weight: 800; color: #f8fafc; }
100
+ .griot-viewer .griot-heading--1 { font-family: var(--griot-font-serif); font-size: 1.95em; margin: 32px 0 10px; letter-spacing: -0.02em; line-height: 1.15; }
101
+ .griot-viewer .griot-heading--2 { font-size: 1.4em; margin: 24px 0 8px; letter-spacing: -0.01em; }
102
+ .griot-viewer .griot-heading--3 { font-size: 0.85em; margin: 18px 0 6px; text-transform: uppercase; letter-spacing: 0.06em; color: var(--griot-accent); font-weight: 700; }
103
+ .griot-viewer .griot-heading--4 { font-size: 1.05em; margin: 14px 0 6px; color: #cbd5e1; }
104
+ .griot-viewer .griot-heading--5 { font-size: 0.95em; margin: 12px 0 5px; color: #94a3b8; }
105
+ .griot-viewer .griot-heading--6 { font-size: 0.85em; margin: 10px 0 4px; color: var(--griot-text-faint); text-transform: uppercase; }
106
+
107
+ .griot-viewer .griot-blockquote {
108
+ border-left: 3px solid var(--griot-accent);
109
+ margin: 18px 0;
110
+ padding: 10px 18px;
111
+ background: var(--griot-accent-soft);
112
+ border-radius: 0 var(--griot-radius) var(--griot-radius) 0;
113
+ color: #94a3b8;
114
+ font-style: italic;
115
+ font-family: var(--griot-font-serif);
116
+ font-size: 1.05em;
117
+ }
118
+
119
+ .griot-viewer .griot-callout {
120
+ display: flex;
121
+ gap: 12px;
122
+ background: rgba(99,102,241,0.07);
123
+ border: 1px solid rgba(99,102,241,0.2);
124
+ border-radius: 10px;
125
+ padding: 13px 16px;
126
+ margin: 14px 0;
127
+ }
128
+ .griot-callout__icon { font-size: 1.3em; flex-shrink: 0; line-height: 1.4; }
129
+ .griot-callout__body { color: var(--griot-text); line-height: 1.7; }
130
+
131
+ .griot-viewer .griot-code {
132
+ background: var(--griot-code-bg);
133
+ border: 1px solid rgba(255,255,255,0.06);
134
+ border-radius: 10px;
135
+ padding: 16px 18px;
136
+ margin: 14px 0;
137
+ font-family: var(--griot-font-mono);
138
+ font-size: 0.85em;
139
+ color: var(--griot-code-color);
140
+ line-height: 1.75;
141
+ overflow-x: auto;
142
+ }
143
+
144
+ .griot-viewer .griot-divider {
145
+ border: none;
146
+ border-top: 1px solid var(--griot-border);
147
+ margin: 28px 0;
148
+ }
149
+
150
+ .griot-viewer .griot-image { margin: 18px 0; }
151
+ .griot-viewer .griot-image img { max-width: 100%; border-radius: var(--griot-radius); display: block; }
152
+ .griot-viewer .griot-image figcaption { font-size: 0.8em; color: var(--griot-text-faint); margin-top: 6px; text-align: center; }
153
+
154
+ .griot-viewer .griot-timeline-ref {
155
+ display: flex;
156
+ align-items: center;
157
+ gap: 12px;
158
+ background: rgba(99,102,241,0.07);
159
+ border: 1px solid rgba(99,102,241,0.22);
160
+ border-radius: 10px;
161
+ padding: 12px 15px;
162
+ margin: 14px 0;
163
+ cursor: pointer;
164
+ transition: background var(--griot-transition);
165
+ }
166
+ .griot-viewer .griot-timeline-ref:hover { background: rgba(99,102,241,0.12); }
167
+ .griot-timeline-ref__icon { font-size: 1.5em; flex-shrink: 0; }
168
+ .griot-timeline-ref__body { flex: 1; }
169
+ .griot-timeline-ref__title { font-weight: 600; color: var(--griot-accent-text); font-size: 0.95em; }
170
+ .griot-timeline-ref__note { font-size: 0.85em; color: var(--griot-text-faint); margin-top: 3px; }
171
+ .griot-timeline-ref__arrow { font-size: 11px; color: var(--griot-text-faint); }
172
+
173
+ .griot-viewer .griot-citation {
174
+ background: rgba(8,12,36,0.6);
175
+ border: 1px solid rgba(99,102,241,0.25);
176
+ border-radius: 11px;
177
+ margin: 18px 0;
178
+ overflow: hidden;
179
+ }
180
+ .griot-citation__inner { border-left: 3px solid var(--griot-accent); padding: 13px 16px; }
181
+ .griot-citation__header { display: flex; align-items: center; gap: 7px; flex-wrap: wrap; font-size: 11px; text-transform: uppercase; font-weight: 700; letter-spacing: 0.08em; color: var(--griot-accent); margin-bottom: 5px; }
182
+ .griot-citation__book-icon { }
183
+ .griot-citation__book-title { }
184
+ .griot-citation__author { color: var(--griot-text-faint); font-weight: 400; text-transform: none; letter-spacing: 0; }
185
+ .griot-citation__unit { color: var(--griot-text-faint); font-weight: 400; text-transform: none; letter-spacing: 0; font-size: 10px; }
186
+ .griot-citation__quote {
187
+ border-left: 2px solid rgba(99,102,241,0.3);
188
+ padding-left: 12px; margin: 0 0 10px;
189
+ color: #94a3b8; font-style: italic;
190
+ font-family: var(--griot-font-serif);
191
+ font-size: 0.95em; line-height: 1.75;
192
+ }
193
+ .griot-citation__note { color: var(--griot-text); line-height: 1.7; font-size: 0.95em; }
194
+ .griot-citation__preview { padding: 7px 16px; border-top: 1px solid rgba(99,102,241,0.1); background: rgba(99,102,241,0.03); font-size: 11px; color: var(--griot-text-faint); line-height: 1.5; }
195
+ .griot-citation__empty,
196
+ .griot-citation__missing { padding: 14px 16px; font-size: 13px; font-style: italic; color: var(--griot-text-faint); }
197
+
198
+ /* Highlight animation */
199
+ @keyframes griot-hl-pulse {
200
+ 0% { outline-color: rgba(99,102,241,0.8); background: rgba(99,102,241,0.1); }
201
+ 70% { outline-color: rgba(99,102,241,0.4); background: rgba(99,102,241,0.05); }
202
+ 100% { outline-color: transparent; background: transparent; }
203
+ }
204
+ .griot-block--highlight {
205
+ border-radius: var(--griot-radius);
206
+ outline: 2px solid rgba(99,102,241,0.8);
207
+ outline-offset: 4px;
208
+ animation: griot-hl-pulse 2.2s ease forwards;
209
+ }
210
+
211
+ /* ── Editor blocks ──────────────────────────────────────────────────────────── */
212
+ .griot-editor-block {
213
+ border: 1px solid var(--griot-border);
214
+ border-radius: var(--griot-radius);
215
+ background: var(--griot-surface);
216
+ margin-bottom: 6px;
217
+ transition: border-color var(--griot-transition), background var(--griot-transition);
218
+ }
219
+ .griot-editor-block.is-focused {
220
+ border-color: var(--griot-border-focus);
221
+ background: var(--griot-surface-hover);
222
+ }
223
+
224
+ .griot-editor-block__toolbar {
225
+ display: flex;
226
+ align-items: center;
227
+ gap: 6px;
228
+ padding: 5px 10px;
229
+ border-bottom: 1px solid var(--griot-border);
230
+ background: rgba(0,0,0,0.15);
231
+ border-radius: var(--griot-radius) var(--griot-radius) 0 0;
232
+ flex-wrap: wrap;
233
+ }
234
+
235
+ .griot-editor-block__type-sel,
236
+ .griot-editor-block__lvl-sel {
237
+ background: rgba(255,255,255,0.04);
238
+ border: 1px solid var(--griot-border);
239
+ border-radius: var(--griot-radius-sm);
240
+ color: var(--griot-text-muted);
241
+ font-size: 10px;
242
+ padding: 3px 6px;
243
+ outline: none;
244
+ cursor: pointer;
245
+ }
246
+
247
+ .griot-editor-block__pick-btn {
248
+ background: rgba(99,102,241,0.08);
249
+ border: 1px solid rgba(99,102,241,0.25);
250
+ border-radius: var(--griot-radius-sm);
251
+ color: var(--griot-accent-text);
252
+ font-size: 10px;
253
+ padding: 3px 9px;
254
+ cursor: pointer;
255
+ outline: none;
256
+ }
257
+
258
+ .griot-editor-block__actions {
259
+ display: flex;
260
+ gap: 3px;
261
+ margin-left: auto;
262
+ }
263
+
264
+ .griot-editor-block__action-btn {
265
+ background: none;
266
+ border: 1px solid var(--griot-border);
267
+ border-radius: 4px;
268
+ color: var(--griot-text-faint);
269
+ font-size: 11px;
270
+ padding: 2px 7px;
271
+ cursor: pointer;
272
+ outline: none;
273
+ transition: color var(--griot-transition), border-color var(--griot-transition);
274
+ }
275
+ .griot-editor-block__action-btn:hover { color: var(--griot-text); }
276
+ .griot-editor-block__action-btn.is-disabled { opacity: 0.3; pointer-events: none; }
277
+ .griot-editor-block__action-btn.is-add { color: var(--griot-accent); border-color: rgba(99,102,241,0.25); }
278
+ .griot-editor-block__action-btn.is-delete { color: #f87171; border-color: rgba(248,113,113,0.25); }
279
+
280
+ .griot-editor-block__input {
281
+ display: block;
282
+ width: 100%;
283
+ padding: 10px 14px;
284
+ box-sizing: border-box;
285
+ background: transparent;
286
+ border: none;
287
+ color: var(--griot-text);
288
+ font-size: 13px;
289
+ font-family: var(--griot-font-body);
290
+ line-height: 1.7;
291
+ outline: none;
292
+ resize: none;
293
+ white-space: pre-wrap;
294
+ word-break: break-word;
295
+ min-height: 38px;
296
+ }
297
+ .griot-input--heading { font-weight: 700; font-size: 16px; }
298
+ .griot-input--code { font-family: var(--griot-font-mono); font-size: 12px; white-space: pre; min-height: 80px; color: var(--griot-code-color); }
299
+ .griot-input--blockquote { font-style: italic; color: #94a3b8; }
300
+
301
+ /* Placeholder via data attribute */
302
+ .griot-editor-block__input:empty::before {
303
+ content: attr(data-placeholder);
304
+ color: var(--griot-text-faint);
305
+ pointer-events: none;
306
+ }
307
+
308
+ /* Live preview strip */
309
+ .griot-editor-block__preview {
310
+ padding: 6px 14px 10px;
311
+ border-top: 1px solid rgba(99,102,241,0.1);
312
+ font-size: 12px;
313
+ color: #64748b;
314
+ background: rgba(99,102,241,0.02);
315
+ border-radius: 0 0 var(--griot-radius) var(--griot-radius);
316
+ line-height: 1.6;
317
+ }
318
+ .griot-editor-block__preview:empty { display: none; }
319
+
320
+ /* Special block UI */
321
+ .griot-editor-block__special { padding: 10px 12px; }
322
+
323
+ .griot-editor-block__meta-input {
324
+ background: rgba(255,255,255,0.04);
325
+ border: 1px solid var(--griot-border);
326
+ border-radius: var(--griot-radius-sm);
327
+ color: var(--griot-text);
328
+ font-size: 12px;
329
+ padding: 5px 8px;
330
+ outline: none;
331
+ font-family: var(--griot-font-body);
332
+ box-sizing: border-box;
333
+ width: 100%;
334
+ transition: border-color var(--griot-transition);
335
+ }
336
+ .griot-editor-block__meta-input:focus { border-color: var(--griot-border-focus); }
337
+ .griot-editor-block__meta-textarea { resize: vertical; min-height: 52px; display: block; }
338
+
339
+ .griot-editor-block__img-preview {
340
+ max-width: 100%;
341
+ max-height: 120px;
342
+ border-radius: 6px;
343
+ display: block;
344
+ margin-bottom: 8px;
345
+ opacity: 0.85;
346
+ }
347
+
348
+ .griot-editor-block__citation-label {
349
+ font-size: 11px;
350
+ color: var(--griot-accent);
351
+ margin-bottom: 7px;
352
+ font-weight: 600;
353
+ }
@@ -1,90 +1,87 @@
1
1
  // ─── InlineLexer.js ───────────────────────────────────────────────────────────
2
- // Tokenises inline markdown-like syntax into a flat token array.
3
- // Runs on a single text string (the content of one block's text field).
2
+ // Tokenises a plain-text string that uses lightweight inline markup.
4
3
  //
5
- // Supported syntax:
6
- // **bold** → { type:'bold', text }
7
- // *italic* → { type:'italic', text }
8
- // `code` → { type:'code', text }
9
- // [label](url) → { type:'link', text, href }
10
- // [[event:id|label]] → { type:'event_ref', eventId, label }
11
- // [[cite:blockId|label]] → { type:'cite_ref', blockId, label }
12
- // plain text → { type:'text', text }
4
+ // Supported syntax
5
+ // ─────────────────────────────────────────────────────────────────────────────
6
+ // **bold** TOKEN.BOLD { text }
7
+ // *italic* TOKEN.ITALIC { text }
8
+ // __underline__ TOKEN.UNDERLINE { text }
9
+ // ~~strikethrough~~ TOKEN.STRIKE { text }
10
+ // `inline code` TOKEN.CODE { code }
11
+ // ==highlight== TOKEN.HIGHLIGHT { text }
12
+ // {#f00:red text} {blue:text} → TOKEN.COLOR_MARK { color, text }
13
+ // [label](url) → TOKEN.LINK { label, href }
14
+ // ![alt](url) → TOKEN.IMAGE { alt, src }
15
+ // [[event:id|label]] → TOKEN.EVENT_REF { eventId, label }
16
+ // [[cite:blockId|label]] → TOKEN.CITE_REF { blockId, label }
17
+ // plain text → TOKEN.TEXT { text }
18
+ //
19
+ // Stateless and re-entrant. Rules are anchored regexes in priority order.
13
20
  // ─────────────────────────────────────────────────────────────────────────────
14
21
 
15
22
  export const TOKEN = Object.freeze({
16
- TEXT: 'text',
17
- BOLD: 'bold',
18
- ITALIC: 'italic',
19
- CODE: 'code',
20
- LINK: 'link',
21
- EVENT_REF: 'event_ref',
22
- CITE_REF: 'cite_ref',
23
+ TEXT: 'text',
24
+ BOLD: 'bold',
25
+ ITALIC: 'italic',
26
+ UNDERLINE: 'underline',
27
+ STRIKE: 'strike',
28
+ CODE: 'code',
29
+ LINK: 'link',
30
+ IMAGE: 'image',
31
+ HIGHLIGHT: 'highlight',
32
+ COLOR_MARK: 'color_mark',
33
+ EVENT_REF: 'event_ref',
34
+ CITE_REF: 'cite_ref',
23
35
  });
24
36
 
25
- // Combined regex — order matters (longest/most-specific first)
26
- const INLINE_RE = new RegExp(
27
- [
28
- /(\*\*(.+?)\*\*)/, // bold
29
- /(\*(.+?)\*)/, // italic
30
- /(`([^`]+)`)/, // inline code
31
- /(\[(.+?)\]\((.+?)\))/, // link
32
- /(\[\[event:([^\]|]+)(?:\|([^\]]*))?\]\])/, // event_ref
33
- /(\[\[cite:([^\]|]+)(?:\|([^\]]*))?\]\])/, // cite_ref
34
- ].map(r => r.source).join('|'),
35
- 'g'
36
- );
37
+ const RULES = [
38
+ // Inline image ![alt](url) — must precede link rule
39
+ { type: TOKEN.IMAGE, re: /^!\[([^\]]*)\]\(([^)\s]+)\)/, build: m => ({ alt: m[1], src: m[2] }) },
40
+ // Link [label](url)
41
+ { type: TOKEN.LINK, re: /^\[([^\]]+)\]\(([^)\s]+)\)/, build: m => ({ label: m[1], href: m[2] }) },
42
+ // Bold **text** — before italic
43
+ { type: TOKEN.BOLD, re: /^\*\*((?:[^*]|\*(?!\*))+)\*\*/, build: m => ({ text: m[1] }) },
44
+ // Italic *text*
45
+ { type: TOKEN.ITALIC, re: /^\*((?:[^*])+)\*/, build: m => ({ text: m[1] }) },
46
+ // Underline __text__
47
+ { type: TOKEN.UNDERLINE, re: /^__((?:[^_])+)__/, build: m => ({ text: m[1] }) },
48
+ // Strikethrough ~~text~~
49
+ { type: TOKEN.STRIKE, re: /^~~((?:[^~])+)~~/, build: m => ({ text: m[1] }) },
50
+ // Highlight ==text==
51
+ { type: TOKEN.HIGHLIGHT, re: /^==((?:[^=])+)==/, build: m => ({ text: m[1] }) },
52
+ // Colour mark {#hex:text} or {colorname:text}
53
+ { type: TOKEN.COLOR_MARK, re: /^\{(#[0-9a-fA-F]{3,8}|[a-zA-Z][a-zA-Z0-9_-]*):([^}]+)\}/, build: m => ({ color: m[1], text: m[2] }) },
54
+ // Inline code `code`
55
+ { type: TOKEN.CODE, re: /^`([^`]+)`/, build: m => ({ code: m[1] }) },
56
+ // Event ref [[event:id|label]]
57
+ { type: TOKEN.EVENT_REF, re: /^\[\[event:([^\]|]+)(?:\|([^\]]*))?\]\]/, build: m => ({ eventId: m[1], label: m[2] || m[1] }) },
58
+ // Cite ref [[cite:id|label]]
59
+ { type: TOKEN.CITE_REF, re: /^\[\[cite:([^\]|]+)(?:\|([^\]]*))?\]\]/, build: m => ({ blockId: m[1], label: m[2] || m[1] }) },
60
+ ];
37
61
 
38
62
  export function tokenizeInline(text = '') {
39
63
  if (!text) return [];
40
-
41
64
  const tokens = [];
42
- let last = 0;
43
- let m;
65
+ let pos = 0, textStart = 0;
44
66
 
45
- INLINE_RE.lastIndex = 0;
67
+ while (pos < text.length) {
68
+ const remaining = text.slice(pos);
69
+ let matched = false;
46
70
 
47
- while ((m = INLINE_RE.exec(text)) !== null) {
48
- // Flush plain text before this match
49
- if (m.index > last) {
50
- tokens.push({ type: TOKEN.TEXT, text: text.slice(last, m.index) });
71
+ for (const rule of RULES) {
72
+ const m = remaining.match(rule.re);
73
+ if (!m) continue;
74
+ if (pos > textStart) tokens.push({ type: TOKEN.TEXT, text: text.slice(textStart, pos) });
75
+ tokens.push({ type: rule.type, ...rule.build(m) });
76
+ pos += m[0].length;
77
+ textStart = pos;
78
+ matched = true;
79
+ break;
51
80
  }
52
81
 
53
- if (m[1]) {
54
- // bold
55
- tokens.push({ type: TOKEN.BOLD, text: m[2] });
56
- } else if (m[3]) {
57
- // italic
58
- tokens.push({ type: TOKEN.ITALIC, text: m[4] });
59
- } else if (m[5]) {
60
- // inline code
61
- tokens.push({ type: TOKEN.CODE, text: m[6] });
62
- } else if (m[7]) {
63
- // link
64
- tokens.push({ type: TOKEN.LINK, text: m[8], href: m[9] });
65
- } else if (m[10]) {
66
- // event_ref
67
- tokens.push({
68
- type: TOKEN.EVENT_REF,
69
- eventId: m[11],
70
- label: m[12] || m[11],
71
- });
72
- } else if (m[13]) {
73
- // cite_ref
74
- tokens.push({
75
- type: TOKEN.CITE_REF,
76
- blockId: m[14],
77
- label: m[15] || m[14],
78
- });
79
- }
80
-
81
- last = INLINE_RE.lastIndex;
82
- }
83
-
84
- // Remaining plain text
85
- if (last < text.length) {
86
- tokens.push({ type: TOKEN.TEXT, text: text.slice(last) });
82
+ if (!matched) pos++;
87
83
  }
88
84
 
85
+ if (textStart < text.length) tokens.push({ type: TOKEN.TEXT, text: text.slice(textStart) });
89
86
  return tokens.length ? tokens : [{ type: TOKEN.TEXT, text }];
90
- }
87
+ }