@zikojs/console 0.1.0 → 0.2.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 +1 -1
- package/package.json +7 -2
- package/src/components/Console.js +27 -23
- package/src/components/ConsoleBody.js +44 -32
- package/src/components/ConsoleFeed.js +27 -121
- package/src/components/ConsolePrompt.js +25 -57
- package/src/components/ConsoleToolbar.js +34 -47
- package/src/components/Row.js +27 -50
- package/src/components/nodeEntry.js +10 -0
- package/src/feed/index.js +96 -0
- package/src/index.js +39 -45
- package/src/utils/const.js +11 -0
- package/src/utils/feed.js +5 -0
- package/src/utils/index.js +3 -1
- package/src/utils/value-formatting.js +1 -0
- package/src/feed/createFeed.js +0 -67
package/LICENSE
CHANGED
package/package.json
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zikojs/console",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"publishConfig": {
|
|
7
7
|
"access": "public"
|
|
8
8
|
},
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/zikojs/console.git",
|
|
12
|
+
"directory": "packages/console"
|
|
13
|
+
},
|
|
9
14
|
"keywords": [
|
|
10
15
|
"zikojs",
|
|
11
16
|
"console",
|
|
@@ -16,7 +21,7 @@
|
|
|
16
21
|
"license": "MIT",
|
|
17
22
|
"type": "module",
|
|
18
23
|
"dependencies": {
|
|
19
|
-
"ziko": "
|
|
24
|
+
"ziko": "latest"
|
|
20
25
|
},
|
|
21
26
|
"scripts": {
|
|
22
27
|
"test": "echo \"Error: no test specified\" && exit 1"
|
|
@@ -1,28 +1,32 @@
|
|
|
1
1
|
import van from "https://cdn.jsdelivr.net/npm/vanjs-core@1.5.3/src/van.js";
|
|
2
|
+
const { div } = van.tags;
|
|
3
|
+
import { nodeEntry, makeEntry } from "./nodeEntry.js";
|
|
4
|
+
import { Row } from "./Row.js";
|
|
2
5
|
|
|
3
|
-
|
|
6
|
+
const entryNode =
|
|
7
|
+
(method) =>
|
|
8
|
+
(...args) => {
|
|
9
|
+
const e = makeEntry(method, args);
|
|
10
|
+
const node = div(
|
|
11
|
+
{ class: () => "cf cf-solo " + Console.theme }, // <-- using a function or string
|
|
12
|
+
Row(e),
|
|
13
|
+
);
|
|
14
|
+
nodeEntry.set(node, e);
|
|
15
|
+
return node;
|
|
16
|
+
};
|
|
4
17
|
|
|
5
18
|
export const Console = {
|
|
6
|
-
theme:
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
const node =
|
|
20
|
-
document.createComment("groupEnd");
|
|
21
|
-
|
|
22
|
-
nodeEntry.set(node, {
|
|
23
|
-
method: "groupEnd",
|
|
24
|
-
args: []
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
return node;
|
|
19
|
+
theme: { val: "dark" },
|
|
20
|
+
log: entryNode("log"),
|
|
21
|
+
info: entryNode("info"),
|
|
22
|
+
debug: entryNode("debug"),
|
|
23
|
+
warn: entryNode("warn"),
|
|
24
|
+
error: entryNode("error"),
|
|
25
|
+
table: entryNode("table"),
|
|
26
|
+
group: entryNode("group"),
|
|
27
|
+
groupEnd: () => {
|
|
28
|
+
const node = document.createComment("groupEnd");
|
|
29
|
+
nodeEntry.set(node, { method: "groupEnd", args: [] });
|
|
30
|
+
return node;
|
|
31
|
+
},
|
|
28
32
|
};
|
|
@@ -1,46 +1,58 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
1
|
+
import { tags } from "ziko/dom";
|
|
2
|
+
const { div } = tags;
|
|
3
|
+
import { Row, cache } from "./Row.js";
|
|
4
|
+
|
|
5
|
+
export function ConsoleBody(feed) {
|
|
6
|
+
const listContainer = div();
|
|
7
|
+
|
|
8
|
+
const renderLogs = () => {
|
|
9
|
+
const visible = feed.getVisibleLogs();
|
|
10
|
+
listContainer.element.replaceChildren(
|
|
11
|
+
(
|
|
12
|
+
visible.length
|
|
13
|
+
? div(...visible.map((e) => Row(e)))
|
|
14
|
+
: div(
|
|
15
|
+
{ class: "cf-empty" },
|
|
16
|
+
"No messages yet. Anything sent to console.* shows up here.",
|
|
17
|
+
)
|
|
18
|
+
).element
|
|
3
19
|
|
|
4
|
-
|
|
20
|
+
);
|
|
21
|
+
};
|
|
5
22
|
|
|
6
|
-
export function ConsoleBody(visible) {
|
|
7
23
|
let stick = true;
|
|
8
|
-
|
|
9
24
|
const body = div(
|
|
10
25
|
{
|
|
11
26
|
class: "cf-body",
|
|
12
|
-
|
|
13
27
|
onscroll: () => {
|
|
14
|
-
stick =
|
|
15
|
-
|
|
16
|
-
body.scrollTop -
|
|
17
|
-
body.clientHeight < 24;
|
|
18
|
-
}
|
|
28
|
+
stick = body.scrollHeight - body.scrollTop - body.clientHeight < 24;
|
|
29
|
+
},
|
|
19
30
|
},
|
|
31
|
+
listContainer
|
|
32
|
+
).element;
|
|
33
|
+
|
|
34
|
+
feed.addEventListener("logs-change", () => {
|
|
35
|
+
renderLogs();
|
|
36
|
+
if (stick) {
|
|
37
|
+
requestAnimationFrame(() => {
|
|
38
|
+
body.scrollTop = body.scrollHeight;
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
});
|
|
20
42
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
visible.val.map(entry =>
|
|
25
|
-
Row(entry)
|
|
26
|
-
)
|
|
27
|
-
)
|
|
28
|
-
: div(
|
|
29
|
-
{ class: "cf-empty" },
|
|
30
|
-
"No messages yet. Anything sent to console.* shows up here."
|
|
31
|
-
)
|
|
32
|
-
);
|
|
33
|
-
|
|
34
|
-
van.derive(() => {
|
|
35
|
-
visible.val;
|
|
43
|
+
feed.addEventListener("clear", () => {
|
|
44
|
+
renderLogs();
|
|
45
|
+
});
|
|
36
46
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
}
|
|
47
|
+
feed.addEventListener("row-update", (e) => {
|
|
48
|
+
const rowEl = cache.get(e.detail);
|
|
49
|
+
if (rowEl) {
|
|
50
|
+
const badge = rowEl.querySelector(".cf-badge");
|
|
51
|
+
if (badge && badge.updateBadge) badge.updateBadge();
|
|
52
|
+
}
|
|
43
53
|
});
|
|
44
54
|
|
|
55
|
+
renderLogs();
|
|
56
|
+
|
|
45
57
|
return body;
|
|
46
58
|
}
|
|
@@ -1,141 +1,47 @@
|
|
|
1
1
|
import van from "https://cdn.jsdelivr.net/npm/vanjs-core@1.5.3/src/van.js";
|
|
2
|
+
const { div } = van.tags;
|
|
2
3
|
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
|
|
4
|
+
import { isProps } from "../utils/index.js";
|
|
5
|
+
import { Hook } from "../hook/index.js";
|
|
6
|
+
import { createFeed } from "../feed/index.js";
|
|
7
|
+
import { nodeEntry } from "./nodeEntry.js";
|
|
8
|
+
import { cache } from "./Row.js";
|
|
6
9
|
import { ConsoleToolbar } from "./ConsoleToolbar.js";
|
|
7
10
|
import { ConsoleBody } from "./ConsoleBody.js";
|
|
8
11
|
import { ConsolePrompt } from "./ConsolePrompt.js";
|
|
9
12
|
|
|
10
|
-
import { Hook } from "../hook/index.js";
|
|
11
|
-
import {
|
|
12
|
-
fmt,
|
|
13
|
-
safe
|
|
14
|
-
} from "../utils/index.js";
|
|
15
|
-
|
|
16
|
-
const { div } = van.tags;
|
|
17
|
-
|
|
18
|
-
const LEVEL = {
|
|
19
|
-
log: "log",
|
|
20
|
-
dir: "log",
|
|
21
|
-
table: "log",
|
|
22
|
-
group: "log",
|
|
23
|
-
|
|
24
|
-
info: "info",
|
|
25
|
-
debug: "debug",
|
|
26
|
-
warn: "warn",
|
|
27
|
-
error: "error"
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
const LEVELS = [
|
|
31
|
-
"log",
|
|
32
|
-
"info",
|
|
33
|
-
"warn",
|
|
34
|
-
"error",
|
|
35
|
-
"debug"
|
|
36
|
-
];
|
|
37
|
-
|
|
38
|
-
const isProps = value =>
|
|
39
|
-
value &&
|
|
40
|
-
typeof value === "object" &&
|
|
41
|
-
!(value instanceof Node) &&
|
|
42
|
-
Object.getPrototypeOf(value) === Object.prototype;
|
|
43
|
-
|
|
44
13
|
export function ConsoleFeed(...args) {
|
|
45
|
-
const [props, ...
|
|
46
|
-
|
|
47
|
-
? args
|
|
48
|
-
: [{}, ...args];
|
|
49
|
-
|
|
14
|
+
const [props, ...kids] = isProps(args[0]) ? args : [{}, ...args];
|
|
15
|
+
const feed = props.feed || createFeed(1000, cache);
|
|
50
16
|
const {
|
|
51
|
-
feed = createFeed(),
|
|
52
17
|
variant = "dark",
|
|
53
18
|
repl = true,
|
|
54
|
-
hook = false
|
|
19
|
+
hook = false,
|
|
55
20
|
} = props;
|
|
56
21
|
|
|
57
|
-
if (hook)
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
.flat(Infinity)
|
|
63
|
-
.forEach(child => {
|
|
64
|
-
const entry = nodeEntry.get(child);
|
|
22
|
+
if (hook) Hook(console, (e) => feed.push(e));
|
|
23
|
+
kids.flat(Infinity).forEach((c) => {
|
|
24
|
+
const e = nodeEntry.get(c);
|
|
25
|
+
if (e) feed.push(e);
|
|
26
|
+
});
|
|
65
27
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
}
|
|
69
|
-
});
|
|
28
|
+
const initialTheme = typeof variant === "string" ? variant : (variant.val || "dark");
|
|
29
|
+
feed.setTheme(initialTheme);
|
|
70
30
|
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
: variant;
|
|
31
|
+
const bar = ConsoleToolbar(feed);
|
|
32
|
+
const body = ConsoleBody(feed);
|
|
33
|
+
const prompt = repl ? ConsolePrompt(feed) : "";
|
|
75
34
|
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
35
|
+
const container = div(
|
|
36
|
+
{ class: () => "cf " + feed.theme, style: "flex:1" },
|
|
37
|
+
bar,
|
|
38
|
+
body,
|
|
39
|
+
prompt,
|
|
81
40
|
);
|
|
82
41
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
const count = level =>
|
|
86
|
-
van.derive(() =>
|
|
87
|
-
feed.logs.val.filter(
|
|
88
|
-
entry =>
|
|
89
|
-
LEVEL[entry.method] === level
|
|
90
|
-
).length
|
|
91
|
-
);
|
|
92
|
-
|
|
93
|
-
const visible = van.derive(() => {
|
|
94
|
-
const search =
|
|
95
|
-
query.val.toLowerCase();
|
|
96
|
-
|
|
97
|
-
return feed.logs.val.filter(entry => {
|
|
98
|
-
const level =
|
|
99
|
-
LEVEL[entry.method];
|
|
100
|
-
|
|
101
|
-
if (
|
|
102
|
-
level &&
|
|
103
|
-
!levels[level].val
|
|
104
|
-
) {
|
|
105
|
-
return false;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
if (!search) {
|
|
109
|
-
return true;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
return fmt(entry.args)
|
|
113
|
-
.map(safe)
|
|
114
|
-
.join(" ")
|
|
115
|
-
.toLowerCase()
|
|
116
|
-
.includes(search);
|
|
117
|
-
});
|
|
42
|
+
feed.addEventListener("theme-change", (e) => {
|
|
43
|
+
container.className = "cf " + e.detail + " flex:1";
|
|
118
44
|
});
|
|
119
45
|
|
|
120
|
-
return
|
|
121
|
-
{
|
|
122
|
-
class: () =>
|
|
123
|
-
"cf " + theme.val,
|
|
124
|
-
|
|
125
|
-
style: "flex:1"
|
|
126
|
-
},
|
|
127
|
-
|
|
128
|
-
ConsoleToolbar({
|
|
129
|
-
levels,
|
|
130
|
-
query,
|
|
131
|
-
count,
|
|
132
|
-
clear: feed.clear
|
|
133
|
-
}),
|
|
134
|
-
|
|
135
|
-
ConsoleBody(visible),
|
|
136
|
-
|
|
137
|
-
repl
|
|
138
|
-
? ConsolePrompt({ feed })
|
|
139
|
-
: ""
|
|
140
|
-
);
|
|
46
|
+
return container;
|
|
141
47
|
}
|
|
@@ -1,74 +1,42 @@
|
|
|
1
|
-
import
|
|
2
|
-
const { div, span, input } = tags;
|
|
3
|
-
export function ConsolePrompt({
|
|
4
|
-
feed
|
|
5
|
-
}) {
|
|
6
|
-
const history = [];
|
|
7
|
-
let index = 0;
|
|
1
|
+
import van from "https://cdn.jsdelivr.net/npm/vanjs-core@1.5.3/src/van.js";
|
|
2
|
+
const { div, span, input } = van.tags;
|
|
8
3
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
args: [code]
|
|
13
|
-
});
|
|
4
|
+
export function ConsolePrompt(feed) {
|
|
5
|
+
const hist = [];
|
|
6
|
+
let hi = 0;
|
|
14
7
|
|
|
8
|
+
const run = (code) => {
|
|
9
|
+
feed.push({ method: "command", args: [code] });
|
|
15
10
|
try {
|
|
16
|
-
feed.push({
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
});
|
|
20
|
-
} catch (error) {
|
|
21
|
-
feed.push({
|
|
22
|
-
method: "error",
|
|
23
|
-
args: [error]
|
|
24
|
-
});
|
|
11
|
+
feed.push({ method: "result", args: [(0, eval)(code)] });
|
|
12
|
+
} catch (err) {
|
|
13
|
+
feed.push({ method: "error", args: [err] });
|
|
25
14
|
}
|
|
26
15
|
};
|
|
27
16
|
|
|
28
|
-
|
|
17
|
+
return div(
|
|
18
|
+
{ class: "cf-prompt" },
|
|
19
|
+
span("\u203A"),
|
|
20
|
+
input({
|
|
29
21
|
placeholder: "Run JavaScript",
|
|
30
22
|
"aria-label": "Run JavaScript",
|
|
31
23
|
spellcheck: false,
|
|
32
24
|
autocomplete: "off",
|
|
33
|
-
|
|
34
|
-
onKeyDown: __e__ => {
|
|
35
|
-
const e = __e__.event
|
|
25
|
+
onkeydown: (e) => {
|
|
36
26
|
const el = e.target;
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
el.value.trim()
|
|
41
|
-
) {
|
|
42
|
-
history.push(el.value);
|
|
43
|
-
index = history.length;
|
|
44
|
-
|
|
27
|
+
if (e.key === "Enter" && el.value.trim()) {
|
|
28
|
+
hist.push(el.value);
|
|
29
|
+
hi = hist.length;
|
|
45
30
|
run(el.value);
|
|
46
|
-
|
|
47
31
|
el.value = "";
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
else if (
|
|
51
|
-
e.key === "ArrowUp" &&
|
|
52
|
-
index > 0
|
|
53
|
-
) {
|
|
54
|
-
el.value = history[--index];
|
|
32
|
+
} else if (e.key === "ArrowUp" && hi > 0) {
|
|
33
|
+
el.value = hist[--hi];
|
|
55
34
|
e.preventDefault();
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
else if (e.key === "ArrowDown") {
|
|
35
|
+
} else if (e.key === "ArrowDown") {
|
|
59
36
|
el.value =
|
|
60
|
-
|
|
61
|
-
? history[++index]
|
|
62
|
-
: (index = history.length, "");
|
|
37
|
+
hi < hist.length - 1 ? hist[++hi] : ((hi = hist.length), "");
|
|
63
38
|
}
|
|
64
|
-
}
|
|
65
|
-
})
|
|
66
|
-
|
|
67
|
-
return div(
|
|
68
|
-
{ class: "cf-prompt" },
|
|
69
|
-
|
|
70
|
-
span("\u203A"),
|
|
71
|
-
inp
|
|
72
|
-
|
|
73
|
-
).element;
|
|
39
|
+
},
|
|
40
|
+
}),
|
|
41
|
+
);
|
|
74
42
|
}
|
|
@@ -1,61 +1,48 @@
|
|
|
1
1
|
import van from "https://cdn.jsdelivr.net/npm/vanjs-core@1.5.3/src/van.js";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
2
|
+
const { div, span, input, button } = van.tags;
|
|
3
|
+
import { LEVELS } from "../utils/index.js";
|
|
4
|
+
|
|
5
|
+
export function ConsoleToolbar(feed) {
|
|
6
|
+
const chipButtons = {};
|
|
7
|
+
const countSpans = {};
|
|
8
|
+
|
|
9
|
+
const updateToolbar = () => {
|
|
10
|
+
LEVELS.forEach((l) => {
|
|
11
|
+
if (chipButtons[l]) {
|
|
12
|
+
const isOn = feed.levels[l];
|
|
13
|
+
chipButtons[l].className = `cf-chip ${l}` + (isOn ? " on" : "");
|
|
14
|
+
chipButtons[l].setAttribute("aria-pressed", isOn);
|
|
15
|
+
}
|
|
16
|
+
if (countSpans[l]) {
|
|
17
|
+
countSpans[l].textContent = feed.getCount(l);
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
feed.addEventListener("filter-change", updateToolbar);
|
|
22
23
|
|
|
23
24
|
return div(
|
|
24
25
|
{ class: "cf-bar" },
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
button(
|
|
26
|
+
LEVELS.map((l) => {
|
|
27
|
+
countSpans[l] = span(feed.getCount(l));
|
|
28
|
+
chipButtons[l] = button(
|
|
28
29
|
{
|
|
29
|
-
class: ()
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
"aria-pressed": () =>
|
|
34
|
-
levels[level].val,
|
|
35
|
-
|
|
36
|
-
onclick: () =>
|
|
37
|
-
levels[level].val =
|
|
38
|
-
!levels[level].val
|
|
30
|
+
class: `cf-chip ${l}` + (feed.levels[l] ? " on" : ""),
|
|
31
|
+
"aria-pressed": feed.levels[l],
|
|
32
|
+
onclick: () => feed.toggleLevel(l),
|
|
39
33
|
},
|
|
40
|
-
|
|
41
|
-
level,
|
|
34
|
+
l,
|
|
42
35
|
" ",
|
|
43
|
-
|
|
44
|
-
)
|
|
45
|
-
|
|
46
|
-
|
|
36
|
+
countSpans[l],
|
|
37
|
+
);
|
|
38
|
+
return chipButtons[l];
|
|
39
|
+
}),
|
|
47
40
|
input({
|
|
48
41
|
type: "search",
|
|
49
42
|
placeholder: "Filter messages",
|
|
50
43
|
"aria-label": "Filter messages",
|
|
51
|
-
|
|
52
|
-
oninput: e =>
|
|
53
|
-
query.val = e.target.value
|
|
44
|
+
oninput: (e) => feed.setQuery(e.target.value),
|
|
54
45
|
}),
|
|
55
|
-
|
|
56
|
-
button(
|
|
57
|
-
{ onclick: clear },
|
|
58
|
-
"Clear"
|
|
59
|
-
)
|
|
46
|
+
button({ onclick: () => feed.clear() }, "Clear"),
|
|
60
47
|
);
|
|
61
48
|
}
|
package/src/components/Row.js
CHANGED
|
@@ -1,64 +1,41 @@
|
|
|
1
1
|
import van from "https://cdn.jsdelivr.net/npm/vanjs-core@1.5.3/src/van.js";
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
fmt,
|
|
5
|
-
isObj
|
|
6
|
-
} from "../utils/index.js";
|
|
7
|
-
|
|
8
|
-
import { Inspect } from "./Inspect.js";
|
|
9
|
-
import { Table } from "./Table.js";
|
|
10
|
-
|
|
11
2
|
const { div, span } = van.tags;
|
|
3
|
+
import { fmt, isObj } from "../utils/index.js";
|
|
4
|
+
import { Inspect, Table } from "./index.js";
|
|
12
5
|
|
|
13
6
|
const cache = new WeakMap();
|
|
14
7
|
|
|
15
|
-
const buildRow =
|
|
16
|
-
const [a0, ...rest] =
|
|
17
|
-
|
|
8
|
+
const buildRow = (e) => {
|
|
9
|
+
const [a0, ...rest] = e.args;
|
|
18
10
|
const items =
|
|
19
|
-
|
|
20
|
-
? [
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
11
|
+
e.method === "table" && isObj(a0)
|
|
12
|
+
? [Table(a0), rest.map((x) => Inspect(x, undefined, [], true)).element]
|
|
13
|
+
: fmt(e.args).map((a) => Inspect(a, undefined, [], true).element);
|
|
14
|
+
|
|
15
|
+
const badgeSpan = span({ class: "cf-badge" }, () => (e.count > 1 ? e.count : ""));
|
|
16
|
+
const updateBadge = () => {
|
|
17
|
+
badgeSpan.textContent = e.count > 1 ? e.count : "";
|
|
18
|
+
badgeSpan.style.display = e.count > 1 ? "inline-block" : "none";
|
|
19
|
+
};
|
|
20
|
+
badgeSpan.updateBadge = updateBadge;
|
|
21
|
+
updateBadge();
|
|
29
22
|
|
|
30
23
|
return div(
|
|
31
|
-
{
|
|
32
|
-
|
|
33
|
-
style: `--d:${entry.depth}`
|
|
34
|
-
},
|
|
35
|
-
|
|
36
|
-
() =>
|
|
37
|
-
entry.count.val > 1
|
|
38
|
-
? span(
|
|
39
|
-
{ class: "cf-badge" },
|
|
40
|
-
entry.count.val
|
|
41
|
-
)
|
|
42
|
-
: "",
|
|
43
|
-
|
|
24
|
+
{ class: `cf-row ${e.method}`, style: `--d:${e.depth}` },
|
|
25
|
+
badgeSpan,
|
|
44
26
|
span(
|
|
45
27
|
{ class: "cf-time" },
|
|
46
|
-
|
|
47
|
-
hour12: false
|
|
48
|
-
})
|
|
28
|
+
e.time.toLocaleTimeString([], { hour12: false }),
|
|
49
29
|
),
|
|
50
|
-
|
|
51
|
-
div(
|
|
52
|
-
{ class: "cf-msg" },
|
|
53
|
-
items
|
|
54
|
-
)
|
|
30
|
+
div({ class: "cf-msg" }, items),
|
|
55
31
|
);
|
|
56
32
|
};
|
|
57
33
|
|
|
58
|
-
export const Row =
|
|
59
|
-
cache.get(
|
|
60
|
-
(
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
34
|
+
export const Row = (e) => {
|
|
35
|
+
if (cache.has(e)) return cache.get(e);
|
|
36
|
+
const n = buildRow(e);
|
|
37
|
+
cache.set(e, n);
|
|
38
|
+
return n;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export { cache };
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { LEVEL, LEVELS, same, fmt, safe } from "../utils/index.js";
|
|
2
|
+
|
|
3
|
+
export class ConsoleStore extends EventTarget {
|
|
4
|
+
constructor(max = 1000, cacheRef = null) {
|
|
5
|
+
super();
|
|
6
|
+
this.logs = [];
|
|
7
|
+
this.max = max;
|
|
8
|
+
this.depth = 0;
|
|
9
|
+
this.id = 0;
|
|
10
|
+
this.levels = Object.fromEntries(LEVELS.map((l) => [l, true]));
|
|
11
|
+
this.query = "";
|
|
12
|
+
this.theme = "dark";
|
|
13
|
+
this.cache = cacheRef;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
setTheme(theme) {
|
|
17
|
+
this.theme = theme;
|
|
18
|
+
this.dispatchEvent(new CustomEvent("theme-change", { detail: theme }));
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
toggleLevel(level) {
|
|
22
|
+
this.levels[level] = !this.levels[level];
|
|
23
|
+
this.dispatchEvent(new CustomEvent("filter-change"));
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
setQuery(query) {
|
|
27
|
+
this.query = query;
|
|
28
|
+
this.dispatchEvent(new CustomEvent("filter-change"));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
push(src) {
|
|
32
|
+
const { method, args = [] } = src;
|
|
33
|
+
if (method === "clear") {
|
|
34
|
+
this.depth = 0;
|
|
35
|
+
this.logs = [];
|
|
36
|
+
this.dispatchEvent(new CustomEvent("clear"));
|
|
37
|
+
this.dispatchEvent(new CustomEvent("logs-change"));
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
if (method === "groupEnd") {
|
|
41
|
+
this.depth = Math.max(0, this.depth - 1);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const isGroup = method === "group" || method === "groupCollapsed";
|
|
45
|
+
const e = src.count
|
|
46
|
+
? Object.assign(src, { id: ++this.id, depth: this.depth })
|
|
47
|
+
: {
|
|
48
|
+
id: ++this.id,
|
|
49
|
+
method: isGroup ? "group" : method,
|
|
50
|
+
args,
|
|
51
|
+
depth: this.depth,
|
|
52
|
+
count: 1,
|
|
53
|
+
time: new Date(),
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
if (src.count && this.cache) {
|
|
57
|
+
this.cache.get(e)?.style.setProperty("--d", this.depth);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const last = this.logs.at(-1);
|
|
61
|
+
if (last && !isGroup && same(last, e)) {
|
|
62
|
+
last.count++;
|
|
63
|
+
this.dispatchEvent(new CustomEvent("row-update", { detail: last }));
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
this.logs.push(e);
|
|
68
|
+
if (this.logs.length > this.max) {
|
|
69
|
+
this.logs.shift();
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
this.dispatchEvent(new CustomEvent("logs-change", { detail: e }));
|
|
73
|
+
if (isGroup) this.depth++;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
clear() {
|
|
77
|
+
this.push({ method: "clear" });
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
getCount(level) {
|
|
81
|
+
return this.logs.filter((e) => LEVEL[e.method] === level).length;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
getVisibleLogs() {
|
|
85
|
+
const s = this.query.toLowerCase();
|
|
86
|
+
return this.logs.filter(
|
|
87
|
+
(e) =>
|
|
88
|
+
(!(e.method in LEVEL) || this.levels[LEVEL[e.method]]) &&
|
|
89
|
+
(!s || fmt(e.args).map(safe).join(" ").toLowerCase().includes(s))
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function createFeed(max = 1000, cache) {
|
|
95
|
+
return new ConsoleStore(max, cache);
|
|
96
|
+
}
|
package/src/index.js
CHANGED
|
@@ -1,51 +1,45 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
Console
|
|
6
|
-
} from './components'
|
|
7
|
-
import { createFeed } from './feed/createFeed'
|
|
8
|
-
import { Hook } from './hook/index.js'
|
|
1
|
+
import "./index.css";
|
|
2
|
+
import { Hook } from "./hook/index.js";
|
|
3
|
+
import { createFeed } from "./feed/index.js";
|
|
4
|
+
import { ConsoleFeed, Console } from "./components/index.js";
|
|
9
5
|
|
|
10
|
-
Object.assign(window, {
|
|
6
|
+
Object.assign(window, {
|
|
7
|
+
ConsoleFeed: { Hook, createFeed, ConsoleFeed, Console },
|
|
8
|
+
});
|
|
11
9
|
|
|
12
10
|
/* =====================================================================
|
|
13
11
|
Demo
|
|
14
12
|
===================================================================== */
|
|
15
13
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
// 1) Declarative
|
|
19
|
-
document.body.append(
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
//
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
Console.
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
console.
|
|
48
|
-
console.log("parsing")
|
|
49
|
-
console.group("Auth")
|
|
50
|
-
console.warn("token expires soon")
|
|
51
|
-
console.groupEnd()
|
|
14
|
+
Console.theme = 'light';
|
|
15
|
+
|
|
16
|
+
// 1) Declarative
|
|
17
|
+
// document.body.append(
|
|
18
|
+
// ConsoleFeed(
|
|
19
|
+
// { variant: theme.val, repl: false },
|
|
20
|
+
// Console.log("Hello from VanJS", 42, true, null, undefined),
|
|
21
|
+
// Console.info("Server listening on :3000"),
|
|
22
|
+
// Console.group("Request"),
|
|
23
|
+
// Console.log("parsing"),
|
|
24
|
+
// Console.warn("token expires soon"),
|
|
25
|
+
// Console.groupEnd(),
|
|
26
|
+
// Console.error(new Error("Something broke")),
|
|
27
|
+
// Console.table([
|
|
28
|
+
// { id: 1, name: "Ada" },
|
|
29
|
+
// { id: 2, name: "Linus" },
|
|
30
|
+
// ]),
|
|
31
|
+
// ),
|
|
32
|
+
// );
|
|
33
|
+
|
|
34
|
+
// 2) Standalone
|
|
35
|
+
document.body.append(Console.log("Just one line", { a: 1, b: 1 }));
|
|
36
|
+
document.body.append(Console.warn("Standalone warning"));
|
|
37
|
+
document.body.append(Console.table([1, 2, 3]));
|
|
38
|
+
|
|
39
|
+
// 3) Live
|
|
40
|
+
// const feed = createFeed();
|
|
41
|
+
// Hook(console, (e) => feed.push(e));
|
|
42
|
+
// globalThis.c = ConsoleFeed({ feed, variant: theme.val });
|
|
43
|
+
// document.body.append(c);
|
|
44
|
+
|
|
45
|
+
// console.log("Hello from VanJS", 42, true, null, undefined);
|
package/src/utils/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export const isObj = v => v !== null && typeof v === "object"
|
|
2
|
+
export const isProps = a => a && typeof a === "object" && !(a instanceof Node) && Object.getPrototypeOf(a) === Object.prototype
|
|
2
3
|
export const cname = v => { try { return Object.getPrototypeOf(v)?.constructor?.name || "" } catch { return "" } }
|
|
3
4
|
export const prim = v => typeof v === "string" ? JSON.stringify(v) : typeof v === "bigint" ? v + "n" : String(v)
|
|
4
5
|
export const primCls = v => v == null ? "nul" : typeof v === "string" ? "str" : typeof v === "boolean" ? "bool" : "num"
|
package/src/feed/createFeed.js
DELETED
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import van from "https://cdn.jsdelivr.net/npm/vanjs-core@1.5.3/src/van.js";
|
|
2
|
-
import { isObj } from "../utils/index.js";
|
|
3
|
-
|
|
4
|
-
export function createFeed(max = 1000) {
|
|
5
|
-
const logs = van.state([]);
|
|
6
|
-
|
|
7
|
-
let depth = 0;
|
|
8
|
-
let id = 0;
|
|
9
|
-
|
|
10
|
-
const same = (a, b) =>
|
|
11
|
-
a.method === b.method &&
|
|
12
|
-
a.depth === b.depth &&
|
|
13
|
-
a.args.length === b.args.length &&
|
|
14
|
-
a.args.every(
|
|
15
|
-
(x, i) => !isObj(x) && Object.is(x, b.args[i])
|
|
16
|
-
);
|
|
17
|
-
|
|
18
|
-
const push = src => {
|
|
19
|
-
const { method, args = [] } = src;
|
|
20
|
-
|
|
21
|
-
if (method === "clear") {
|
|
22
|
-
depth = 0;
|
|
23
|
-
logs.val = [];
|
|
24
|
-
return;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
if (method === "groupEnd") {
|
|
28
|
-
depth = Math.max(0, depth - 1);
|
|
29
|
-
return;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const isGroup =
|
|
33
|
-
method === "group" ||
|
|
34
|
-
method === "groupCollapsed";
|
|
35
|
-
|
|
36
|
-
const entry = src.count
|
|
37
|
-
? Object.assign(src, {
|
|
38
|
-
id: ++id,
|
|
39
|
-
depth
|
|
40
|
-
})
|
|
41
|
-
: {
|
|
42
|
-
id: ++id,
|
|
43
|
-
method: isGroup ? "group" : method,
|
|
44
|
-
args,
|
|
45
|
-
depth,
|
|
46
|
-
count: van.state(1),
|
|
47
|
-
time: new Date()
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
const last = logs.val.at(-1);
|
|
51
|
-
|
|
52
|
-
if (last && !isGroup && same(last, entry)) {
|
|
53
|
-
last.count.val++;
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
logs.val = [...logs.val, entry].slice(-max);
|
|
58
|
-
|
|
59
|
-
if (isGroup) depth++;
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
return {
|
|
63
|
-
logs,
|
|
64
|
-
push,
|
|
65
|
-
clear: () => push({ method: "clear" })
|
|
66
|
-
};
|
|
67
|
-
}
|