@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2024 ZAKARIA ELALAOUI
3
+ Copyright (c) 2025 ZAKARIA ELALAOUI
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/package.json CHANGED
@@ -1,11 +1,16 @@
1
1
  {
2
2
  "name": "@zikojs/console",
3
- "version": "0.1.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": "^2.0.0-beta.16.0"
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
- import { entryNode, nodeEntry } from "../entries/entryNode.js";
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: van.state("light")
7
- };
8
-
9
- Console.log = entryNode("log", Console.theme);
10
- Console.info = entryNode("info", Console.theme);
11
- Console.debug = entryNode("debug", Console.theme);
12
- Console.warn = entryNode("warn", Console.theme);
13
- Console.error = entryNode("error", Console.theme);
14
- Console.table = entryNode("table", Console.theme);
15
-
16
- Console.group = entryNode("group", Console.theme);
17
-
18
- Console.groupEnd = () => {
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 van from "https://cdn.jsdelivr.net/npm/vanjs-core@1.5.3/src/van.js";
2
- import { Row } from "./Row.js";
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
- const { div } = van.tags;
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
- body.scrollHeight -
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
- visible.val.length
23
- ? div(
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
- requestAnimationFrame(() => {
38
- if (stick) {
39
- body.scrollTop =
40
- body.scrollHeight;
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 { createFeed } from "../feed/createFeed.js";
4
- import { nodeEntry } from "../entries/entryNode.js";
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, ...children] =
46
- isProps(args[0])
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
- Hook(console, feed.push);
59
- }
60
-
61
- children
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
- if (entry) {
67
- feed.push(entry);
68
- }
69
- });
28
+ const initialTheme = typeof variant === "string" ? variant : (variant.val || "dark");
29
+ feed.setTheme(initialTheme);
70
30
 
71
- const theme =
72
- typeof variant === "string"
73
- ? van.state(variant)
74
- : variant;
31
+ const bar = ConsoleToolbar(feed);
32
+ const body = ConsoleBody(feed);
33
+ const prompt = repl ? ConsolePrompt(feed) : "";
75
34
 
76
- const levels = Object.fromEntries(
77
- LEVELS.map(level => [
78
- level,
79
- van.state(true)
80
- ])
35
+ const container = div(
36
+ { class: () => "cf " + feed.theme, style: "flex:1" },
37
+ bar,
38
+ body,
39
+ prompt,
81
40
  );
82
41
 
83
- const query = van.state("");
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 div(
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 { tags } from "ziko/dom";
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
- const run = code => {
10
- feed.push({
11
- method: "command",
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
- method: "result",
18
- args: [(0, eval)(code)]
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
- const inp = input({
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
- if (
39
- e.key === "Enter" &&
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
- index < history.length - 1
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
- const {
4
- div,
5
- button,
6
- input
7
- } = van.tags;
8
-
9
- export function ConsoleToolbar({
10
- levels,
11
- query,
12
- count,
13
- clear
14
- }) {
15
- const LEVELS = [
16
- "log",
17
- "info",
18
- "warn",
19
- "error",
20
- "debug"
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
- LEVELS.map(level =>
27
- button(
26
+ LEVELS.map((l) => {
27
+ countSpans[l] = span(feed.getCount(l));
28
+ chipButtons[l] = button(
28
29
  {
29
- class: () =>
30
- `cf-chip ${level}` +
31
- (levels[level].val ? " on" : ""),
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
- count(level)
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
  }
@@ -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 = entry => {
16
- const [a0, ...rest] = entry.args;
17
-
8
+ const buildRow = (e) => {
9
+ const [a0, ...rest] = e.args;
18
10
  const items =
19
- entry.method === "table" && isObj(a0)
20
- ? [
21
- Table(a0),
22
- ...rest.map(x =>
23
- Inspect(x, undefined, [], true).element
24
- )
25
- ]
26
- : fmt(entry.args).map(a =>
27
- Inspect(a, undefined, [], true).element
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
- class: `cf-row ${entry.method}`,
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
- entry.time.toLocaleTimeString([], {
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 = entry =>
59
- cache.get(entry) ||
60
- (() => {
61
- const node = buildRow(entry);
62
- cache.set(entry, node);
63
- return node;
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,10 @@
1
+ export const nodeEntry = new WeakMap();
2
+
3
+ export const makeEntry = (method, args) => ({
4
+ id: 0,
5
+ method,
6
+ args,
7
+ depth: 0,
8
+ count: 1,
9
+ time: new Date(),
10
+ });
@@ -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 './index.css'
2
- import van from "https://cdn.jsdelivr.net/npm/vanjs-core@1.5.3/src/van.js"
3
- import {
4
- ConsoleFeed,
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, {ConsoleFeed: {Hook, createFeed, ConsoleFeed, Console}})
6
+ Object.assign(window, {
7
+ ConsoleFeed: { Hook, createFeed, ConsoleFeed, Console },
8
+ });
11
9
 
12
10
  /* =====================================================================
13
11
  Demo
14
12
  ===================================================================== */
15
13
 
16
- const theme = van.state("light")
17
-
18
- // 1) Declarative: children only, or props first then children
19
- document.body.append(
20
- ConsoleFeed({variant: theme, repl: false},
21
- Console.log("Hello from VanJS", 42, true, null, undefined),
22
- Console.info("Server listening on :3000"),
23
- Console.group("Request"),
24
- Console.log("parsing"),
25
- Console.warn("token expires soon"),
26
- Console.groupEnd(),
27
- Console.error(new Error("Something broke")),
28
- Console.table([{id: 1, name: "Ada"}, {id: 2, name: "Linus"}]),
29
- )
30
- )
31
-
32
- // 2) Standalone: a single row, no toolbar
33
- document.body.append(Console.log("Just one line", {a: 1, b : 1}))
34
- document.body.append(Console.warn("Standalone warning"))
35
- document.body.append(Console.table([1,2,3]))
36
-
37
-
38
- // 3) Live: hook the real console into a feed
39
- Console.theme = theme
40
- const feed = createFeed()
41
- Hook(console, e => feed.push(e))
42
- globalThis.c = ConsoleFeed({feed})
43
- document.body.append(c)
44
-
45
- console.log("Hello from VanJS", 42, true, null, undefined)
46
- console.log({a: 1})
47
- console.group("Request")
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);
@@ -0,0 +1,11 @@
1
+ export const LEVEL = {
2
+ log: "log",
3
+ dir: "log",
4
+ table: "log",
5
+ group: "log",
6
+ info: "info",
7
+ debug: "debug",
8
+ warn: "warn",
9
+ error: "error",
10
+ };
11
+ export const LEVELS = ["log", "info", "warn", "error", "debug"];
@@ -0,0 +1,5 @@
1
+ export const same = (a, b) =>
2
+ a.method === b.method &&
3
+ a.depth === b.depth &&
4
+ a.args.length === b.args.length &&
5
+ a.args.every((x, i) => !isObj(x) && Object.is(x, b.args[i])); // is v f
@@ -1,2 +1,4 @@
1
1
  export * from './value-formatting.js'
2
- export * from './fmt.js'
2
+ export * from './fmt.js'
3
+ export * from './feed.js'
4
+ export * from './const.js'
@@ -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"
@@ -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
- }