lognal 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 +21 -0
- package/README.md +99 -0
- package/dist/core/filter.d.ts +29 -0
- package/dist/core/filter.js +82 -0
- package/dist/core/layout/entry-lines.d.ts +16 -0
- package/dist/core/layout/entry-lines.js +120 -0
- package/dist/core/layout/layout.d.ts +152 -0
- package/dist/core/layout/layout.js +784 -0
- package/dist/core/layout/row-index.d.ts +33 -0
- package/dist/core/layout/row-index.js +99 -0
- package/dist/core/layout/types.d.ts +128 -0
- package/dist/core/layout/types.js +8 -0
- package/dist/core/store.d.ts +96 -0
- package/dist/core/store.js +204 -0
- package/dist/core/text/ansi.d.ts +20 -0
- package/dist/core/text/ansi.js +187 -0
- package/dist/core/text/graphemes.d.ts +17 -0
- package/dist/core/text/graphemes.js +70 -0
- package/dist/core/text/line-splitter.d.ts +18 -0
- package/dist/core/text/line-splitter.js +56 -0
- package/dist/core/text/measure.d.ts +10 -0
- package/dist/core/text/measure.js +38 -0
- package/dist/core/text/shape.d.ts +24 -0
- package/dist/core/text/shape.js +220 -0
- package/dist/core/text/unicode-width-data.d.ts +54 -0
- package/dist/core/text/unicode-width-data.js +227 -0
- package/dist/core/text/width.d.ts +20 -0
- package/dist/core/text/width.js +157 -0
- package/dist/core/text/wrap.d.ts +14 -0
- package/dist/core/text/wrap.js +94 -0
- package/dist/core/time.d.ts +10 -0
- package/dist/core/time.js +24 -0
- package/dist/core/types.d.ts +131 -0
- package/dist/core/types.js +9 -0
- package/dist/core/value/preview.d.ts +16 -0
- package/dist/core/value/preview.js +207 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.js +24 -0
- package/dist/lognal.css +589 -0
- package/dist/react/LogViewer.d.ts +32 -0
- package/dist/react/LogViewer.js +183 -0
- package/dist/react/index.d.ts +1 -0
- package/dist/react/index.js +2 -0
- package/dist/renderer/canvas/canvas-renderer.d.ts +45 -0
- package/dist/renderer/canvas/canvas-renderer.js +464 -0
- package/dist/renderer/canvas/palette.d.ts +6 -0
- package/dist/renderer/canvas/palette.js +25 -0
- package/dist/renderer/theme.d.ts +3 -0
- package/dist/renderer/theme.js +52 -0
- package/dist/renderer/types.d.ts +82 -0
- package/dist/renderer/types.js +1 -0
- package/dist/sources/console/format.d.ts +32 -0
- package/dist/sources/console/format.js +189 -0
- package/dist/sources/console/hook.d.ts +27 -0
- package/dist/sources/console/hook.js +94 -0
- package/dist/sources/console/recorder.d.ts +41 -0
- package/dist/sources/console/recorder.js +239 -0
- package/dist/sources/console/snapshot.d.ts +22 -0
- package/dist/sources/console/snapshot.js +547 -0
- package/dist/sources/console/table.d.ts +9 -0
- package/dist/sources/console/table.js +87 -0
- package/dist/sources/text/encoding.d.ts +11 -0
- package/dist/sources/text/encoding.js +59 -0
- package/dist/sources/text/follow-file.d.ts +49 -0
- package/dist/sources/text/follow-file.js +109 -0
- package/dist/sources/text/read-file.d.ts +63 -0
- package/dist/sources/text/read-file.js +82 -0
- package/dist/viewer/icons.d.ts +14 -0
- package/dist/viewer/icons.js +30 -0
- package/dist/viewer/input-line.d.ts +46 -0
- package/dist/viewer/input-line.js +144 -0
- package/dist/viewer/labels.d.ts +35 -0
- package/dist/viewer/labels.js +61 -0
- package/dist/viewer/scrollbar.d.ts +27 -0
- package/dist/viewer/scrollbar.js +143 -0
- package/dist/viewer/theme.d.ts +15 -0
- package/dist/viewer/theme.js +105 -0
- package/dist/viewer/viewer.d.ts +217 -0
- package/dist/viewer/viewer.js +1201 -0
- package/package.json +92 -0
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
export const EN_LABELS = {
|
|
2
|
+
viewer: 'Log viewer',
|
|
3
|
+
toolbar: 'Log viewer tools',
|
|
4
|
+
follow: 'Follow new logs',
|
|
5
|
+
clear: 'Clear logs',
|
|
6
|
+
scrollToTop: 'Scroll to top',
|
|
7
|
+
scrollToBottom: 'Scroll to bottom',
|
|
8
|
+
wrap: 'Wrap long lines',
|
|
9
|
+
filter: 'Filter',
|
|
10
|
+
invalidFilter: 'The filter is not a valid pattern',
|
|
11
|
+
levels: 'Log levels',
|
|
12
|
+
levelAll: 'All levels',
|
|
13
|
+
levelLog: 'Log and above',
|
|
14
|
+
levelInfo: 'Info and above',
|
|
15
|
+
levelWarn: 'Warnings and errors',
|
|
16
|
+
levelError: 'Errors only',
|
|
17
|
+
input: 'Command',
|
|
18
|
+
inputPlaceholder: 'Type a command',
|
|
19
|
+
newLogs: 'New logs',
|
|
20
|
+
entryList: 'Visible log entries',
|
|
21
|
+
following: 'Following',
|
|
22
|
+
paused: 'Paused',
|
|
23
|
+
entries: (shown, total, format) => {
|
|
24
|
+
const noun = total === 1 ? 'entry' : 'entries';
|
|
25
|
+
return shown === total
|
|
26
|
+
? `${format(total)} ${noun}`
|
|
27
|
+
: `${format(shown)} of ${format(total)} ${noun}`;
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
export const KO_LABELS = {
|
|
31
|
+
viewer: '로그 뷰어',
|
|
32
|
+
toolbar: '로그 뷰어 도구',
|
|
33
|
+
follow: '새 로그 따라가기',
|
|
34
|
+
clear: '로그 지우기',
|
|
35
|
+
scrollToTop: '맨 위로 이동',
|
|
36
|
+
scrollToBottom: '맨 아래로 이동',
|
|
37
|
+
wrap: '긴 줄 바꾸기',
|
|
38
|
+
filter: '필터',
|
|
39
|
+
invalidFilter: '필터 패턴이 올바르지 않습니다',
|
|
40
|
+
levels: '로그 수준',
|
|
41
|
+
levelAll: '모든 수준',
|
|
42
|
+
levelLog: '로그 이상',
|
|
43
|
+
levelInfo: '정보 이상',
|
|
44
|
+
levelWarn: '경고와 오류',
|
|
45
|
+
levelError: '오류만',
|
|
46
|
+
input: '명령',
|
|
47
|
+
inputPlaceholder: '명령을 입력하세요',
|
|
48
|
+
newLogs: '새 로그',
|
|
49
|
+
entryList: '화면에 보이는 로그',
|
|
50
|
+
following: '따라가는 중',
|
|
51
|
+
paused: '멈춤',
|
|
52
|
+
entries: (shown, total, format) => {
|
|
53
|
+
return shown === total
|
|
54
|
+
? `로그 ${format(total)}개`
|
|
55
|
+
: `로그 ${format(total)}개 중 ${format(shown)}개`;
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
/** Returns the built-in labels for a language tag. English is used for any other language. */
|
|
59
|
+
export const labelsFor = (locale) => {
|
|
60
|
+
return locale && /^ko\b/i.test(locale) ? KO_LABELS : EN_LABELS;
|
|
61
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export type ScrollbarOrientation = 'vertical' | 'horizontal';
|
|
2
|
+
/**
|
|
3
|
+
* A scrollbar drawn in the DOM for a scroll container whose own scrollbar is hidden.
|
|
4
|
+
*
|
|
5
|
+
* The container keeps doing the scrolling, so wheel, touch, keyboard and assistive technology
|
|
6
|
+
* work as usual. This element only shows the position and lets a mouse drag the thumb or
|
|
7
|
+
* click the track. It is hidden from assistive technology, which uses the container.
|
|
8
|
+
*/
|
|
9
|
+
export declare class Scrollbar {
|
|
10
|
+
private readonly orientation;
|
|
11
|
+
private readonly viewport;
|
|
12
|
+
readonly element: HTMLDivElement;
|
|
13
|
+
private readonly thumb;
|
|
14
|
+
private idleTimer;
|
|
15
|
+
private drag;
|
|
16
|
+
constructor(ownerDocument: Document, orientation: ScrollbarOrientation, viewport: HTMLElement);
|
|
17
|
+
/** Moves the thumb to match the container, and shows the scrollbar for a moment. */
|
|
18
|
+
update(showActivity?: boolean): void;
|
|
19
|
+
dispose(): void;
|
|
20
|
+
private measure;
|
|
21
|
+
private trackSize;
|
|
22
|
+
private pointerOffset;
|
|
23
|
+
private scrollTo;
|
|
24
|
+
private readonly onPointerDown;
|
|
25
|
+
private readonly onPointerMove;
|
|
26
|
+
private readonly onPointerUp;
|
|
27
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/** The shortest a thumb gets, in CSS pixels, so it stays easy to grab. */
|
|
2
|
+
const MIN_THUMB_SIZE = 28;
|
|
3
|
+
/** How long the scrollbar stays visible after the view scrolls, in milliseconds. */
|
|
4
|
+
const IDLE_DELAY = 1000;
|
|
5
|
+
/**
|
|
6
|
+
* A scrollbar drawn in the DOM for a scroll container whose own scrollbar is hidden.
|
|
7
|
+
*
|
|
8
|
+
* The container keeps doing the scrolling, so wheel, touch, keyboard and assistive technology
|
|
9
|
+
* work as usual. This element only shows the position and lets a mouse drag the thumb or
|
|
10
|
+
* click the track. It is hidden from assistive technology, which uses the container.
|
|
11
|
+
*/
|
|
12
|
+
export class Scrollbar {
|
|
13
|
+
orientation;
|
|
14
|
+
viewport;
|
|
15
|
+
element;
|
|
16
|
+
thumb;
|
|
17
|
+
idleTimer;
|
|
18
|
+
drag = null;
|
|
19
|
+
constructor(ownerDocument, orientation, viewport) {
|
|
20
|
+
this.orientation = orientation;
|
|
21
|
+
this.viewport = viewport;
|
|
22
|
+
this.element = ownerDocument.createElement('div');
|
|
23
|
+
this.element.className = `lognal-scrollbar lognal-scrollbar-${orientation}`;
|
|
24
|
+
this.element.setAttribute('aria-hidden', 'true');
|
|
25
|
+
this.thumb = ownerDocument.createElement('div');
|
|
26
|
+
this.thumb.className = 'lognal-scrollbar-thumb';
|
|
27
|
+
this.element.append(this.thumb);
|
|
28
|
+
this.element.addEventListener('pointerdown', this.onPointerDown);
|
|
29
|
+
this.element.addEventListener('pointermove', this.onPointerMove);
|
|
30
|
+
this.element.addEventListener('pointerup', this.onPointerUp);
|
|
31
|
+
this.element.addEventListener('pointercancel', this.onPointerUp);
|
|
32
|
+
}
|
|
33
|
+
/** Moves the thumb to match the container, and shows the scrollbar for a moment. */
|
|
34
|
+
update(showActivity = false) {
|
|
35
|
+
const { client, total, position } = this.measure();
|
|
36
|
+
const scrollable = total - client > 1;
|
|
37
|
+
this.element.hidden = !scrollable;
|
|
38
|
+
if (!scrollable) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
const track = this.trackSize();
|
|
42
|
+
const thumbSize = Math.max(MIN_THUMB_SIZE, (client / total) * track);
|
|
43
|
+
const offset = ((track - thumbSize) * position) / (total - client);
|
|
44
|
+
if (this.orientation === 'vertical') {
|
|
45
|
+
this.thumb.style.height = `${thumbSize}px`;
|
|
46
|
+
this.thumb.style.transform = `translateY(${offset}px)`;
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
this.thumb.style.width = `${thumbSize}px`;
|
|
50
|
+
this.thumb.style.transform = `translateX(${offset}px)`;
|
|
51
|
+
}
|
|
52
|
+
if (showActivity) {
|
|
53
|
+
this.element.classList.add('is-active');
|
|
54
|
+
clearTimeout(this.idleTimer);
|
|
55
|
+
this.idleTimer = setTimeout(() => {
|
|
56
|
+
this.element.classList.remove('is-active');
|
|
57
|
+
}, IDLE_DELAY);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
dispose() {
|
|
61
|
+
clearTimeout(this.idleTimer);
|
|
62
|
+
this.element.remove();
|
|
63
|
+
}
|
|
64
|
+
measure() {
|
|
65
|
+
const viewport = this.viewport;
|
|
66
|
+
if (this.orientation === 'vertical') {
|
|
67
|
+
return {
|
|
68
|
+
client: viewport.clientHeight,
|
|
69
|
+
total: viewport.scrollHeight,
|
|
70
|
+
position: viewport.scrollTop
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
client: viewport.clientWidth,
|
|
75
|
+
total: viewport.scrollWidth,
|
|
76
|
+
position: viewport.scrollLeft
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
trackSize() {
|
|
80
|
+
const rect = this.element.getBoundingClientRect();
|
|
81
|
+
return this.orientation === 'vertical' ? rect.height : rect.width;
|
|
82
|
+
}
|
|
83
|
+
pointerOffset(event) {
|
|
84
|
+
const rect = this.element.getBoundingClientRect();
|
|
85
|
+
return this.orientation === 'vertical' ? event.clientY - rect.top : event.clientX - rect.left;
|
|
86
|
+
}
|
|
87
|
+
scrollTo(position) {
|
|
88
|
+
if (this.orientation === 'vertical') {
|
|
89
|
+
this.viewport.scrollTop = position;
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
this.viewport.scrollLeft = position;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
onPointerDown = (event) => {
|
|
96
|
+
if (event.button !== 0) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
event.preventDefault();
|
|
100
|
+
const { client, total, position } = this.measure();
|
|
101
|
+
if (event.target === this.thumb) {
|
|
102
|
+
this.drag = {
|
|
103
|
+
pointerId: event.pointerId,
|
|
104
|
+
start: this.pointerOffset(event),
|
|
105
|
+
scroll: position
|
|
106
|
+
};
|
|
107
|
+
try {
|
|
108
|
+
this.element.setPointerCapture(event.pointerId);
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
// The pointer ended before the capture could start.
|
|
112
|
+
}
|
|
113
|
+
this.element.classList.add('is-dragging');
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
const thumbRect = this.thumb.getBoundingClientRect();
|
|
117
|
+
const thumbStart = this.orientation === 'vertical' ? thumbRect.top : thumbRect.left;
|
|
118
|
+
const pointer = this.orientation === 'vertical' ? event.clientY : event.clientX;
|
|
119
|
+
const direction = pointer < thumbStart ? -1 : 1;
|
|
120
|
+
this.scrollTo(Math.max(0, Math.min(total - client, position + direction * client * 0.9)));
|
|
121
|
+
};
|
|
122
|
+
onPointerMove = (event) => {
|
|
123
|
+
if (!this.drag || event.pointerId !== this.drag.pointerId) {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
const { client, total } = this.measure();
|
|
127
|
+
const track = this.trackSize();
|
|
128
|
+
const thumbSize = Math.max(MIN_THUMB_SIZE, (client / total) * track);
|
|
129
|
+
const movable = Math.max(1, track - thumbSize);
|
|
130
|
+
const delta = this.pointerOffset(event) - this.drag.start;
|
|
131
|
+
this.scrollTo(this.drag.scroll + (delta * (total - client)) / movable);
|
|
132
|
+
};
|
|
133
|
+
onPointerUp = (event) => {
|
|
134
|
+
if (!this.drag || event.pointerId !== this.drag.pointerId) {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
this.drag = null;
|
|
138
|
+
this.element.classList.remove('is-dragging');
|
|
139
|
+
if (this.element.hasPointerCapture(event.pointerId)) {
|
|
140
|
+
this.element.releasePointerCapture(event.pointerId);
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { FontSettings, RenderTheme } from '../renderer/types.js';
|
|
2
|
+
/** Which color scheme the viewer uses. `auto` follows the operating system setting. */
|
|
3
|
+
export type ThemeMode = 'auto' | 'light' | 'dark';
|
|
4
|
+
export declare const DEFAULT_FONT: FontSettings;
|
|
5
|
+
/**
|
|
6
|
+
* Reads the render colors from the `--lognal-*` custom properties of an element, so a theme is
|
|
7
|
+
* written in CSS and the canvas follows it. A property that is not set keeps the default.
|
|
8
|
+
*/
|
|
9
|
+
export declare const readTheme: (element: Element) => RenderTheme;
|
|
10
|
+
/**
|
|
11
|
+
* Reads the font from the `--lognal-font-*` custom properties, with explicit options on top.
|
|
12
|
+
* The size may be written in `px`, `rem` or `em`, and the line height as a number, a
|
|
13
|
+
* percentage or a length.
|
|
14
|
+
*/
|
|
15
|
+
export declare const readFont: (element: Element, overrides: Partial<FontSettings>) => FontSettings;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { DEFAULT_RENDER_THEME } from '../renderer/theme.js';
|
|
2
|
+
const TOKEN_NAMES = [
|
|
3
|
+
'muted',
|
|
4
|
+
'string',
|
|
5
|
+
'number',
|
|
6
|
+
'boolean',
|
|
7
|
+
'null',
|
|
8
|
+
'key',
|
|
9
|
+
'symbol',
|
|
10
|
+
'function',
|
|
11
|
+
'regexp',
|
|
12
|
+
'date',
|
|
13
|
+
'tag',
|
|
14
|
+
'attribute',
|
|
15
|
+
'error',
|
|
16
|
+
'warn',
|
|
17
|
+
'info',
|
|
18
|
+
'accent'
|
|
19
|
+
];
|
|
20
|
+
export const DEFAULT_FONT = {
|
|
21
|
+
family: 'ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "D2Coding", "Noto Sans Mono CJK KR", "Liberation Mono", monospace',
|
|
22
|
+
size: 13,
|
|
23
|
+
weight: 400,
|
|
24
|
+
lineHeight: 1.6
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Reads the render colors from the `--lognal-*` custom properties of an element, so a theme is
|
|
28
|
+
* written in CSS and the canvas follows it. A property that is not set keeps the default.
|
|
29
|
+
*/
|
|
30
|
+
export const readTheme = (element) => {
|
|
31
|
+
const style = getComputedStyle(element);
|
|
32
|
+
const read = (name, fallback) => {
|
|
33
|
+
return style.getPropertyValue(`--lognal-${name}`).trim() || fallback;
|
|
34
|
+
};
|
|
35
|
+
const defaults = DEFAULT_RENDER_THEME;
|
|
36
|
+
const tokens = {};
|
|
37
|
+
for (const token of TOKEN_NAMES) {
|
|
38
|
+
tokens[token] = read(`token-${token}`, defaults.tokens[token]);
|
|
39
|
+
}
|
|
40
|
+
return {
|
|
41
|
+
background: read('background', defaults.background),
|
|
42
|
+
foreground: read('foreground', defaults.foreground),
|
|
43
|
+
muted: read('muted', defaults.muted),
|
|
44
|
+
accent: read('accent', defaults.accent),
|
|
45
|
+
selection: read('selection', defaults.selection),
|
|
46
|
+
match: read('match', defaults.match),
|
|
47
|
+
separator: read('separator', defaults.separator),
|
|
48
|
+
error: read('error', defaults.error),
|
|
49
|
+
errorBackground: read('error-background', defaults.errorBackground),
|
|
50
|
+
warn: read('warn', defaults.warn),
|
|
51
|
+
warnBackground: read('warn-background', defaults.warnBackground),
|
|
52
|
+
info: read('info', defaults.info),
|
|
53
|
+
debug: read('debug', defaults.debug),
|
|
54
|
+
tokens,
|
|
55
|
+
ansi: defaults.ansi.map((color, index) => read(`ansi-${index}`, color))
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
/** Converts a CSS length in `px`, `rem` or `em` to pixels. Returns `NaN` for anything else. */
|
|
59
|
+
const toPixels = (value, element) => {
|
|
60
|
+
const match = /^(-?[\d.]+)(px|rem|em)?$/i.exec(value);
|
|
61
|
+
if (!match) {
|
|
62
|
+
return Number.NaN;
|
|
63
|
+
}
|
|
64
|
+
const amount = Number.parseFloat(match[1]);
|
|
65
|
+
const unit = (match[2] ?? 'px').toLowerCase();
|
|
66
|
+
if (unit === 'rem') {
|
|
67
|
+
return (amount * Number.parseFloat(getComputedStyle(element.ownerDocument.documentElement).fontSize));
|
|
68
|
+
}
|
|
69
|
+
if (unit === 'em') {
|
|
70
|
+
return amount * Number.parseFloat(getComputedStyle(element).fontSize);
|
|
71
|
+
}
|
|
72
|
+
return amount;
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* Reads a line height as a multiple of the font size: `1.6` and `1.6em` stay `1.6`, `150%`
|
|
76
|
+
* becomes `1.5`, and a length such as `20px` or `1.25rem` is divided by the font size.
|
|
77
|
+
*/
|
|
78
|
+
const toLineHeight = (value, fontSize, element) => {
|
|
79
|
+
if (/^[\d.]+(em)?$/i.test(value)) {
|
|
80
|
+
return Number.parseFloat(value);
|
|
81
|
+
}
|
|
82
|
+
if (value.endsWith('%')) {
|
|
83
|
+
return Number.parseFloat(value) / 100;
|
|
84
|
+
}
|
|
85
|
+
return toPixels(value, element) / fontSize;
|
|
86
|
+
};
|
|
87
|
+
/**
|
|
88
|
+
* Reads the font from the `--lognal-font-*` custom properties, with explicit options on top.
|
|
89
|
+
* The size may be written in `px`, `rem` or `em`, and the line height as a number, a
|
|
90
|
+
* percentage or a length.
|
|
91
|
+
*/
|
|
92
|
+
export const readFont = (element, overrides) => {
|
|
93
|
+
const style = getComputedStyle(element);
|
|
94
|
+
const read = (name) => style.getPropertyValue(`--lognal-${name}`).trim();
|
|
95
|
+
const cssSize = toPixels(read('font-size'), element);
|
|
96
|
+
const size = overrides.size ?? (cssSize > 0 ? cssSize : DEFAULT_FONT.size);
|
|
97
|
+
const lineHeight = toLineHeight(read('line-height'), size, element);
|
|
98
|
+
const weight = Number.parseFloat(read('font-weight'));
|
|
99
|
+
return {
|
|
100
|
+
family: overrides.family ?? (read('font-family') || DEFAULT_FONT.family),
|
|
101
|
+
size,
|
|
102
|
+
weight: overrides.weight ?? (weight > 0 ? weight : DEFAULT_FONT.weight),
|
|
103
|
+
lineHeight: overrides.lineHeight ?? (lineHeight > 0 ? lineHeight : DEFAULT_FONT.lineHeight)
|
|
104
|
+
};
|
|
105
|
+
};
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { type LogFilter } from '../core/filter.js';
|
|
2
|
+
import { LogLayout, type LayoutOptions } from '../core/layout/layout.js';
|
|
3
|
+
import { LogStore, type LogStoreOptions, type WriteOptions } from '../core/store.js';
|
|
4
|
+
import { type TimestampFormat } from '../core/time.js';
|
|
5
|
+
import type { FontSettings, Renderer } from '../renderer/types.js';
|
|
6
|
+
import { type HookConsoleOptions, type LognalConsole } from '../sources/console/hook.js';
|
|
7
|
+
import { type ViewerLabels } from './labels.js';
|
|
8
|
+
import { type ThemeMode } from './theme.js';
|
|
9
|
+
/** Options that belong to the core: what is kept, how it is laid out, and what is shown. */
|
|
10
|
+
export interface CoreOptions extends LogStoreOptions, LayoutOptions {
|
|
11
|
+
filter: LogFilter | null;
|
|
12
|
+
}
|
|
13
|
+
/** Which controls the toolbar shows. */
|
|
14
|
+
export interface ToolbarOptions {
|
|
15
|
+
follow: boolean;
|
|
16
|
+
clear: boolean;
|
|
17
|
+
scroll: boolean;
|
|
18
|
+
wrap: boolean;
|
|
19
|
+
filter: boolean;
|
|
20
|
+
levels: boolean;
|
|
21
|
+
}
|
|
22
|
+
/** The input line, shown when something can answer the commands typed into it. */
|
|
23
|
+
export interface InputOptions {
|
|
24
|
+
/**
|
|
25
|
+
* Called with each command. A returned value, or the value a returned promise resolves to,
|
|
26
|
+
* is printed as the reply. Return `undefined` to print nothing, for example when the reply
|
|
27
|
+
* arrives later through `viewer.write`.
|
|
28
|
+
*/
|
|
29
|
+
onSubmit: (command: string, viewer: LogViewer) => unknown;
|
|
30
|
+
/** The prompt shown before the input. */
|
|
31
|
+
prompt?: string;
|
|
32
|
+
placeholder?: string;
|
|
33
|
+
/** Whether the command is added to the log before it runs. */
|
|
34
|
+
echo?: boolean;
|
|
35
|
+
/** How many past commands the arrow keys go through. */
|
|
36
|
+
historySize?: number;
|
|
37
|
+
}
|
|
38
|
+
export interface LogViewerOptions {
|
|
39
|
+
/** A store to show. Several viewers can share one store. A new store is created when absent. */
|
|
40
|
+
store?: LogStore;
|
|
41
|
+
/** Core options. Store options also apply to a store passed in `store`. */
|
|
42
|
+
core?: Partial<CoreOptions>;
|
|
43
|
+
/** The color scheme. `auto` follows the operating system. */
|
|
44
|
+
theme?: ThemeMode;
|
|
45
|
+
/** The font. Values left out come from the `--lognal-font-*` CSS properties. */
|
|
46
|
+
font?: Partial<FontSettings>;
|
|
47
|
+
/** Whether each entry shows its time, and in which format. */
|
|
48
|
+
timestamps?: boolean | TimestampFormat;
|
|
49
|
+
/** Whether the view follows new entries at the start. */
|
|
50
|
+
follow?: boolean;
|
|
51
|
+
/** The toolbar, or `false` to hide it. */
|
|
52
|
+
toolbar?: boolean | Partial<ToolbarOptions>;
|
|
53
|
+
/** Whether the status bar is shown. */
|
|
54
|
+
statusBar?: boolean;
|
|
55
|
+
/** The input line. Leave it out for a read-only viewer. */
|
|
56
|
+
input?: InputOptions | null;
|
|
57
|
+
/** The language of the built-in labels and number formatting, such as `en` or `ko`. */
|
|
58
|
+
locale?: string;
|
|
59
|
+
/** Labels that replace the built-in ones. */
|
|
60
|
+
labels?: Partial<ViewerLabels>;
|
|
61
|
+
/** Creates the renderer. Defaults to the Canvas 2D renderer. */
|
|
62
|
+
renderer?: (ownerDocument: Document) => Renderer;
|
|
63
|
+
}
|
|
64
|
+
/** Events a viewer emits. */
|
|
65
|
+
export interface LogViewerEvents {
|
|
66
|
+
/** Following new entries was turned on or off. */
|
|
67
|
+
follow: boolean;
|
|
68
|
+
/** The filter changed, from the toolbar or through `setFilter`. */
|
|
69
|
+
filter: LogFilter | null;
|
|
70
|
+
/** The text selection changed. */
|
|
71
|
+
selection: string;
|
|
72
|
+
}
|
|
73
|
+
type Listener<Value> = (value: Value) => void;
|
|
74
|
+
/**
|
|
75
|
+
* A log viewer: a toolbar, the log drawn by a renderer, an optional input line and a status
|
|
76
|
+
* bar. The viewer does the scrolling, selection, keyboard handling and accessibility, and
|
|
77
|
+
* hands each frame to the renderer.
|
|
78
|
+
*
|
|
79
|
+
* Import `lognal/style.css` once for the layout and the themes.
|
|
80
|
+
*/
|
|
81
|
+
export declare class LogViewer {
|
|
82
|
+
readonly store: LogStore;
|
|
83
|
+
readonly layout: LogLayout;
|
|
84
|
+
readonly element: HTMLDivElement;
|
|
85
|
+
private readonly ownerDocument;
|
|
86
|
+
private readonly renderer;
|
|
87
|
+
private options;
|
|
88
|
+
private readonly body;
|
|
89
|
+
private readonly viewport;
|
|
90
|
+
private readonly spacer;
|
|
91
|
+
private readonly mirror;
|
|
92
|
+
private readonly verticalScrollbar;
|
|
93
|
+
private readonly horizontalScrollbar;
|
|
94
|
+
private readonly newLogsButton;
|
|
95
|
+
private toolbarElement;
|
|
96
|
+
private statusElement;
|
|
97
|
+
private inputLine;
|
|
98
|
+
private readonly controls;
|
|
99
|
+
private metrics;
|
|
100
|
+
private width;
|
|
101
|
+
private height;
|
|
102
|
+
private following;
|
|
103
|
+
private hasUnseen;
|
|
104
|
+
private topPixels;
|
|
105
|
+
private firstRow;
|
|
106
|
+
private visibleRows;
|
|
107
|
+
private expectedScrollTop;
|
|
108
|
+
private lastScrollTop;
|
|
109
|
+
private anchor;
|
|
110
|
+
private columns;
|
|
111
|
+
private measureTimer;
|
|
112
|
+
private selection;
|
|
113
|
+
private drag;
|
|
114
|
+
private autoScrollFrame;
|
|
115
|
+
private lastPointer;
|
|
116
|
+
private frame;
|
|
117
|
+
private accessoryTimer;
|
|
118
|
+
private filterTimer;
|
|
119
|
+
private filter;
|
|
120
|
+
private readonly listeners;
|
|
121
|
+
private readonly cleanups;
|
|
122
|
+
private consoleObject;
|
|
123
|
+
/** The wrapping mode the toolbar button restores after turning wrapping off. */
|
|
124
|
+
private wrapMode;
|
|
125
|
+
private cachedNumberFormat;
|
|
126
|
+
private disposed;
|
|
127
|
+
constructor(container: HTMLElement, options?: LogViewerOptions);
|
|
128
|
+
/** An object with the console methods that writes to this viewer's store. */
|
|
129
|
+
get console(): LognalConsole;
|
|
130
|
+
/** Whether the view follows new entries. */
|
|
131
|
+
get isFollowing(): boolean;
|
|
132
|
+
/** Changes options after creation. Only the options given are changed. */
|
|
133
|
+
setOptions(options: Omit<LogViewerOptions, 'store' | 'renderer'>): void;
|
|
134
|
+
/** Adds text as one entry. */
|
|
135
|
+
write(text: string, options?: WriteOptions): void;
|
|
136
|
+
/** Adds text as one entry per line. */
|
|
137
|
+
writeLines(text: string, options?: WriteOptions): void;
|
|
138
|
+
/**
|
|
139
|
+
* Starts recording a console, `console` by default, into this viewer's store. Returns a
|
|
140
|
+
* function that stops recording. Recording also stops when the viewer is disposed.
|
|
141
|
+
*/
|
|
142
|
+
hookConsole(target?: Console, options?: HookConsoleOptions): () => void;
|
|
143
|
+
/** Removes every entry. */
|
|
144
|
+
clear(): void;
|
|
145
|
+
/** Sets the filter. `null` shows every entry. */
|
|
146
|
+
setFilter(filter: LogFilter | null): void;
|
|
147
|
+
getFilter(): LogFilter | null;
|
|
148
|
+
/** Turns following new entries on or off. Turning it on scrolls to the newest entry. */
|
|
149
|
+
setFollowing(following: boolean): void;
|
|
150
|
+
scrollToTop(): void;
|
|
151
|
+
scrollToBottom(): void;
|
|
152
|
+
/** Scrolls so that an entry is at the top of the view. */
|
|
153
|
+
scrollToEntry(entryId: number): void;
|
|
154
|
+
/** Returns the selected text, or an empty string. */
|
|
155
|
+
getSelectionText(): string;
|
|
156
|
+
/** Selects the text of every visible entry. */
|
|
157
|
+
selectAll(): void;
|
|
158
|
+
clearSelection(): void;
|
|
159
|
+
/** Copies the selected text to the clipboard. Resolves to whether anything was copied. */
|
|
160
|
+
copySelection(): Promise<boolean>;
|
|
161
|
+
/** Moves keyboard focus to the input line, or to the log when there is no input line. */
|
|
162
|
+
focus(): void;
|
|
163
|
+
/** Reads the theme and the font from CSS again, for example after the page changed them. */
|
|
164
|
+
refresh(): void;
|
|
165
|
+
/** Calls a listener for an event. Returns a function that removes the listener. */
|
|
166
|
+
on<Name extends keyof LogViewerEvents>(name: Name, listener: Listener<LogViewerEvents[Name]>): () => void;
|
|
167
|
+
/** Removes the viewer from the page and stops everything it started. */
|
|
168
|
+
dispose(): void;
|
|
169
|
+
private resolveOptions;
|
|
170
|
+
private unresolvedOptions;
|
|
171
|
+
private emit;
|
|
172
|
+
private listen;
|
|
173
|
+
private bindEvents;
|
|
174
|
+
private watchPixelRatio;
|
|
175
|
+
private buildChrome;
|
|
176
|
+
private buildToolbar;
|
|
177
|
+
private syncWrapButton;
|
|
178
|
+
private applyTheme;
|
|
179
|
+
private applyFont;
|
|
180
|
+
private timestampCells;
|
|
181
|
+
private contentLeft;
|
|
182
|
+
private updateColumns;
|
|
183
|
+
private scrollScale;
|
|
184
|
+
private contentHeight;
|
|
185
|
+
private requestRender;
|
|
186
|
+
private render;
|
|
187
|
+
/** Returns an anchor for the row at a pixel position of the whole log. */
|
|
188
|
+
private anchorAt;
|
|
189
|
+
/**
|
|
190
|
+
* Lays out the entries that still have estimated row counts, a slice at a time between
|
|
191
|
+
* frames, starting with the ones nearest to the view.
|
|
192
|
+
*/
|
|
193
|
+
private scheduleMeasure;
|
|
194
|
+
private decorationFor;
|
|
195
|
+
private findMatches;
|
|
196
|
+
private scheduleAccessories;
|
|
197
|
+
/** Updates the status bar. Cheap enough for every frame, since text is only written when it changes. */
|
|
198
|
+
private updateStatus;
|
|
199
|
+
private updateAccessories;
|
|
200
|
+
private numberFormat;
|
|
201
|
+
private hitTest;
|
|
202
|
+
private positionFrom;
|
|
203
|
+
private readonly onStoreChange;
|
|
204
|
+
private readonly onResize;
|
|
205
|
+
private readonly onScroll;
|
|
206
|
+
private readonly onPointerDown;
|
|
207
|
+
private readonly onPointerMove;
|
|
208
|
+
private readonly onPointerUp;
|
|
209
|
+
private extendSelection;
|
|
210
|
+
private updateAutoScroll;
|
|
211
|
+
private readonly onDoubleClick;
|
|
212
|
+
private readonly onKeyDown;
|
|
213
|
+
private readonly onCopy;
|
|
214
|
+
private onCommand;
|
|
215
|
+
private printReply;
|
|
216
|
+
}
|
|
217
|
+
export {};
|