@orz/ui-ansi 0.0.0-stage → 0.1.0
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/LICENSE +29 -0
- package/README.md +232 -2
- package/dist/content.d.ts +20 -0
- package/dist/content.d.ts.map +1 -0
- package/dist/content.js +90 -0
- package/dist/content.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/parse/index.d.ts +6 -0
- package/dist/parse/index.d.ts.map +1 -0
- package/dist/parse/index.js +5 -0
- package/dist/parse/index.js.map +1 -0
- package/dist/parse/level-tracker.d.ts +14 -0
- package/dist/parse/level-tracker.d.ts.map +1 -0
- package/dist/parse/level-tracker.js +87 -0
- package/dist/parse/level-tracker.js.map +1 -0
- package/dist/parse/levels.d.ts +19 -0
- package/dist/parse/levels.d.ts.map +1 -0
- package/dist/parse/levels.js +167 -0
- package/dist/parse/levels.js.map +1 -0
- package/dist/parse/line.d.ts +49 -0
- package/dist/parse/line.d.ts.map +1 -0
- package/dist/parse/line.js +289 -0
- package/dist/parse/line.js.map +1 -0
- package/dist/parse/palette.d.ts +12 -0
- package/dist/parse/palette.d.ts.map +1 -0
- package/dist/parse/palette.js +40 -0
- package/dist/parse/palette.js.map +1 -0
- package/dist/parse/parser.d.ts +18 -0
- package/dist/parse/parser.d.ts.map +1 -0
- package/dist/parse/parser.js +178 -0
- package/dist/parse/parser.js.map +1 -0
- package/dist/parse/sgr.d.ts +7 -0
- package/dist/parse/sgr.d.ts.map +1 -0
- package/dist/parse/sgr.js +179 -0
- package/dist/parse/sgr.js.map +1 -0
- package/dist/parse/styles.d.ts +15 -0
- package/dist/parse/styles.d.ts.map +1 -0
- package/dist/parse/styles.js +75 -0
- package/dist/parse/styles.js.map +1 -0
- package/dist/parse/tokenizer.d.ts +36 -0
- package/dist/parse/tokenizer.d.ts.map +1 -0
- package/dist/parse/tokenizer.js +287 -0
- package/dist/parse/tokenizer.js.map +1 -0
- package/dist/parse/types.d.ts +50 -0
- package/dist/parse/types.d.ts.map +1 -0
- package/dist/parse/types.js +2 -0
- package/dist/parse/types.js.map +1 -0
- package/dist/view/ansi-view.d.ts +27 -0
- package/dist/view/ansi-view.d.ts.map +1 -0
- package/dist/view/ansi-view.js +675 -0
- package/dist/view/ansi-view.js.map +1 -0
- package/dist/view/geometry.d.ts +30 -0
- package/dist/view/geometry.d.ts.map +1 -0
- package/dist/view/geometry.js +125 -0
- package/dist/view/geometry.js.map +1 -0
- package/dist/view/metrics.d.ts +17 -0
- package/dist/view/metrics.d.ts.map +1 -0
- package/dist/view/metrics.js +69 -0
- package/dist/view/metrics.js.map +1 -0
- package/dist/view/row.d.ts +41 -0
- package/dist/view/row.d.ts.map +1 -0
- package/dist/view/row.js +113 -0
- package/dist/view/row.js.map +1 -0
- package/dist/view/run-style.d.ts +11 -0
- package/dist/view/run-style.d.ts.map +1 -0
- package/dist/view/run-style.js +87 -0
- package/dist/view/run-style.js.map +1 -0
- package/dist/view/search.d.ts +28 -0
- package/dist/view/search.d.ts.map +1 -0
- package/dist/view/search.js +76 -0
- package/dist/view/search.js.map +1 -0
- package/dist/view/shown-lines.d.ts +15 -0
- package/dist/view/shown-lines.d.ts.map +1 -0
- package/dist/view/shown-lines.js +50 -0
- package/dist/view/shown-lines.js.map +1 -0
- package/dist/view/time-slices.d.ts +9 -0
- package/dist/view/time-slices.d.ts.map +1 -0
- package/dist/view/time-slices.js +35 -0
- package/dist/view/time-slices.js.map +1 -0
- package/dist/view/toolbar.d.ts +47 -0
- package/dist/view/toolbar.d.ts.map +1 -0
- package/dist/view/toolbar.js +58 -0
- package/dist/view/toolbar.js.map +1 -0
- package/dist/view/use-ansi-lines.d.ts +28 -0
- package/dist/view/use-ansi-lines.d.ts.map +1 -0
- package/dist/view/use-ansi-lines.js +103 -0
- package/dist/view/use-ansi-lines.js.map +1 -0
- package/dist/view/use-search.d.ts +37 -0
- package/dist/view/use-search.d.ts.map +1 -0
- package/dist/view/use-search.js +205 -0
- package/dist/view/use-search.js.map +1 -0
- package/dist/view/virtual-rows.d.ts +36 -0
- package/dist/view/virtual-rows.d.ts.map +1 -0
- package/dist/view/virtual-rows.js +156 -0
- package/dist/view/virtual-rows.js.map +1 -0
- package/package.json +58 -3
- package/styles.css +110 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright 2026 the orzui team
|
|
4
|
+
Copyright 2026 the todou team
|
|
5
|
+
|
|
6
|
+
Redistribution and use in source and binary forms, with or without
|
|
7
|
+
modification, are permitted provided that the following conditions are met:
|
|
8
|
+
|
|
9
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
10
|
+
list of conditions and the following disclaimer.
|
|
11
|
+
|
|
12
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
13
|
+
this list of conditions and the following disclaimer in the documentation
|
|
14
|
+
and/or other materials provided with the distribution.
|
|
15
|
+
|
|
16
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
17
|
+
contributors may be used to endorse or promote products derived from
|
|
18
|
+
this software without specific prior written permission.
|
|
19
|
+
|
|
20
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
21
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
22
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
23
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
24
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
25
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
26
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
27
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
28
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
29
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,233 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @orz/ui-ansi
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Text with ANSI escape sequences, such as CI logs, `ls --color` or `git diff --color`, drawn in the
|
|
4
|
+
active `@orz/ui` terminal colours, and plain logs, with each line coloured and filterable by its log
|
|
5
|
+
level. The package contributes two renderers to `@orz/ui-content`'s registry, so an
|
|
6
|
+
```` ```ansi ```` or ```` ```log ```` fence in `@orz/ui-markdown` and an `.ansi` or `.log` file in a
|
|
7
|
+
viewer are drawn without importing this package from either. Its parser and level detector have no
|
|
8
|
+
React and no DOM, and are usable on their own.
|
|
9
|
+
|
|
10
|
+
A log is the same view with levels turned on, not a second component: a `.log` file full of escapes
|
|
11
|
+
needs no routing decision, and plain text takes the parser's fast path at no extra cost.
|
|
12
|
+
|
|
13
|
+
## Install
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pnpm add @orz/ui-ansi @orz/ui-content @orz/ui react lucide-react
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
There are no runtime dependencies. `@orz/ui-content` is a peer dependency so that the app and every
|
|
20
|
+
content package share one registry.
|
|
21
|
+
|
|
22
|
+
## CSS
|
|
23
|
+
|
|
24
|
+
```css
|
|
25
|
+
@import "tailwindcss";
|
|
26
|
+
@import "@orz/ui/styles.css";
|
|
27
|
+
@import "@orz/ui-ansi/styles.css";
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Import it after `@orz/ui/styles.css`. It holds the selection colour (`--terminal-selection`), the level and
|
|
31
|
+
search-match colours, and
|
|
32
|
+
like `@orz/ui`'s stylesheet it registers `@source "./dist"` on its own.
|
|
33
|
+
|
|
34
|
+
## `ansiContent`
|
|
35
|
+
|
|
36
|
+
```tsx
|
|
37
|
+
import { ansiContent, logContent } from "@orz/ui-ansi";
|
|
38
|
+
import { codeContent } from "@orz/ui-code";
|
|
39
|
+
import { ContentRenderers } from "@orz/ui-content";
|
|
40
|
+
|
|
41
|
+
const RENDERERS = [ansiContent, logContent, codeContent];
|
|
42
|
+
|
|
43
|
+
<ContentRenderers renderers={RENDERERS}>{app}</ContentRenderers>;
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
It accepts inline sources whose `lang` is `ansi` (any case), which is what a markdown fence becomes,
|
|
47
|
+
and files named `*.ansi` or typed `text/x-ansi`. `.log` files and `log` fences are `logContent`'s
|
|
48
|
+
(see [Logs](#logs)). Register it before any renderer that takes every inline source, such as a code renderer, since
|
|
49
|
+
the first renderer that accepts wins.
|
|
50
|
+
|
|
51
|
+
- A fence draws the terminal pane, 32rem tall at most in a document.
|
|
52
|
+
- A file draws the file chrome, 32rem at most in a document and the attachment pane's height in a
|
|
53
|
+
viewer. Its final newline is dropped, so it does not end on an empty numbered line. While it loads
|
|
54
|
+
the registry's fallback link shows; a file that fails to load shows the message and a Retry button.
|
|
55
|
+
- A file declared or loaded over `ANSI_PREVIEW_MAX_BYTES` (64 MiB) is not drawn: it shows the
|
|
56
|
+
download link and "Too large to preview — download to view."
|
|
57
|
+
|
|
58
|
+
## `AnsiView`
|
|
59
|
+
|
|
60
|
+
For a surface that draws ANSI text outside the registry:
|
|
61
|
+
|
|
62
|
+
```tsx
|
|
63
|
+
import { AnsiView } from "@orz/ui-ansi";
|
|
64
|
+
|
|
65
|
+
<AnsiView chrome="pane" text={output} />
|
|
66
|
+
<AnsiView chrome="file" text={log} filename="build.log" downloadUrl={url} maxHeight="40rem" levels />
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
| Prop | |
|
|
70
|
+
|---|---|
|
|
71
|
+
| `text` | The text, escapes included. |
|
|
72
|
+
| `chrome` | `"pane"`: the terminal pane, with Copy and Wrap on hover and on focus. `"file"`: a header with the filename, the line count, Wrap, Copy and Download, a toolbar with the level chips and the search box, and a line-number gutter. |
|
|
73
|
+
| `filename` | Shown in the file header, and the region's accessible name. |
|
|
74
|
+
| `downloadUrl` | The header's Download link. |
|
|
75
|
+
| `boldIsBright` | Bold text in palette colours 0–7 is drawn in 8–15. Default `false`. |
|
|
76
|
+
| `wrap` | The initial wrap state; the Wrap button toggles it. Default `false`. |
|
|
77
|
+
| `maxHeight` | How tall the view may grow before it scrolls. Default: it grows to fit. |
|
|
78
|
+
| `levels` | `true` colours each line by its detected level and, in file chrome, adds the level filter; a function replaces the detector (see [Logs](#logs)). Default `false`. |
|
|
79
|
+
| `className` | On the root. |
|
|
80
|
+
|
|
81
|
+
Copy writes the whole text with its escapes stripped, not only the rows on screen; while a filter
|
|
82
|
+
hides lines, it writes only the shown ones. A selection copied from the view is rebuilt from the
|
|
83
|
+
parsed lines, so a wrapped line copies as one line and a hidden line is never pasted.
|
|
84
|
+
|
|
85
|
+
## Logs
|
|
86
|
+
|
|
87
|
+
`logContent` accepts inline sources whose `lang` is `log` (any case), and files named `*.log` or
|
|
88
|
+
typed `text/x-log`. A file whose name and type disagree, such as `build.log` typed `text/x-ansi`,
|
|
89
|
+
goes to whichever of `logContent` and `ansiContent` is registered first. Both go before
|
|
90
|
+
`codeContent`: it claims the same sources, draws them with a syntax grammar, and stops at 1 MiB.
|
|
91
|
+
Files get everything `ansiContent`'s files get, including the 64 MiB cap. A fence draws the pane,
|
|
92
|
+
coloured by level but with no toolbar: fences are short, mount every row, and keep the browser's
|
|
93
|
+
find.
|
|
94
|
+
|
|
95
|
+
With `levels`, each line's level is read from its text, escapes stripped. A function in its place
|
|
96
|
+
replaces the detector for logs of a shape the rules below miss. Keep its identity across renders,
|
|
97
|
+
since a new function classifies the whole text again; a detector that throws is read as "no level"
|
|
98
|
+
for that line, and the first error is logged once.
|
|
99
|
+
|
|
100
|
+
```tsx
|
|
101
|
+
import type { LevelDetector } from "@orz/ui-ansi";
|
|
102
|
+
|
|
103
|
+
// At module level, so it keeps its identity.
|
|
104
|
+
const detect: LevelDetector = (text) => (text.startsWith("!!") ? { level: "error" } : null);
|
|
105
|
+
|
|
106
|
+
<AnsiView chrome="file" text={log} levels={detect} />;
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
`detectLevel` tries these rules over the first 256 characters, and the first that matches wins:
|
|
110
|
+
|
|
111
|
+
1. A JSON line with a `level`, `lvl`, `severity` or `loglevel` key, as a word or as a pino/bunyan
|
|
112
|
+
number (≥60 fatal, ≥50 error, ≥40 warn, ≥30 info, ≥20 debug, below that trace).
|
|
113
|
+
2. logfmt: `level=`, `lvl=` or `severity=`.
|
|
114
|
+
3. A syslog priority, `<n>` at the start.
|
|
115
|
+
4. glog/klog: `E1023 09:14:11…`.
|
|
116
|
+
5. Android logcat, in the `brief` and `threadtime` forms.
|
|
117
|
+
6. `Traceback (most recent call last):`, as error.
|
|
118
|
+
7. A level word, whichever form comes first: UPPERCASE anywhere, any case inside `[…]`, `<…>` or
|
|
119
|
+
`(…)`, any case before `:`, or any case as the line's first or second word.
|
|
120
|
+
|
|
121
|
+
| Words (any case) | Level |
|
|
122
|
+
|---|---|
|
|
123
|
+
| `fatal`, `critical`, `crit`, `emerg`, `emergency`, `alert`, `panic` | fatal |
|
|
124
|
+
| `error`, `err`, `severe` | error |
|
|
125
|
+
| `warning`, `warn` | warn |
|
|
126
|
+
| `info`, `information`, `notice` | info |
|
|
127
|
+
| `debug`, `dbg`, `fine` | debug |
|
|
128
|
+
| `trace`, `verbose`, `finer`, `finest` | trace |
|
|
129
|
+
|
|
130
|
+
A lowercase word in the middle of a message does not count, so "disk I/O error" and "no errors
|
|
131
|
+
expected" stay unlevelled, and the word must stand whole: `ERROR_CODE` and `errorCount` do not match.
|
|
132
|
+
The rules are a heuristic; the fixtures in `test/parse/levels.test.ts` are their specification.
|
|
133
|
+
|
|
134
|
+
A line with no level of its own takes the level of the line above when it looks like the rest of
|
|
135
|
+
that entry: it is indented, starts with `Caused by:`, `Suppressed:` or `... N more`, or is an
|
|
136
|
+
exception head such as `java.lang.IllegalStateException: …` or `ValueError: …`. A stack trace
|
|
137
|
+
therefore stays with its error under the filter, while an unindented `Done in 3.2s.` after a warning
|
|
138
|
+
does not. Any other line, a blank one included, is "Other" and ends the chain. This applies to a
|
|
139
|
+
custom detector too.
|
|
140
|
+
|
|
141
|
+
How a level shows, in the theme's terminal palette:
|
|
142
|
+
|
|
143
|
+
- Fatal, error and warn rows get a stripe on the left and a faint tint, so they stand out while
|
|
144
|
+
scrolling; an inherited line's stripe is fainter.
|
|
145
|
+
- The level word takes the level's colour: bright red (inverted for fatal), red, yellow, green for
|
|
146
|
+
info, blue for debug and grey for trace.
|
|
147
|
+
- Debug and trace text is dimmed.
|
|
148
|
+
- A line with ANSI colours of its own keeps them: no word colour and no dimming, but still the
|
|
149
|
+
stripe, the tint and the filter.
|
|
150
|
+
|
|
151
|
+
In file chrome, a chip per level that occurs shows its line count. A click hides or shows that
|
|
152
|
+
level; shift+click shows only that level, and again shows all. Hidden lines leave the view: line
|
|
153
|
+
numbers keep their values, a dashed rule marks each gap, and the header reads "N of M lines".
|
|
154
|
+
|
|
155
|
+
## Search
|
|
156
|
+
|
|
157
|
+
The file chrome has a search box that finds text in every line, including the rows a virtualised
|
|
158
|
+
view has not mounted. Ctrl+F (⌘F) while focus is inside the view goes to it instead of the browser's
|
|
159
|
+
find; outside the view the browser's find is untouched. Enter and Shift+Enter, or the arrows, step
|
|
160
|
+
through the matches and wrap at the ends; Escape returns to the log and keeps the highlights.
|
|
161
|
+
|
|
162
|
+
- **Aa** matches case, **.\*** reads the query as a regular expression, and **≡** shows only the
|
|
163
|
+
lines with a match.
|
|
164
|
+
- Search sees only the shown lines: a level the filter hides cannot be reached from the box.
|
|
165
|
+
- A match never spans two lines, and it is found in the text with escapes stripped.
|
|
166
|
+
- The scan runs in time slices, so typing stays responsive in a large log; the counter reads
|
|
167
|
+
"3 / 41…" until it finishes. It stops storing at 100,000 matches and reads "100,000+".
|
|
168
|
+
- The slices bound the total time, not one `exec`: a pathological regular expression on one long line
|
|
169
|
+
still blocks while it runs.
|
|
170
|
+
|
|
171
|
+
## `@orz/ui-ansi/parse`
|
|
172
|
+
|
|
173
|
+
```ts
|
|
174
|
+
import { createAnsiParser, parseAnsi, stripAnsi } from "@orz/ui-ansi/parse";
|
|
175
|
+
|
|
176
|
+
const { lines, styles, maxCells } = parseAnsi(text);
|
|
177
|
+
stripAnsi("\x1b[31merror\x1b[0m"); // "error"
|
|
178
|
+
|
|
179
|
+
const parser = createAnsiParser();
|
|
180
|
+
parser.push(chunk); // any split point: an escape cut in half is carried over
|
|
181
|
+
parser.end();
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
```ts
|
|
185
|
+
import { createLevelTracker, detectLevel } from "@orz/ui-ansi/parse";
|
|
186
|
+
|
|
187
|
+
detectLevel("2026-10-23 09:14:03 WARN pool exhausted"); // { level: "warn", from: 20, to: 24 }
|
|
188
|
+
|
|
189
|
+
const tracker = createLevelTracker(); // or createLevelTracker(detect)
|
|
190
|
+
tracker.push(lines, 0, lines.length); // then push(lines, tracker.count, lines.length) as lines arrive
|
|
191
|
+
tracker.levels; // one code per line, continuation lines included
|
|
192
|
+
tracker.counts; // { fatal, error, warn, info, debug, trace, other }
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
Each line holds its text, its display width in cells, and its runs as `[offset, styleId]` pairs over
|
|
196
|
+
one `Uint32Array`; styles are interned, so a million lines share a few dozen style objects.
|
|
197
|
+
|
|
198
|
+
What is drawn:
|
|
199
|
+
|
|
200
|
+
- SGR: bold, dim, italic, the underline styles (`4:1`–`4:5`, `21`), blink (kept, not animated),
|
|
201
|
+
inverse, hidden, strike and overline; foreground, background and underline colour (`58`) as the 16
|
|
202
|
+
palette colours, the 256-colour palette or 24-bit RGB, in both the `;` and `:` forms.
|
|
203
|
+
- `\r`, `\b`, `\t` (stops every 8 cells), `CSI C`/`D`/`G` and `CSI 0K`/`1K`/`2K` within the line, so
|
|
204
|
+
a progress bar ends in its final state.
|
|
205
|
+
- OSC 8 links whose URI is `http:`, `https:` or `mailto:` and at most 2,048 characters.
|
|
206
|
+
- Wide characters take two cells and combining marks none, from `@orz/ui/lib/cell-width`.
|
|
207
|
+
|
|
208
|
+
What is dropped: every other CSI (cursor up and down, scroll regions, screen erase), every other OSC,
|
|
209
|
+
DCS, APC, PM and SOS up to its terminator, other C0 controls, and C1 controls. An unterminated OSC is
|
|
210
|
+
dropped once it passes 4 KiB, and the text after it is parsed normally.
|
|
211
|
+
|
|
212
|
+
## Theming
|
|
213
|
+
|
|
214
|
+
Palette colours 0–15 are `var(--ansi-*)`, and the default foreground and background are
|
|
215
|
+
`var(--terminal-foreground)` and `var(--terminal-background)`, which `@orz/themes` writes for every
|
|
216
|
+
theme, including the terminal slot. A theme change needs no re-render. The 256-colour palette and
|
|
217
|
+
24-bit colours are absolute, as in every terminal.
|
|
218
|
+
|
|
219
|
+
## Large text
|
|
220
|
+
|
|
221
|
+
Up to 1 MiB is parsed in one go; above that, the text is parsed in slices that yield to the browser,
|
|
222
|
+
and the header reads "Parsing… N lines" while it runs; level counts grow with it. Up to 5,000
|
|
223
|
+
visual rows of the shown lines, every row is in the DOM. Above that only the rows on screen are
|
|
224
|
+
mounted, so the browser's find sees only those; in file chrome, the view's own search covers the
|
|
225
|
+
rest. A virtualised view needs a bounded height: without `maxHeight` it takes 32rem.
|
|
226
|
+
|
|
227
|
+
## Entry points
|
|
228
|
+
|
|
229
|
+
| Import | Contents |
|
|
230
|
+
|---|---|
|
|
231
|
+
| `@orz/ui-ansi` | `ansiContent`, `logContent`, `ANSI_PREVIEW_MAX_BYTES`, `AnsiView`, `AnsiViewProps`, `LogLevel`, `LevelMatch`, `LevelDetector` |
|
|
232
|
+
| `@orz/ui-ansi/parse` | `parseAnsi`, `stripAnsi`, `createAnsiParser`, `detectLevel`, `createLevelTracker`, `LEVELS`, `LEVEL_INHERITED`, `levelCode`, `levelOfCode`, `xterm256`, `PALETTE_NAMES`, and the types |
|
|
233
|
+
| `@orz/ui-ansi/styles.css` | the selection, level and match colours |
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type ContentRenderer } from "@orz/ui-content";
|
|
2
|
+
/**
|
|
3
|
+
* The largest file `ansiContent` or `logContent` previews. The view is virtualised, so the cap is
|
|
4
|
+
* set by the memory a parsed log takes rather than by layout, and sits far above
|
|
5
|
+
* `TEXT_PREVIEW_MAX_BYTES`.
|
|
6
|
+
*/
|
|
7
|
+
export declare const ANSI_PREVIEW_MAX_BYTES: number;
|
|
8
|
+
/**
|
|
9
|
+
* The registry entry for terminal output: a source tagged `ansi` (a fenced block) and a `.ansi` or
|
|
10
|
+
* `text/x-ansi` file, in either placement. Register it before any renderer that takes every inline
|
|
11
|
+
* source, such as `codeContent`. A file over `ANSI_PREVIEW_MAX_BYTES` gets the download link.
|
|
12
|
+
*/
|
|
13
|
+
export declare const ansiContent: ContentRenderer;
|
|
14
|
+
/**
|
|
15
|
+
* The registry entry for logs: a source tagged `log` and a `.log` or `text/x-log` file, drawn with
|
|
16
|
+
* level chips, the level filter and search. Register it before `codeContent`, which claims the same
|
|
17
|
+
* sources and stops at a far smaller size. Files share `ansiContent`'s cap.
|
|
18
|
+
*/
|
|
19
|
+
export declare const logContent: ContentRenderer;
|
|
20
|
+
//# sourceMappingURL=content.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"content.d.ts","sourceRoot":"","sources":["../src/content.tsx"],"names":[],"mappings":"AAEA,OAAO,EAGL,KAAK,eAAe,EAGrB,MAAM,iBAAiB,CAAC;AAGzB;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,QAAmB,CAAC;AAyIvD;;;;GAIG;AACH,eAAO,MAAM,WAAW,EAAE,eAKzB,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,UAAU,EAAE,eAKxB,CAAC"}
|
package/dist/content.js
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Button } from "@orz/ui/components/button";
|
|
3
|
+
import { cn } from "@orz/ui/lib/utils";
|
|
4
|
+
import { ContentFallback, useSourceText, } from "@orz/ui-content";
|
|
5
|
+
import { AnsiView } from "./view/ansi-view.js";
|
|
6
|
+
/**
|
|
7
|
+
* The largest file `ansiContent` or `logContent` previews. The view is virtualised, so the cap is
|
|
8
|
+
* set by the memory a parsed log takes rather than by layout, and sits far above
|
|
9
|
+
* `TEXT_PREVIEW_MAX_BYTES`.
|
|
10
|
+
*/
|
|
11
|
+
export const ANSI_PREVIEW_MAX_BYTES = 64 * 1024 * 1024;
|
|
12
|
+
const DOCUMENT_MAX_HEIGHT = "32rem";
|
|
13
|
+
function isAnsi(source) {
|
|
14
|
+
if (source.kind === "inline")
|
|
15
|
+
return source.lang?.toLowerCase() === "ansi";
|
|
16
|
+
if (/\.ansi$/i.test(source.name))
|
|
17
|
+
return true;
|
|
18
|
+
const mime = source.mime?.split(";")[0]?.trim().toLowerCase();
|
|
19
|
+
return mime === "text/x-ansi";
|
|
20
|
+
}
|
|
21
|
+
function isLog(source) {
|
|
22
|
+
if (source.kind === "inline")
|
|
23
|
+
return source.lang?.toLowerCase() === "log";
|
|
24
|
+
if (/\.log$/i.test(source.name))
|
|
25
|
+
return true;
|
|
26
|
+
const mime = source.mime?.split(";")[0]?.trim().toLowerCase();
|
|
27
|
+
return mime === "text/x-log";
|
|
28
|
+
}
|
|
29
|
+
function TooLargeFile({ source, placement, className, }) {
|
|
30
|
+
return (_jsxs("div", { className: className, children: [_jsx(ContentFallback, { source: source, placement: placement }), " ", _jsx("span", { children: "Too large to preview \u2014 download to view." })] }));
|
|
31
|
+
}
|
|
32
|
+
function LoadedFile({ source, placement, className, levels }) {
|
|
33
|
+
const text = useSourceText(source);
|
|
34
|
+
if (text.status === "loading") {
|
|
35
|
+
return (_jsx(ContentFallback, { source: source, placement: placement, className: className }));
|
|
36
|
+
}
|
|
37
|
+
if (text.status === "error") {
|
|
38
|
+
return (_jsxs("div", { className: className, children: [_jsx(ContentFallback, { source: source, placement: placement }), " ", _jsx("span", { children: text.error.message }), " ", _jsx(Button, { size: "xs", variant: "outline", onClick: text.retry, children: "Retry" })] }));
|
|
39
|
+
}
|
|
40
|
+
// Counted in UTF-16 units, not bytes: the cap is generous, and encoding a
|
|
41
|
+
// 64 MiB string only to measure it would cost more than the check saves.
|
|
42
|
+
if (text.text.length > ANSI_PREVIEW_MAX_BYTES) {
|
|
43
|
+
return (_jsx(TooLargeFile, { source: source, placement: placement, className: className }));
|
|
44
|
+
}
|
|
45
|
+
return (_jsx(AnsiView, { chrome: "file", levels: levels, filename: source.name, downloadUrl: source.url,
|
|
46
|
+
// The final newline would otherwise end the file on an empty numbered line.
|
|
47
|
+
text: text.text.replace(/\r?\n$/, ""), maxHeight: placement === "document" ? DOCUMENT_MAX_HEIGHT : undefined, className: cn(placement === "viewer" && "h-(--attachment-pane-height)", className) }));
|
|
48
|
+
}
|
|
49
|
+
function AnsiFile(props) {
|
|
50
|
+
const { source, placement, className } = props;
|
|
51
|
+
if (source.size !== undefined && source.size > ANSI_PREVIEW_MAX_BYTES) {
|
|
52
|
+
return (_jsx(TooLargeFile, { source: source, placement: placement, className: className }));
|
|
53
|
+
}
|
|
54
|
+
return _jsx(LoadedFile, { ...props });
|
|
55
|
+
}
|
|
56
|
+
function Source({ source, placement, className, levels, }) {
|
|
57
|
+
if (source.kind === "file") {
|
|
58
|
+
return (_jsx(AnsiFile, { source: source, placement: placement, className: className, levels: levels }));
|
|
59
|
+
}
|
|
60
|
+
return (_jsx(AnsiView, { chrome: "pane", levels: levels, text: source.text, className: className, maxHeight: placement === "document" ? DOCUMENT_MAX_HEIGHT : undefined }));
|
|
61
|
+
}
|
|
62
|
+
function Ansi(props) {
|
|
63
|
+
return _jsx(Source, { ...props, levels: false });
|
|
64
|
+
}
|
|
65
|
+
function Log(props) {
|
|
66
|
+
return _jsx(Source, { ...props, levels: true });
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* The registry entry for terminal output: a source tagged `ansi` (a fenced block) and a `.ansi` or
|
|
70
|
+
* `text/x-ansi` file, in either placement. Register it before any renderer that takes every inline
|
|
71
|
+
* source, such as `codeContent`. A file over `ANSI_PREVIEW_MAX_BYTES` gets the download link.
|
|
72
|
+
*/
|
|
73
|
+
export const ansiContent = {
|
|
74
|
+
name: "ansi",
|
|
75
|
+
accepts: isAnsi,
|
|
76
|
+
display: "block",
|
|
77
|
+
Component: Ansi,
|
|
78
|
+
};
|
|
79
|
+
/**
|
|
80
|
+
* The registry entry for logs: a source tagged `log` and a `.log` or `text/x-log` file, drawn with
|
|
81
|
+
* level chips, the level filter and search. Register it before `codeContent`, which claims the same
|
|
82
|
+
* sources and stops at a far smaller size. Files share `ansiContent`'s cap.
|
|
83
|
+
*/
|
|
84
|
+
export const logContent = {
|
|
85
|
+
name: "log",
|
|
86
|
+
accepts: isLog,
|
|
87
|
+
display: "block",
|
|
88
|
+
Component: Log,
|
|
89
|
+
};
|
|
90
|
+
//# sourceMappingURL=content.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"content.js","sourceRoot":"","sources":["../src/content.tsx"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { ANSI_PREVIEW_MAX_BYTES, ansiContent, logContent, } from "./content.tsx";
|
|
2
|
+
export type { LevelDetector, LevelMatch, LogLevel } from "./parse/levels.ts";
|
|
3
|
+
export type { AnsiViewProps } from "./view/ansi-view.tsx";
|
|
4
|
+
export { AnsiView } from "./view/ansi-view.tsx";
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,sBAAsB,EACtB,WAAW,EACX,UAAU,GACX,MAAM,eAAe,CAAC;AACvB,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC7E,YAAY,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,sBAAsB,EACtB,WAAW,EACX,UAAU,GACX,MAAM,cAAe,CAAC;AAGvB,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAsB,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { createLevelTracker, type LevelCounts, type LevelTracker, } from "./level-tracker.ts";
|
|
2
|
+
export { detectLevel, LEVEL_INHERITED, LEVELS, type LevelDetector, type LevelMatch, type LogLevel, levelCode, levelOfCode, } from "./levels.ts";
|
|
3
|
+
export { PALETTE_NAMES, xterm256 } from "./palette.ts";
|
|
4
|
+
export { createAnsiParser, parseAnsi, stripAnsi } from "./parser.ts";
|
|
5
|
+
export type { AnsiColor, AnsiLine, AnsiParse, AnsiParser, AnsiStyle, UnderlineStyle, } from "./types.ts";
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/parse/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,KAAK,WAAW,EAChB,KAAK,YAAY,GAClB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,WAAW,EACX,eAAe,EACf,MAAM,EACN,KAAK,aAAa,EAClB,KAAK,UAAU,EACf,KAAK,QAAQ,EACb,SAAS,EACT,WAAW,GACZ,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACrE,YAAY,EACV,SAAS,EACT,QAAQ,EACR,SAAS,EACT,UAAU,EACV,SAAS,EACT,cAAc,GACf,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { createLevelTracker, } from "./level-tracker.js";
|
|
2
|
+
export { detectLevel, LEVEL_INHERITED, LEVELS, levelCode, levelOfCode, } from "./levels.js";
|
|
3
|
+
export { PALETTE_NAMES, xterm256 } from "./palette.js";
|
|
4
|
+
export { createAnsiParser, parseAnsi, stripAnsi } from "./parser.js";
|
|
5
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/parse/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,GAGnB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,WAAW,EACX,eAAe,EACf,MAAM,EAIN,SAAS,EACT,WAAW,GACZ,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type LevelDetector, type LogLevel } from "./levels.ts";
|
|
2
|
+
import type { AnsiLine } from "./types.ts";
|
|
3
|
+
export type LevelCounts = Readonly<Record<LogLevel | "other", number>>;
|
|
4
|
+
export interface LevelTracker {
|
|
5
|
+
/** Classifies `lines[from…to)`; `from` must be where the last call stopped. */
|
|
6
|
+
push(lines: readonly Pick<AnsiLine, "text">[], from: number, to: number): void;
|
|
7
|
+
/** One code per line: 0 none, 1–6 `LEVELS`, `| LEVEL_INHERITED` inherited. */
|
|
8
|
+
readonly levels: Uint8Array;
|
|
9
|
+
readonly count: number;
|
|
10
|
+
/** An inherited line counts under its level. */
|
|
11
|
+
readonly counts: LevelCounts;
|
|
12
|
+
}
|
|
13
|
+
export declare function createLevelTracker(detect?: LevelDetector): LevelTracker;
|
|
14
|
+
//# sourceMappingURL=level-tracker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"level-tracker.d.ts","sourceRoot":"","sources":["../../src/parse/level-tracker.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,aAAa,EAElB,KAAK,QAAQ,EAEd,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C,MAAM,MAAM,WAAW,GAAG,QAAQ,CAAC,MAAM,CAAC,QAAQ,GAAG,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC;AAEvE,MAAM,WAAW,YAAY;IAC3B,+EAA+E;IAC/E,IAAI,CACF,KAAK,EAAE,SAAS,IAAI,CAAC,QAAQ,EAAE,MAAM,CAAC,EAAE,EACxC,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,GACT,IAAI,CAAC;IACR,8EAA8E;IAC9E,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,gDAAgD;IAChD,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;CAC9B;AAmGD,wBAAgB,kBAAkB,CAChC,MAAM,GAAE,aAA2B,GAClC,YAAY,CAEd"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { detectLevel, LEVEL_INHERITED, levelCode, } from "./levels.js";
|
|
2
|
+
const CONTINUATION = /^(?:[ \t]+\S|Caused by:|Suppressed:|\.\.\. \d+ more|[\w$]+(?:\.[\w$]+)*(?:Exception|Error|Throwable)(?::|\s|$))/;
|
|
3
|
+
const COUNT_KEYS = [
|
|
4
|
+
"other",
|
|
5
|
+
"fatal",
|
|
6
|
+
"error",
|
|
7
|
+
"warn",
|
|
8
|
+
"info",
|
|
9
|
+
"debug",
|
|
10
|
+
"trace",
|
|
11
|
+
];
|
|
12
|
+
class Tracker {
|
|
13
|
+
buffer = new Uint8Array(1024);
|
|
14
|
+
view = this.buffer.subarray(0, 0);
|
|
15
|
+
tally = new Uint32Array(COUNT_KEYS.length);
|
|
16
|
+
snapshot = countsOf(this.tally);
|
|
17
|
+
reported = false;
|
|
18
|
+
detect;
|
|
19
|
+
constructor(detect) {
|
|
20
|
+
this.detect = detect;
|
|
21
|
+
}
|
|
22
|
+
get levels() {
|
|
23
|
+
return this.view;
|
|
24
|
+
}
|
|
25
|
+
get count() {
|
|
26
|
+
return this.view.length;
|
|
27
|
+
}
|
|
28
|
+
get counts() {
|
|
29
|
+
return this.snapshot;
|
|
30
|
+
}
|
|
31
|
+
push(lines, from, to) {
|
|
32
|
+
const count = this.view.length;
|
|
33
|
+
if (from !== count) {
|
|
34
|
+
throw new RangeError(`LevelTracker: push from ${from}, but ${count} lines are classified`);
|
|
35
|
+
}
|
|
36
|
+
if (to <= from)
|
|
37
|
+
return;
|
|
38
|
+
if (to > this.buffer.length) {
|
|
39
|
+
let size = this.buffer.length * 2;
|
|
40
|
+
while (size < to)
|
|
41
|
+
size *= 2;
|
|
42
|
+
const grown = new Uint8Array(size);
|
|
43
|
+
grown.set(this.view);
|
|
44
|
+
this.buffer = grown;
|
|
45
|
+
}
|
|
46
|
+
const buffer = this.buffer;
|
|
47
|
+
let prev = count > 0 ? buffer[count - 1] : 0;
|
|
48
|
+
for (let i = from; i < to; i++) {
|
|
49
|
+
const text = lines[i].text;
|
|
50
|
+
const match = this.classify(text);
|
|
51
|
+
// levelCode gives 0 for a level a custom detector made up.
|
|
52
|
+
let code = match ? levelCode(match.level) : 0;
|
|
53
|
+
if (code === 0 && prev !== 0 && CONTINUATION.test(text)) {
|
|
54
|
+
code = prev | LEVEL_INHERITED;
|
|
55
|
+
}
|
|
56
|
+
buffer[i] = code;
|
|
57
|
+
this.tally[code & ~LEVEL_INHERITED]++;
|
|
58
|
+
prev = code;
|
|
59
|
+
}
|
|
60
|
+
this.view = buffer.subarray(0, to);
|
|
61
|
+
// A new object per push, so that a store publishing it sees a change.
|
|
62
|
+
this.snapshot = countsOf(this.tally);
|
|
63
|
+
}
|
|
64
|
+
classify(text) {
|
|
65
|
+
try {
|
|
66
|
+
return this.detect(text);
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
if (!this.reported) {
|
|
70
|
+
this.reported = true;
|
|
71
|
+
console.error("LevelTracker: the level detector threw", error);
|
|
72
|
+
}
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
function countsOf(tally) {
|
|
78
|
+
const counts = {};
|
|
79
|
+
COUNT_KEYS.forEach((key, code) => {
|
|
80
|
+
counts[key] = tally[code];
|
|
81
|
+
});
|
|
82
|
+
return counts;
|
|
83
|
+
}
|
|
84
|
+
export function createLevelTracker(detect = detectLevel) {
|
|
85
|
+
return new Tracker(detect);
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=level-tracker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"level-tracker.js","sourceRoot":"","sources":["../../src/parse/level-tracker.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export type LogLevel = "fatal" | "error" | "warn" | "info" | "debug" | "trace";
|
|
2
|
+
/** `from`/`to`: UTF-16 offsets of the level word in the line's text, when there is one to colour. */
|
|
3
|
+
export interface LevelMatch {
|
|
4
|
+
level: LogLevel;
|
|
5
|
+
from?: number;
|
|
6
|
+
to?: number;
|
|
7
|
+
}
|
|
8
|
+
export type LevelDetector = (text: string) => LevelMatch | null;
|
|
9
|
+
/** In code order: a level's code is its index + 1, and 0 is no level. */
|
|
10
|
+
export declare const LEVELS: readonly LogLevel[];
|
|
11
|
+
/** Set on a level code that a continuation line took from the line above. */
|
|
12
|
+
export declare const LEVEL_INHERITED = 128;
|
|
13
|
+
export declare function levelCode(level: LogLevel): number;
|
|
14
|
+
export declare function levelOfCode(code: number): LogLevel | null;
|
|
15
|
+
/** How much of a line the detector reads. */
|
|
16
|
+
export declare const LEVEL_WINDOW = 256;
|
|
17
|
+
/** The default detector. Pure; reads at most the first 256 UTF-16 units. */
|
|
18
|
+
export declare function detectLevel(text: string): LevelMatch | null;
|
|
19
|
+
//# sourceMappingURL=levels.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"levels.d.ts","sourceRoot":"","sources":["../../src/parse/levels.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,CAAC;AAE/E,qGAAqG;AACrG,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,QAAQ,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,EAAE,CAAC,EAAE,MAAM,CAAC;CACb;AAED,MAAM,MAAM,aAAa,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,UAAU,GAAG,IAAI,CAAC;AAEhE,yEAAyE;AACzE,eAAO,MAAM,MAAM,EAAE,SAAS,QAAQ,EAOrC,CAAC;AAEF,6EAA6E;AAC7E,eAAO,MAAM,eAAe,MAAO,CAAC;AAEpC,wBAAgB,SAAS,CAAC,KAAK,EAAE,QAAQ,GAAG,MAAM,CAEjD;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAEzD;AAED,6CAA6C;AAC7C,eAAO,MAAM,YAAY,MAAM,CAAC;AAyHhC,4EAA4E;AAC5E,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAgD3D"}
|