@fusion.dev/helix-headless 0.199.1 → 0.201.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.
@@ -177,6 +177,8 @@ registerElementEvents({
177
177
  'helix-reorder',
178
178
  'input',
179
179
  ],
180
+ 'helix-headless-log': ['helix-before-pin', 'helix-expand', 'helix-pin'],
181
+ 'helix-headless-log-group': ['helix-before-close', 'helix-before-open', 'toggle'],
180
182
  'helix-headless-map': [
181
183
  'change',
182
184
  'helix-activate',
package/dist/index.d.ts CHANGED
@@ -61,6 +61,7 @@ export * from './line-chart/index.js';
61
61
  export * from './list/index.js';
62
62
  export * from './listbox/index.js';
63
63
  export * from './loader/index.js';
64
+ export * from './log/index.js';
64
65
  export * from './map/index.js';
65
66
  export * from './megamenu/index.js';
66
67
  export * from './menu/filter/index.js';
package/dist/index.js CHANGED
@@ -52,6 +52,7 @@ export * from './line-chart/index.js';
52
52
  export * from './list/index.js';
53
53
  export * from './listbox/index.js';
54
54
  export * from './loader/index.js';
55
+ export * from './log/index.js';
55
56
  export * from './map/index.js';
56
57
  export * from './megamenu/index.js';
57
58
  export * from './menu/filter/index.js';
@@ -0,0 +1,63 @@
1
+ /**
2
+ * The color and weight a run of a log line is drawn in, read from the ANSI
3
+ * escape codes a terminal program writes.
4
+ *
5
+ * **Only Select Graphic Rendition is drawn.** Every other escape sequence, a
6
+ * cursor move, a line erase, a window title, is taken out of the text and
7
+ * dropped, because a log is a record of what was written and has no cursor
8
+ * to move. A sequence cut off at the end of the text is dropped too.
9
+ */
10
+ /** How one run of text is drawn. Absent fields are the log's own defaults. */
11
+ export interface LogStyle {
12
+ /** The background: one of `ANSI_NAMES`, or a CSS color. */
13
+ background?: string;
14
+ /** Bold, or bright on a terminal that draws it that way. */
15
+ bold?: boolean;
16
+ /** The text color: one of `ANSI_NAMES`, or a CSS color. */
17
+ color?: string;
18
+ /** Faint. */
19
+ dim?: boolean;
20
+ /** Foreground and background swapped. */
21
+ inverse?: boolean;
22
+ /** Italic. */
23
+ italic?: boolean;
24
+ /** Struck through. */
25
+ strike?: boolean;
26
+ /** Underlined. */
27
+ underline?: boolean;
28
+ }
29
+ /** A stretch of a line drawn one way. */
30
+ export interface LogRun extends LogStyle {
31
+ /** The text, with every escape sequence taken out. */
32
+ text: string;
33
+ }
34
+ /**
35
+ * The sixteen colors a terminal names, in the order SGR numbers them: the
36
+ * eight at `30` to `37`, then their bright forms at `90` to `97`.
37
+ *
38
+ * A run in one of them carries the name, and is drawn in the `--log-ansi-`
39
+ * color of that name, so the stylesheet says what red is in its theme. The
40
+ * palette's own index above fifteen is a fact about the color and is carried
41
+ * as the color itself.
42
+ */
43
+ export declare const ANSI_NAMES: readonly ['black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white', 'bright-black', 'bright-red', 'bright-green', 'bright-yellow', 'bright-blue', 'bright-magenta', 'bright-cyan', 'bright-white'];
44
+ /**
45
+ * Entry `index` of the 256-color palette: a name below 16, and the CSS color
46
+ * of the cube to 231 and of the ramp of grays from 232 to 255.
47
+ */
48
+ export declare const paletteColor: (index: number) => string | undefined;
49
+ /** The style `codes` leave behind, applied over `style`. */
50
+ export declare const applySgr: (style: LogStyle, codes: readonly number[]) => LogStyle;
51
+ /**
52
+ * Splits one line of text into runs, starting from `style`, and says what
53
+ * style the line ends in, which is where the next line starts.
54
+ *
55
+ * A run with no style left is plain. Adjacent runs that draw the same are
56
+ * merged, so a line that resets to the style it already had stays one run.
57
+ */
58
+ export declare const parseAnsi: (text: string, style?: LogStyle) => {
59
+ runs: LogRun[];
60
+ style: LogStyle;
61
+ };
62
+ /** The text of a line with every escape sequence taken out. */
63
+ export declare const stripAnsi: (text: string) => string;
@@ -0,0 +1,163 @@
1
+ export const ANSI_NAMES = [
2
+ 'black',
3
+ 'red',
4
+ 'green',
5
+ 'yellow',
6
+ 'blue',
7
+ 'magenta',
8
+ 'cyan',
9
+ 'white',
10
+ 'bright-black',
11
+ 'bright-red',
12
+ 'bright-green',
13
+ 'bright-yellow',
14
+ 'bright-blue',
15
+ 'bright-magenta',
16
+ 'bright-cyan',
17
+ 'bright-white',
18
+ ];
19
+ const named = (index) => ANSI_NAMES[index] ?? 'white';
20
+ const CUBE = [0, 95, 135, 175, 215, 255];
21
+ export const paletteColor = (index) => {
22
+ if (!Number.isInteger(index) || index < 0 || index > 255) {
23
+ return undefined;
24
+ }
25
+ if (index < 16) {
26
+ return named(index);
27
+ }
28
+ if (index < 232) {
29
+ const cube = index - 16;
30
+ const red = CUBE[Math.floor(cube / 36)];
31
+ const green = CUBE[Math.floor(cube / 6) % 6];
32
+ const blue = CUBE[cube % 6];
33
+ return `rgb(${red} ${green} ${blue})`;
34
+ }
35
+ const gray = 8 + (index - 232) * 10;
36
+ return `rgb(${gray} ${gray} ${gray})`;
37
+ };
38
+ const extended = (codes, at) => {
39
+ const form = codes[at];
40
+ if (form === 5) {
41
+ return { color: paletteColor(codes[at + 1] ?? -1), used: 2 };
42
+ }
43
+ if (form === 2) {
44
+ const [red, green, blue] = [codes[at + 1], codes[at + 2], codes[at + 3]];
45
+ const valid = [red, green, blue].every((value) => value !== undefined && Number.isInteger(value) && value >= 0 && value <= 255);
46
+ return { color: valid ? `rgb(${red} ${green} ${blue})` : undefined, used: 4 };
47
+ }
48
+ return { color: undefined, used: 1 };
49
+ };
50
+ export const applySgr = (style, codes) => {
51
+ const next = { ...style };
52
+ const list = codes.length === 0 ? [0] : codes;
53
+ for (let at = 0; at < list.length; at += 1) {
54
+ const code = list[at] ?? 0;
55
+ if (code === 0) {
56
+ for (const key of Object.keys(next)) {
57
+ delete next[key];
58
+ }
59
+ }
60
+ else if (code === 1) {
61
+ next.bold = true;
62
+ }
63
+ else if (code === 2) {
64
+ next.dim = true;
65
+ }
66
+ else if (code === 3) {
67
+ next.italic = true;
68
+ }
69
+ else if (code === 4) {
70
+ next.underline = true;
71
+ }
72
+ else if (code === 7) {
73
+ next.inverse = true;
74
+ }
75
+ else if (code === 9) {
76
+ next.strike = true;
77
+ }
78
+ else if (code === 22) {
79
+ delete next.bold;
80
+ delete next.dim;
81
+ }
82
+ else if (code === 23) {
83
+ delete next.italic;
84
+ }
85
+ else if (code === 24) {
86
+ delete next.underline;
87
+ }
88
+ else if (code === 27) {
89
+ delete next.inverse;
90
+ }
91
+ else if (code === 29) {
92
+ delete next.strike;
93
+ }
94
+ else if (code >= 30 && code <= 37) {
95
+ next.color = named(code - 30);
96
+ }
97
+ else if (code === 38 || code === 48) {
98
+ const { color, used } = extended(list, at + 1);
99
+ if (color !== undefined) {
100
+ next[code === 38 ? 'color' : 'background'] = color;
101
+ }
102
+ at += used;
103
+ }
104
+ else if (code === 39) {
105
+ delete next.color;
106
+ }
107
+ else if (code >= 40 && code <= 47) {
108
+ next.background = named(code - 40);
109
+ }
110
+ else if (code === 49) {
111
+ delete next.background;
112
+ }
113
+ else if (code >= 90 && code <= 97) {
114
+ next.color = named(code - 90 + 8);
115
+ }
116
+ else if (code >= 100 && code <= 107) {
117
+ next.background = named(code - 100 + 8);
118
+ }
119
+ }
120
+ return next;
121
+ };
122
+ const plain = (style) => Object.keys(style).length === 0;
123
+ const same = (a, b) => {
124
+ const keys = new Set([...Object.keys(a), ...Object.keys(b)]);
125
+ keys.delete('text');
126
+ for (const key of keys) {
127
+ if (a[key] !== b[key]) {
128
+ return false;
129
+ }
130
+ }
131
+ return true;
132
+ };
133
+ const SEQUENCE = /(?:\x1b\[|\x9b)([0-?]*)[ -/]*([@-~])|\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)|\x1b[@-Z\\-_]/g;
134
+ const UNFINISHED = /(?:\x1b(?:\[[0-?]*[ -/]*|\][^\x07]*)?|\x9b[0-?]*[ -/]*)$/;
135
+ export const parseAnsi = (text, style = {}) => {
136
+ const runs = [];
137
+ let current = style;
138
+ let from = 0;
139
+ const push = (chunk) => {
140
+ if (chunk === '') {
141
+ return;
142
+ }
143
+ const last = runs.at(-1);
144
+ if (last && same(last, current)) {
145
+ last.text += chunk;
146
+ }
147
+ else {
148
+ runs.push(plain(current) ? { text: chunk } : { ...current, text: chunk });
149
+ }
150
+ };
151
+ const body = text.replace(UNFINISHED, '');
152
+ for (const match of body.matchAll(SEQUENCE)) {
153
+ push(body.slice(from, match.index));
154
+ from = match.index + match[0].length;
155
+ const [, parameters, final] = match;
156
+ if (final === 'm' && parameters !== undefined && !/[<=>?]/.test(parameters)) {
157
+ current = applySgr(current, parameters === '' ? [] : parameters.split(/[;:]/).map((code) => Number(code || 0)));
158
+ }
159
+ }
160
+ push(body.slice(from));
161
+ return { runs, style: current };
162
+ };
163
+ export const stripAnsi = (text) => text.replace(UNFINISHED, '').replace(SEQUENCE, '');
@@ -0,0 +1,99 @@
1
+ import { type LogRun } from './ansi.js';
2
+ /** How much a line matters, which a stylesheet draws and a screen reader may hear. */
3
+ export type LogLevel = 'debug' | 'error' | 'info' | 'warn';
4
+ /** Where a step of the run stands. */
5
+ export type LogStatus = 'done' | 'failed' | 'pending' | 'running' | 'skipped';
6
+ /** One line of output, as `lines` holds it and `parse` returns it. */
7
+ export interface LogLine {
8
+ /** When it was written: milliseconds since the epoch, or anything `Date` parses. */
9
+ at?: number | string;
10
+ /** The `id` of the group in `groups` the line belongs to. */
11
+ group?: string;
12
+ /** How much it matters. Absent is `info`. */
13
+ level?: LogLevel;
14
+ /** What was written, with any ANSI escape codes still in it. */
15
+ text: string;
16
+ }
17
+ /** One step of the run, which folds the lines that name its `id`. */
18
+ export interface LogGroup {
19
+ /** How long the step took, as it should read, such as `12s`. */
20
+ duration?: string;
21
+ /** What the step is called. */
22
+ heading: string;
23
+ /** What its lines name in `group`. */
24
+ id: string;
25
+ /**
26
+ * Whether it is open. Read the first time the log sees the group, and again
27
+ * whenever it changes, so a page can open the step that failed.
28
+ */
29
+ open?: boolean;
30
+ /** Where it stands. */
31
+ status?: LogStatus;
32
+ }
33
+ /**
34
+ * Reads one finished line of written text and says what it is.
35
+ *
36
+ * Return the fields to set, such as a `level` read off a prefix or a `group`
37
+ * read off a marker, and `text` to show something else. Return `null` to drop
38
+ * the line, such as a marker that only opens a group.
39
+ */
40
+ export type LogParser = (text: string) => Partial<LogLine> | null | undefined;
41
+ /**
42
+ * The text of a run, kept as lines.
43
+ *
44
+ * **Written text arrives in pieces that do not end at line breaks**, so the
45
+ * line still being written is held apart and shown as it stands, the way a
46
+ * terminal shows a prompt or a progress bar before its line ends. A line is
47
+ * finished by `\n` or `\r\n`.
48
+ *
49
+ * **A carriage return on its own starts the line over.** A progress bar is
50
+ * one line rewritten a hundred times, and drawing each rewrite as a line of
51
+ * its own is the failure every log viewer is judged by. The text after the
52
+ * last `\r` replaces the line. A terminal overwrites only as many characters
53
+ * as were written, so `abc\rxy` reads `xyc` there and `xy` here; a program
54
+ * that redraws a line writes the whole line, which reads the same in both.
55
+ *
56
+ * **Color carries from line to line**, as it does in a terminal, so a block
57
+ * that opens with green and closes many lines later is green throughout. It
58
+ * carries through text a carriage return replaced as well.
59
+ */
60
+ export declare class LogBuffer {
61
+ /** The finished lines kept, oldest first. */
62
+ lines: LogLine[];
63
+ /** How many finished lines were let go from the start to stay within `limit`. */
64
+ dropped: number;
65
+ /** The most finished lines kept, or 0 to keep them all. */
66
+ limit: number;
67
+ /** Reads each finished line of written text. */
68
+ parse: LogParser | undefined;
69
+ /** The line still being written, as it reads now. */
70
+ private partial;
71
+ /** Whether the next text written starts the line over. */
72
+ private returned;
73
+ /** The style the line being written started in. */
74
+ private lineStyle;
75
+ /** The runs of each line, read once. */
76
+ private readonly runs;
77
+ /** The line `current` last returned, which stands until the text changes. */
78
+ private shown;
79
+ /**
80
+ * Adds text, finishing a line at every line break, and returns how many
81
+ * lines it finished.
82
+ */
83
+ write(chunk: string): number;
84
+ /** Replaces every line, starting a fresh buffer that keeps its `parse` and `limit`. */
85
+ replace(lines: readonly LogLine[]): void;
86
+ /** Lets every line go. */
87
+ clear(): void;
88
+ /** The line still being written, or undefined where the last line ended. */
89
+ current(): LogLine | undefined;
90
+ /**
91
+ * The runs a line is drawn in. A written line was read with the color the
92
+ * line before it left; a line handed over in `lines` starts plain.
93
+ */
94
+ runsOf(line: LogLine): LogRun[];
95
+ /** Finishes the line being written. */
96
+ private finish;
97
+ /** Lets the oldest lines go past `limit`, all at once. */
98
+ private trim;
99
+ }
@@ -0,0 +1,92 @@
1
+ import { parseAnsi } from './ansi.js';
2
+ export class LogBuffer {
3
+ constructor() {
4
+ this.lines = [];
5
+ this.dropped = 0;
6
+ this.limit = 0;
7
+ this.parse = undefined;
8
+ this.partial = '';
9
+ this.returned = false;
10
+ this.lineStyle = {};
11
+ this.runs = new WeakMap();
12
+ this.shown = undefined;
13
+ }
14
+ write(chunk) {
15
+ let finished = 0;
16
+ for (const token of chunk.split(/(\r\n|\n|\r)/)) {
17
+ if (token === '\n' || token === '\r\n') {
18
+ this.finish();
19
+ finished += 1;
20
+ }
21
+ else if (token === '\r') {
22
+ this.returned = true;
23
+ }
24
+ else if (token !== '') {
25
+ if (this.returned) {
26
+ this.lineStyle = parseAnsi(this.partial, this.lineStyle).style;
27
+ this.partial = token;
28
+ this.returned = false;
29
+ }
30
+ else {
31
+ this.partial += token;
32
+ }
33
+ }
34
+ }
35
+ this.trim();
36
+ return finished;
37
+ }
38
+ replace(lines) {
39
+ this.lines = [...lines];
40
+ this.dropped = 0;
41
+ this.partial = '';
42
+ this.returned = false;
43
+ this.lineStyle = {};
44
+ this.shown = undefined;
45
+ this.trim();
46
+ }
47
+ clear() {
48
+ this.replace([]);
49
+ }
50
+ current() {
51
+ if (this.partial === '') {
52
+ return undefined;
53
+ }
54
+ const group = this.lines.at(-1)?.group;
55
+ if (this.shown?.text !== this.partial || this.shown.group !== group) {
56
+ this.shown = group === undefined ? { text: this.partial } : { group, text: this.partial };
57
+ }
58
+ return this.shown;
59
+ }
60
+ runsOf(line) {
61
+ if (line === this.shown) {
62
+ return parseAnsi(line.text, this.lineStyle).runs;
63
+ }
64
+ const known = this.runs.get(line);
65
+ if (known) {
66
+ return known;
67
+ }
68
+ const { runs } = parseAnsi(line.text);
69
+ this.runs.set(line, runs);
70
+ return runs;
71
+ }
72
+ finish() {
73
+ const raw = this.partial;
74
+ const flow = parseAnsi(raw, this.lineStyle);
75
+ this.partial = '';
76
+ this.returned = false;
77
+ const read = this.parse?.(raw);
78
+ if (read !== null) {
79
+ const line = { ...read, text: read?.text ?? raw };
80
+ this.runs.set(line, line.text === raw ? flow.runs : parseAnsi(line.text, this.lineStyle).runs);
81
+ this.lines.push(line);
82
+ }
83
+ this.lineStyle = flow.style;
84
+ }
85
+ trim() {
86
+ const over = this.limit > 0 ? this.lines.length - this.limit : 0;
87
+ if (over > 0) {
88
+ this.lines.splice(0, over);
89
+ this.dropped += over;
90
+ }
91
+ }
92
+ }
@@ -0,0 +1,78 @@
1
+ import { HelixElement } from '@fusion.dev/helix-core';
2
+ import { type TemplateResult } from 'lit';
3
+ import type { LogStatus } from '../buffer.js';
4
+ declare const HelixHeadlessLogGroup_base: (new (...args: any[]) => import("@fusion.dev/helix-core").MixinControllerOpenInterface) & typeof HelixElement;
5
+ /**
6
+ * @summary One step of a log, which folds its lines under a heading with its status and how long it took.
7
+ *
8
+ * @description
9
+ * A group is a disclosure: its heading is a button that shows and hides the
10
+ * lines inside it, and says whether they are shown with `aria-expanded`. Put
11
+ * `Log.Line` elements inside it, and nest groups for steps within steps.
12
+ *
13
+ * `status` draws a mark before the heading, and a screen reader hears the
14
+ * status after the heading (`status-label`). `duration` stands at the end of
15
+ * the heading, as you write it, such as `12s`.
16
+ *
17
+ * A group starts shut. Write `default-open` on the step a reader should see,
18
+ * such as the one that failed.
19
+ *
20
+ * @since 0.199.0
21
+ * @status experimental
22
+ * @documentation https://fusion.dev/helix/components/log
23
+ *
24
+ * @slot - The lines of the step, and any groups inside it.
25
+ * @slot heading - The heading, where it holds more than text. Defaults to `heading`.
26
+ *
27
+ * @event {HelixBeforeCloseEvent} helix-before-close - Fired before the group closes. Cancelable.
28
+ * @event {HelixBeforeOpenEvent} helix-before-open - Fired before the group opens. Cancelable.
29
+ * @event {ToggleEvent} toggle - Fired after the group opens or closes, with `newState` and `oldState`.
30
+ *
31
+ * @csspart content - The lines, hidden while the group is shut.
32
+ * @csspart duration - How long the step took.
33
+ * @csspart heading - The heading.
34
+ * @csspart mark - The caret that turns as the group opens, an `<svg>` you size.
35
+ * @csspart status - The mark for the status, an `<svg>` you size.
36
+ * @csspart summary - The button that opens and closes the group.
37
+ *
38
+ * @cssstate open - The group shows its lines.
39
+ */
40
+ export declare class HelixHeadlessLogGroup extends HelixHeadlessLogGroup_base {
41
+ protected static markRules: string[];
42
+ /** The key this family's words are registered under, its styled tag. */
43
+ static words: string;
44
+ /** How long the step took, as it should read, such as `12s`. */
45
+ duration: string;
46
+ /** What the step is called. The `heading` slot replaces it. */
47
+ heading: string;
48
+ /**
49
+ * @internal Set by a log that draws the group from data: the group stands
50
+ * for a heading alone, its lines being drawn beside it.
51
+ */
52
+ logDrawn: boolean;
53
+ /** Where the step stands, which draws a mark before the heading. */
54
+ status?: LogStatus;
55
+ /**
56
+ * @internal Read by the log to find its groups among its children, which is
57
+ * cheaper than `instanceof`.
58
+ */
59
+ readonly isLogGroup = true;
60
+ /**
61
+ * What a screen reader hears for the status, after the heading.
62
+ * `{status}` is one of `pending`, `running`, `done`, `failed`, and `skipped`.
63
+ */
64
+ statusLabel: string;
65
+ private readonly handlePress;
66
+ /**
67
+ * The words in the button are separated by spaces in the markup, since a
68
+ * name is read off inline boxes with nothing between them: without them a
69
+ * screen reader heard "Installfailed12s". A flex row draws none of them.
70
+ */
71
+ protected render(): TemplateResult;
72
+ }
73
+ declare global {
74
+ interface HTMLElementTagNameMap {
75
+ 'helix-headless-log-group': HelixHeadlessLogGroup;
76
+ }
77
+ }
78
+ export {};
@@ -0,0 +1,76 @@
1
+ import { __decorate } from "tslib";
2
+ import { customElement, formatWords, HelixElement, MixinControllerOpen, SPOKEN_RULE, words, } from '@fusion.dev/helix-core';
3
+ import { caretDown, checkCircleFill, circle, clock, minus, xCircleFill, } from '@fusion.dev/helix-icons';
4
+ import { html, nothing } from 'lit';
5
+ import { property } from 'lit/decorators.js';
6
+ import { unsafeSVG } from 'lit/directives/unsafe-svg.js';
7
+ const MARKS = {
8
+ done: checkCircleFill,
9
+ failed: xCircleFill,
10
+ pending: circle,
11
+ running: clock,
12
+ skipped: minus,
13
+ };
14
+ let HelixHeadlessLogGroup = class HelixHeadlessLogGroup extends MixinControllerOpen(HelixElement) {
15
+ constructor() {
16
+ super(...arguments);
17
+ this.duration = '';
18
+ this.heading = '';
19
+ this.logDrawn = false;
20
+ this.isLogGroup = true;
21
+ this.statusLabel = '{status, select, pending {waiting} running {running} done {finished} failed {failed} skipped {skipped} other {}}';
22
+ this.handlePress = () => {
23
+ this.toggleOpen();
24
+ };
25
+ }
26
+ static { this.markRules = [SPOKEN_RULE]; }
27
+ static { this.words = 'helix-log'; }
28
+ render() {
29
+ const status = this.status;
30
+ return html `<button
31
+ part="summary"
32
+ id="summary"
33
+ type="button"
34
+ aria-expanded=${this.open ? 'true' : 'false'}
35
+ aria-controls=${this.logDrawn ? nothing : 'content'}
36
+ @click=${this.handlePress}
37
+ ><svg part="mark" aria-hidden="true" fill="currentColor" viewBox=${caretDown.viewBox}>
38
+ ${unsafeSVG(caretDown.content)}
39
+ </svg>${status && Object.hasOwn(MARKS, status)
40
+ ? html `<svg part="status" aria-hidden="true" fill="currentColor" viewBox=${MARKS[status].viewBox}>
41
+ ${unsafeSVG(MARKS[status].content)}
42
+ </svg>`
43
+ : nothing}<span part="heading"><slot name="heading">${this.heading}</slot></span>${status
44
+ ? html ` <span data-spoken>${formatWords(this, 'statusLabel', { status })}</span>`
45
+ : nothing}${this.duration === '' ? nothing : html ` <span part="duration">${this.duration}</span>`}</button
46
+ >${this.logDrawn
47
+ ? nothing
48
+ : html `<div
49
+ part="content"
50
+ id="content"
51
+ role="group"
52
+ aria-labelledby="summary"
53
+ ?hidden=${!this.open}
54
+ ><slot></slot></div>`}`;
55
+ }
56
+ };
57
+ __decorate([
58
+ property()
59
+ ], HelixHeadlessLogGroup.prototype, "duration", void 0);
60
+ __decorate([
61
+ property()
62
+ ], HelixHeadlessLogGroup.prototype, "heading", void 0);
63
+ __decorate([
64
+ property({ attribute: false })
65
+ ], HelixHeadlessLogGroup.prototype, "logDrawn", void 0);
66
+ __decorate([
67
+ property({ reflect: true })
68
+ ], HelixHeadlessLogGroup.prototype, "status", void 0);
69
+ __decorate([
70
+ words,
71
+ property({ attribute: 'status-label' })
72
+ ], HelixHeadlessLogGroup.prototype, "statusLabel", void 0);
73
+ HelixHeadlessLogGroup = __decorate([
74
+ customElement('helix-headless-log-group')
75
+ ], HelixHeadlessLogGroup);
76
+ export { HelixHeadlessLogGroup };
@@ -0,0 +1,5 @@
1
+ export { type LogRun, type LogStyle, parseAnsi, stripAnsi } from './ansi.js';
2
+ export type { LogGroup, LogLevel, LogLine, LogParser, LogStatus } from './buffer.js';
3
+ export { HelixHeadlessLogGroup } from './group/log-group.js';
4
+ export { HelixHeadlessLogLine, type LogTimes } from './line/log-line.js';
5
+ export { HelixHeadlessLog, type LogScrollOptions, type LogSource } from './root/log-root.js';
@@ -0,0 +1,4 @@
1
+ export { parseAnsi, stripAnsi } from './ansi.js';
2
+ export { HelixHeadlessLogGroup } from './group/log-group.js';
3
+ export { HelixHeadlessLogLine } from './line/log-line.js';
4
+ export { HelixHeadlessLog } from './root/log-root.js';