@praxisjs/devtools 0.2.34 → 0.3.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/CHANGELOG.md +32 -0
- package/assets/uno.generated.css +5 -0
- package/dist/assets/logo.svg.js +4 -0
- package/dist/assets/tailwind-v4.css.js +4 -0
- package/dist/assets/uno.generated.css.js +4 -0
- package/dist/core/event-bus.js +21 -0
- package/dist/core/profiler.js +79 -0
- package/dist/core/registry.js +147 -0
- package/dist/decorators/debug.js +126 -0
- package/dist/decorators/trace.js +45 -0
- package/dist/icons/ellipsis-vertical.js +38 -0
- package/dist/icons/panel-bottom.js +28 -0
- package/dist/icons/panel-left.js +28 -0
- package/dist/icons/panel-right.js +28 -0
- package/dist/icons/panel-top.js +28 -0
- package/dist/icons/x.js +22 -0
- package/dist/index.d.ts +96 -3
- package/dist/index.js +10 -1157
- package/dist/plugins/components/components/component-detail.js +53 -0
- package/dist/plugins/components/components/component-row.js +22 -0
- package/dist/plugins/components/components/detail-row.js +15 -0
- package/dist/plugins/components/components/detail-section.js +14 -0
- package/dist/plugins/components/components/status-dot.js +15 -0
- package/dist/plugins/components/components-tab.js +54 -0
- package/dist/plugins/components/index.js +9 -0
- package/dist/plugins/performance/index.js +9 -0
- package/dist/plugins/performance/performance-tab.js +62 -0
- package/dist/plugins/signals/components/signal-detail.js +29 -0
- package/dist/plugins/signals/components/signal-row.js +17 -0
- package/dist/plugins/signals/index.js +9 -0
- package/dist/plugins/signals/signals-tab.js +53 -0
- package/dist/plugins/timeline/components/badge.js +16 -0
- package/dist/plugins/timeline/components/timeline-row.js +33 -0
- package/dist/plugins/timeline/constants.js +54 -0
- package/dist/plugins/timeline/index.js +9 -0
- package/dist/plugins/timeline/timeline-tab.js +46 -0
- package/dist/ui/dev-tools.js +74 -0
- package/dist/ui/panel.js +106 -0
- package/dist/ui/shared/empty-state.js +14 -0
- package/dist/ui/shared/icon-button.js +17 -0
- package/dist/ui/shared/panel-section.js +14 -0
- package/dist/ui/shared/search-input.js +17 -0
- package/dist/ui/shared/side-panel.js +15 -0
- package/dist/utils/format-time.js +7 -0
- package/dist/utils/format-value.js +14 -0
- package/package.json +14 -6
- package/src/core/profiler.ts +162 -0
- package/src/core/registry.ts +23 -21
- package/src/decorators/debug.ts +96 -32
- package/src/decorators/index.ts +1 -0
- package/src/decorators/trace.ts +27 -4
- package/src/index.ts +5 -0
- package/src/plugins/performance/index.ts +9 -0
- package/src/plugins/performance/performance-tab.tsx +111 -0
- package/src/ui/dev-tools.tsx +5 -0
- package/src/__tests__/debug.test.ts +0 -333
- package/src/__tests__/event-bus.test.ts +0 -123
- package/src/__tests__/format-time.test.ts +0 -52
- package/src/__tests__/format-value.test.ts +0 -46
- package/src/__tests__/plugins.test.ts +0 -101
- package/src/__tests__/registry.test.ts +0 -386
- package/src/__tests__/trace.test.ts +0 -200
- package/tsconfig.json +0 -21
- package/uno.config.ts +0 -57
- package/vite.config.ts +0 -38
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { SidePanel as e } from "../../../ui/shared/side-panel.js";
|
|
2
|
+
import { formatValue as t } from "../../../utils/format-value.js";
|
|
3
|
+
import { DetailRow as n } from "./detail-row.js";
|
|
4
|
+
import { DetailSection as r } from "./detail-section.js";
|
|
5
|
+
import { StatusDot as i } from "./status-dot.js";
|
|
6
|
+
import { _$element as a, _$html as o, _$insert as s, _$insertOnly as c, _$scope as l, _$template as u } from "@praxisjs/runtime";
|
|
7
|
+
import { jsx as d, jsxs as f } from "@praxisjs/jsx/jsx-runtime";
|
|
8
|
+
import { StatelessComponent as p } from "@praxisjs/core";
|
|
9
|
+
import { Component as m } from "@praxisjs/decorators";
|
|
10
|
+
//#region src/plugins/components/components/component-detail.tsx
|
|
11
|
+
var h = /* @__PURE__ */ u(() => a("div", { class: "px-3 py-2 border-b border-border flex items-center gap-2 bg-bg shrink-0" }, [a("span", { class: "text-accent font-mono text-[11px] font-semibold truncate pl-1" }, ["<", ">"])])), g = /* @__PURE__ */ o("<div class=\"flex-1 overflow-y-auto\">"), _ = /* @__PURE__ */ o("<div class=\"px-3 py-2 flex justify-between items-center border-b border-border\"><span class=\"text-[11px] text-text font-mono\"></span><span class=\"text-[10px] text-subtle tabular-nums\">"), v = @m() class extends p {
|
|
12
|
+
render() {
|
|
13
|
+
let { entry: a, signals: o } = this.props;
|
|
14
|
+
return /* @__PURE__ */ f(e, { children: [((e) => {
|
|
15
|
+
let t = h(), n = t.firstChild, r = n.firstChild.nextSibling;
|
|
16
|
+
return s(t, /* @__PURE__ */ d(i, { status: a.status }), n, e), s(n, a.name, r, e), t;
|
|
17
|
+
})(l()), ((e) => {
|
|
18
|
+
let i = g();
|
|
19
|
+
return s(i, /* @__PURE__ */ f(r, {
|
|
20
|
+
label: "Stats",
|
|
21
|
+
children: [
|
|
22
|
+
/* @__PURE__ */ d(n, {
|
|
23
|
+
k: "renders",
|
|
24
|
+
v: String(a.renderCount)
|
|
25
|
+
}),
|
|
26
|
+
/* @__PURE__ */ d(n, {
|
|
27
|
+
k: "last render",
|
|
28
|
+
v: `${a.lastRenderDuration.toFixed(3)}ms`
|
|
29
|
+
}),
|
|
30
|
+
/* @__PURE__ */ d(n, {
|
|
31
|
+
k: "status",
|
|
32
|
+
v: a.status
|
|
33
|
+
})
|
|
34
|
+
]
|
|
35
|
+
}), null, e), s(i, o.length > 0 && /* @__PURE__ */ d(r, {
|
|
36
|
+
label: "State",
|
|
37
|
+
children: o.map((e) => /* @__PURE__ */ d(n, {
|
|
38
|
+
k: e.label,
|
|
39
|
+
v: t(e.value),
|
|
40
|
+
signal: !0
|
|
41
|
+
}, e.id))
|
|
42
|
+
}), null, e), s(i, a.lifecycle.length > 0 && /* @__PURE__ */ d(r, {
|
|
43
|
+
label: "Lifecycle",
|
|
44
|
+
children: [...a.lifecycle].reverse().slice(0, 20).map((e, t) => {
|
|
45
|
+
let n = l(), r = _(), i = r.firstChild, a = i.nextSibling;
|
|
46
|
+
return c(i, e.hook, n), c(a, new Date(e.timestamp).toLocaleTimeString(), n), r;
|
|
47
|
+
})
|
|
48
|
+
}), null, e), i;
|
|
49
|
+
})(l())] });
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
//#endregion
|
|
53
|
+
export { v as ComponentDetail };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { StatusDot as e } from "./status-dot.js";
|
|
2
|
+
import { _$className as t, _$element as n, _$event as r, _$html as i, _$insert as a, _$onEvent as o, _$prop as s, _$reactive as c, _$scope as l, _$template as u } from "@praxisjs/runtime";
|
|
3
|
+
import { jsx as d } from "@praxisjs/jsx/jsx-runtime";
|
|
4
|
+
import { StatelessComponent as f } from "@praxisjs/core";
|
|
5
|
+
import { Component as p } from "@praxisjs/decorators";
|
|
6
|
+
//#region src/plugins/components/components/component-row.tsx
|
|
7
|
+
var m = /* @__PURE__ */ r("onClick"), h = /* @__PURE__ */ u(() => n("div", null, [
|
|
8
|
+
0,
|
|
9
|
+
n("span", { class: "text-accent font-mono text-[11px] flex-1 truncate pl-1" }, ["<", ">"]),
|
|
10
|
+
n("span", { class: "text-muted text-[11px] tabular-nums" }, ["×"]),
|
|
11
|
+
n("span", null, ["ms"])
|
|
12
|
+
])), g = /* @__PURE__ */ i("<span class=\"absolute left-0 top-0 bottom-0 w-[2px] bg-accent rounded-r\">"), _ = @p() class extends f {
|
|
13
|
+
render() {
|
|
14
|
+
let { entry: n, selected: r, onClick: i } = this.props;
|
|
15
|
+
{
|
|
16
|
+
let u = l(), f = h(), p = f.firstChild, _ = p.nextSibling, v = _.nextSibling, y = v.nextSibling, b = _.firstChild.nextSibling, x = y.firstChild;
|
|
17
|
+
return c(p, () => r() && g(), u), a(f, /* @__PURE__ */ d(e, { status: n.status }), _, u), a(_, n.name, b, u), a(v, n.renderCount, null, u), a(y, n.lastRenderDuration.toFixed(1), x, u), s(y, "class", `text-[10px] tabular-nums w-12 text-right ${n.lastRenderDuration > 16 ? "text-warn" : "text-subtle"}`, u), o(f, m, i, u), t(f, () => `relative flex items-center gap-2 px-3 py-2 cursor-pointer border-b border-border transition-colors duration-100 ${r() ? "bg-selected" : "hover:bg-section"}`, u), f;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
//#endregion
|
|
22
|
+
export { _ as ComponentRow };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { _$html as e, _$insertOnly as t, _$prop as n, _$scope as r } from "@praxisjs/runtime";
|
|
2
|
+
import { StatelessComponent as i } from "@praxisjs/core";
|
|
3
|
+
import { Component as a } from "@praxisjs/decorators";
|
|
4
|
+
//#region src/plugins/components/components/detail-row.tsx
|
|
5
|
+
var o = /* @__PURE__ */ e("<div class=\"flex justify-between items-center px-3 py-2 border-b border-border gap-3\"><span></span><span class=\"text-[11px] text-text font-mono truncate text-right\">"), s = @a() class extends i {
|
|
6
|
+
render() {
|
|
7
|
+
let { k: e, v: i, signal: a } = this.props;
|
|
8
|
+
{
|
|
9
|
+
let s = r(), c = o(), l = c.firstChild, u = l.nextSibling;
|
|
10
|
+
return t(l, e, s), n(l, "class", `text-[11px] font-mono shrink-0 ${a ? "text-accent" : "text-muted"}`, s), t(u, i, s), c;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
//#endregion
|
|
15
|
+
export { s as DetailRow };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { _$html as e, _$insert as t, _$insertOnly as n, _$scope as r } from "@praxisjs/runtime";
|
|
2
|
+
import { StatelessComponent as i } from "@praxisjs/core";
|
|
3
|
+
import { Component as a } from "@praxisjs/decorators";
|
|
4
|
+
//#region src/plugins/components/components/detail-section.tsx
|
|
5
|
+
var o = /* @__PURE__ */ e("<div class=\"border-b border-border\"><div class=\"px-3 py-[4px] text-[9px] text-subtle font-bold tracking-[0.12em] uppercase bg-section border-b border-border\">"), s = @a() class extends i {
|
|
6
|
+
render() {
|
|
7
|
+
{
|
|
8
|
+
let e = r(), i = o(), a = i.firstChild;
|
|
9
|
+
return n(a, this.props.label, e), t(i, this.props.children, null, e), i;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
//#endregion
|
|
14
|
+
export { s as DetailSection };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { jsx as e } from "@praxisjs/jsx/jsx-runtime";
|
|
2
|
+
import { StatelessComponent as t } from "@praxisjs/core";
|
|
3
|
+
import { Component as n } from "@praxisjs/decorators";
|
|
4
|
+
//#region src/plugins/components/components/status-dot.tsx
|
|
5
|
+
var r = @n() class extends t {
|
|
6
|
+
render() {
|
|
7
|
+
let { status: t } = this.props;
|
|
8
|
+
return /* @__PURE__ */ e("span", {
|
|
9
|
+
class: `inline-block w-[6px] h-[6px] rounded-full shrink-0 ${t === "mounted" ? "bg-success" : "bg-subtle"}`,
|
|
10
|
+
style: t === "mounted" ? { boxShadow: "0 0 6px rgba(14,165,122,0.7)" } : void 0
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
//#endregion
|
|
15
|
+
export { r as StatusDot };
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { EmptyState as e } from "../../ui/shared/empty-state.js";
|
|
2
|
+
import { ComponentDetail as t } from "./components/component-detail.js";
|
|
3
|
+
import { ComponentRow as n } from "./components/component-row.js";
|
|
4
|
+
import { _$html as r, _$reactive as i, _$reactiveOnly as a, _$scope as o } from "@praxisjs/runtime";
|
|
5
|
+
import { jsx as s } from "@praxisjs/jsx/jsx-runtime";
|
|
6
|
+
import { StatefulComponent as c } from "@praxisjs/core";
|
|
7
|
+
import { Component as l, Prop as u, State as d } from "@praxisjs/decorators";
|
|
8
|
+
//#region src/plugins/components/components-tab.tsx
|
|
9
|
+
var f = /* @__PURE__ */ r("<div class=\"flex h-full overflow-hidden\"><div class=\"flex-1 flex flex-col overflow-hidden min-w-0\"><div class=\"flex items-center px-3 h-7 text-[9px] text-subtle font-bold tracking-[0.12em] uppercase border-b border-border bg-section gap-2 shrink-0\"><span class=flex-1>Component</span><span>Renders</span><span class=\"w-12 text-right\">Last</span></div><div class=\"flex-1 overflow-y-auto\"></div></div><!---->"), p = @l() class extends c {
|
|
10
|
+
@d() components = [];
|
|
11
|
+
@d() selectedId = null;
|
|
12
|
+
@u() registry;
|
|
13
|
+
_handlers = [];
|
|
14
|
+
onMount() {
|
|
15
|
+
this.components = this.registry.getComponents();
|
|
16
|
+
let e = () => {
|
|
17
|
+
this.components = this.registry.getComponents();
|
|
18
|
+
};
|
|
19
|
+
this._handlers = [
|
|
20
|
+
"component:registered",
|
|
21
|
+
"component:render",
|
|
22
|
+
"component:unmount",
|
|
23
|
+
"lifecycle",
|
|
24
|
+
"signal:registered",
|
|
25
|
+
"signal:changed"
|
|
26
|
+
].map((t) => this.registry.bus.on(t, e));
|
|
27
|
+
}
|
|
28
|
+
onUnmount() {
|
|
29
|
+
this._handlers.forEach((e) => {
|
|
30
|
+
e();
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
render() {
|
|
34
|
+
let { registry: r } = this;
|
|
35
|
+
{
|
|
36
|
+
let c = o(), l = f(), u = l.firstChild, d = u.nextSibling, p = u.firstChild.nextSibling;
|
|
37
|
+
return a(p, () => this.components.length === 0 ? /* @__PURE__ */ s(e, { message: "No components tracked. Add @Trace() to component classes." }) : this.components.map((e) => /* @__PURE__ */ s(n, {
|
|
38
|
+
entry: e,
|
|
39
|
+
selected: () => this.selectedId === e.id,
|
|
40
|
+
onClick: () => {
|
|
41
|
+
this.selectedId = this.selectedId === e.id ? null : e.id;
|
|
42
|
+
}
|
|
43
|
+
}, e.id)), c), i(d, () => {
|
|
44
|
+
let e = this.selectedId, n = e ? this.components.find((t) => t.id === e) ?? null : null, i = e ? r.getSignalsByComponent(e) : [];
|
|
45
|
+
return n ? /* @__PURE__ */ s(t, {
|
|
46
|
+
entry: n,
|
|
47
|
+
signals: i
|
|
48
|
+
}) : null;
|
|
49
|
+
}, c), l;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
//#endregion
|
|
54
|
+
export { p as ComponentsTab };
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { EmptyState as e } from "../../ui/shared/empty-state.js";
|
|
2
|
+
import { For as t, _$className as n, _$event as r, _$html as i, _$insertOnly as a, _$onEvent as o, _$prop as s, _$reactiveOnly as c, _$scope as l } from "@praxisjs/runtime";
|
|
3
|
+
import { jsx as u } from "@praxisjs/jsx/jsx-runtime";
|
|
4
|
+
import { StatefulComponent as d } from "@praxisjs/core";
|
|
5
|
+
import { Component as f, Prop as p, State as m } from "@praxisjs/decorators";
|
|
6
|
+
//#region src/plugins/performance/performance-tab.tsx
|
|
7
|
+
var h = /* @__PURE__ */ r("onClick"), g = /* @__PURE__ */ i("<div class=\"flex flex-col h-full overflow-hidden\"><div class=\"flex items-center gap-[6px] px-2 py-2 border-b border-border bg-bg shrink-0\"><button></button><button>Reset</button><div class=flex-1></div><span class=\"text-[10px] text-subtle font-mono\"></span></div><div class=\"grid grid-cols-4 gap-px bg-border border-b border-border shrink-0\"></div><div class=\"grid grid-cols-[1.4fr_0.6fr_0.8fr_0.8fr] items-center px-3 h-7 text-[9px] text-subtle font-bold tracking-[0.12em] uppercase border-b border-border bg-section gap-2 shrink-0\"><span>Component</span><span>Runs</span><span>Time</span><span>Slowest</span></div><div class=\"flex-1 overflow-y-auto\">"), _ = /* @__PURE__ */ i("<div class=\"bg-bg px-3 py-2\"><div class=\"text-[9px] text-subtle font-bold tracking-[0.12em] uppercase\"></div><div class=\"text-[13px] text-text font-mono\">"), v = /* @__PURE__ */ i("<div class=\"grid grid-cols-[1.4fr_0.6fr_0.8fr_0.8fr] items-center px-3 h-7 text-[11px] border-b border-border gap-2 font-mono\"><span class=\"text-accent truncate\"></span><span class=text-text></span><span class=text-text></span><span class=text-muted>"), y = "text-[11px] px-2 py-[3px] rounded cursor-pointer font-sans border border-border text-muted hover:text-text transition-colors duration-150";
|
|
8
|
+
function b(e) {
|
|
9
|
+
return `${e.toFixed(e < 10 ? 2 : 1)} ms`;
|
|
10
|
+
}
|
|
11
|
+
var x = @f() class extends d {
|
|
12
|
+
@m() snapshot = null;
|
|
13
|
+
@p() registry;
|
|
14
|
+
_off = null;
|
|
15
|
+
onMount() {
|
|
16
|
+
let { profiler: e } = this.registry;
|
|
17
|
+
this.refresh(), this._off = e.bus.on("profile:changed", () => {
|
|
18
|
+
this.refresh();
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
onUnmount() {
|
|
22
|
+
this._off?.();
|
|
23
|
+
}
|
|
24
|
+
refresh() {
|
|
25
|
+
let { profiler: e } = this.registry;
|
|
26
|
+
e.muted(() => {
|
|
27
|
+
this.snapshot = e.snapshot();
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
render() {
|
|
31
|
+
let { profiler: r } = this.registry;
|
|
32
|
+
{
|
|
33
|
+
let i = l(), d = g(), f = d.firstChild, p = f.nextSibling, m = p.nextSibling.nextSibling, x = f.firstChild, S = x.nextSibling, C = S.nextSibling.nextSibling;
|
|
34
|
+
return c(x, () => this.snapshot?.recording ? "Stop" : "Record", i), n(x, () => `${y} ${this.snapshot?.recording ? "text-warn border-warn" : ""}`, i), o(x, h, () => {
|
|
35
|
+
r.isRecording ? r.stop() : r.start();
|
|
36
|
+
}, i), s(S, "class", y, i), o(S, h, () => {
|
|
37
|
+
r.reset();
|
|
38
|
+
}, i), c(C, () => this.snapshot ? `${(this.snapshot.duration / 1e3).toFixed(1)} s` : "", i), c(p, () => {
|
|
39
|
+
let e = this.snapshot;
|
|
40
|
+
return (e ? [
|
|
41
|
+
["Writes", String(e.writes)],
|
|
42
|
+
["Effect runs", String(e.effectRuns)],
|
|
43
|
+
["Effect time", b(e.effectMs)],
|
|
44
|
+
["Computed runs", `${String(e.computeRuns)} · ${b(e.computeMs)}`]
|
|
45
|
+
] : []).map(([e, t]) => {
|
|
46
|
+
let n = l(), r = _(), i = r.firstChild, o = i.nextSibling;
|
|
47
|
+
return a(i, e, n), a(o, t, n), r;
|
|
48
|
+
});
|
|
49
|
+
}, i), a(m, /* @__PURE__ */ u(t, {
|
|
50
|
+
each: () => this.snapshot?.owners ?? [],
|
|
51
|
+
by: "name",
|
|
52
|
+
fallback: /* @__PURE__ */ u(e, { message: "Press Record, use your app, then Stop. Bindings and effects are grouped by the component that created them." }),
|
|
53
|
+
children: (e) => {
|
|
54
|
+
let t = l(), n = v(), r = n.firstChild, i = r.nextSibling, a = i.nextSibling, o = a.nextSibling;
|
|
55
|
+
return c(r, () => e().name, t), c(i, () => String(e().effectRuns), t), c(a, () => b(e().effectMs), t), c(o, () => b(e().maxMs), t), n;
|
|
56
|
+
}
|
|
57
|
+
}), i), d;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
//#endregion
|
|
62
|
+
export { x as PerformanceTab };
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { SidePanel as e } from "../../../ui/shared/side-panel.js";
|
|
2
|
+
import { formatValue as t } from "../../../utils/format-value.js";
|
|
3
|
+
import { PanelSection as n } from "../../../ui/shared/panel-section.js";
|
|
4
|
+
import { time as r } from "../../../utils/format-time.js";
|
|
5
|
+
import { _$html as i, _$insert as a, _$insertOnly as o, _$scope as s } from "@praxisjs/runtime";
|
|
6
|
+
import { jsx as c } from "@praxisjs/jsx/jsx-runtime";
|
|
7
|
+
import { StatelessComponent as l } from "@praxisjs/core";
|
|
8
|
+
import { Component as u } from "@praxisjs/decorators";
|
|
9
|
+
//#region src/plugins/signals/components/signal-detail.tsx
|
|
10
|
+
var d = /* @__PURE__ */ i("<div class=overflow-y-auto>"), f = /* @__PURE__ */ i("<div class=\"px-3 py-2 border-b border-border flex justify-between items-center gap-3\"><span class=\"font-mono text-[11px] text-text truncate\"></span><span class=\"text-[10px] text-subtle shrink-0 tabular-nums\">"), p = /* @__PURE__ */ i("<p class=\"px-3 py-6 text-[11px] text-subtle text-center\">No history yet."), m = @u() class extends l {
|
|
11
|
+
render() {
|
|
12
|
+
let { entry: i } = this.props;
|
|
13
|
+
return /* @__PURE__ */ c(e, {
|
|
14
|
+
width: "260px",
|
|
15
|
+
children: /* @__PURE__ */ c(n, {
|
|
16
|
+
label: "History",
|
|
17
|
+
children: ((e) => {
|
|
18
|
+
let n = d();
|
|
19
|
+
return a(n, [...i.history].reverse().map((e, n) => {
|
|
20
|
+
let i = s(), a = f(), c = a.firstChild, l = c.nextSibling;
|
|
21
|
+
return o(c, t(e.value), i), o(l, r(e.timestamp, "ago"), i), a;
|
|
22
|
+
}), null, e), a(n, i.history.length === 0 && p(), null, e), n;
|
|
23
|
+
})(s())
|
|
24
|
+
})
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
//#endregion
|
|
29
|
+
export { m as SignalDetail };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { formatValue as e } from "../../../utils/format-value.js";
|
|
2
|
+
import { time as t } from "../../../utils/format-time.js";
|
|
3
|
+
import { _$event as n, _$html as r, _$insert as i, _$insertOnly as a, _$onEvent as o, _$prop as s, _$scope as c } from "@praxisjs/runtime";
|
|
4
|
+
import { StatelessComponent as l } from "@praxisjs/core";
|
|
5
|
+
import { Component as u } from "@praxisjs/decorators";
|
|
6
|
+
//#region src/plugins/signals/components/signal-row.tsx
|
|
7
|
+
var d = /* @__PURE__ */ n("onClick"), f = /* @__PURE__ */ r("<div><span class=\"text-accent font-mono text-[11px] truncate pl-1\"></span><span class=\"text-muted text-[11px] truncate\"></span><span class=\"text-text font-mono text-[11px] truncate\"></span><span class=\"text-subtle text-[10px] text-right tabular-nums\">"), p = /* @__PURE__ */ r("<span class=\"absolute left-0 top-0 bottom-0 w-[2px] bg-accent rounded-r\">"), m = @u() class extends l {
|
|
8
|
+
render() {
|
|
9
|
+
let { entry: n, selected: r, onClick: l } = this.props;
|
|
10
|
+
{
|
|
11
|
+
let u = c(), m = f(), h = m.firstChild, g = h.nextSibling, _ = g.nextSibling, v = _.nextSibling;
|
|
12
|
+
return i(m, r && p(), h, u), a(h, n.label, u), a(g, n.componentName, u), a(_, e(n.value), u), a(v, t(n.changedAt, "ago"), u), o(m, d, l, u), s(m, "class", `relative grid grid-cols-[1.2fr_0.8fr_1fr_auto] items-center px-3 py-2 cursor-pointer border-b border-border transition-colors duration-100 ${r ? "bg-selected" : "hover:bg-section"}`, u), m;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
//#endregion
|
|
17
|
+
export { m as SignalRow };
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { EmptyState as e } from "../../ui/shared/empty-state.js";
|
|
2
|
+
import { SearchInput as t } from "../../ui/shared/search-input.js";
|
|
3
|
+
import { SignalDetail as n } from "./components/signal-detail.js";
|
|
4
|
+
import { SignalRow as r } from "./components/signal-row.js";
|
|
5
|
+
import { _$html as i, _$insertOnly as a, _$reactive as o, _$reactiveOnly as s, _$scope as c } from "@praxisjs/runtime";
|
|
6
|
+
import { jsx as l } from "@praxisjs/jsx/jsx-runtime";
|
|
7
|
+
import { StatefulComponent as u } from "@praxisjs/core";
|
|
8
|
+
import { Component as d, Prop as f, State as p } from "@praxisjs/decorators";
|
|
9
|
+
//#region src/plugins/signals/signals-tab.tsx
|
|
10
|
+
var m = /* @__PURE__ */ i("<div class=\"flex h-full overflow-hidden\"><div class=\"flex-1 flex flex-col overflow-hidden min-w-0\"><div class=\"px-3 py-2 border-b border-border bg-bg shrink-0\"></div><div class=\"grid grid-cols-[1.2fr_0.8fr_1fr_auto] items-center px-3 h-7 text-[9px] text-subtle font-bold tracking-[0.12em] uppercase border-b border-border bg-section gap-2 shrink-0\"><span>Signal</span><span>Component</span><span>Value</span><span>Age</span></div><div class=\"flex-1 overflow-y-auto\"></div></div><!---->"), h = @d() class extends u {
|
|
11
|
+
@p() signals = [];
|
|
12
|
+
@p() search = "";
|
|
13
|
+
@p() selectedId = null;
|
|
14
|
+
@f() registry;
|
|
15
|
+
_handlers = [];
|
|
16
|
+
onMount() {
|
|
17
|
+
this.signals = this.registry.getSignals(), this._handlers = [this.registry.bus.on("signal:registered", () => {
|
|
18
|
+
this.signals = this.registry.getSignals();
|
|
19
|
+
}), this.registry.bus.on("signal:changed", () => {
|
|
20
|
+
this.signals = this.registry.getSignals();
|
|
21
|
+
})];
|
|
22
|
+
}
|
|
23
|
+
onUnmount() {
|
|
24
|
+
this._handlers.forEach((e) => {
|
|
25
|
+
e();
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
render() {
|
|
29
|
+
{
|
|
30
|
+
let i = c(), u = m(), d = u.firstChild, f = d.nextSibling, p = d.firstChild, h = p.nextSibling.nextSibling;
|
|
31
|
+
return a(p, /* @__PURE__ */ l(t, {
|
|
32
|
+
placeholder: "Search signals…",
|
|
33
|
+
onInput: (e) => {
|
|
34
|
+
this.search = e;
|
|
35
|
+
}
|
|
36
|
+
}), i), s(h, () => {
|
|
37
|
+
let t = this.search.toLowerCase(), n = t === "" ? this.signals : this.signals.filter((e) => e.label.toLowerCase().includes(t) || e.componentName.toLowerCase().includes(t));
|
|
38
|
+
return n.length === 0 ? /* @__PURE__ */ l(e, { message: this.signals.length === 0 ? "No signals tracked. Add @Debug() on top of @State() properties." : "No signals match your search." }) : n.map((e) => /* @__PURE__ */ l(r, {
|
|
39
|
+
entry: e,
|
|
40
|
+
selected: this.selectedId === e.id,
|
|
41
|
+
onClick: () => {
|
|
42
|
+
this.selectedId = this.selectedId === e.id ? null : e.id;
|
|
43
|
+
}
|
|
44
|
+
}, e.id));
|
|
45
|
+
}, i), o(f, () => {
|
|
46
|
+
let e = this.selectedId, t = e ? this.signals.find((t) => t.id === e) ?? null : null;
|
|
47
|
+
return t ? /* @__PURE__ */ l(n, { entry: t }) : null;
|
|
48
|
+
}, i), u;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
//#endregion
|
|
53
|
+
export { h as SignalsTab };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { TYPE_META as e } from "../constants.js";
|
|
2
|
+
import { jsx as t } from "@praxisjs/jsx/jsx-runtime";
|
|
3
|
+
import { StatelessComponent as n } from "@praxisjs/core";
|
|
4
|
+
import { Component as r } from "@praxisjs/decorators";
|
|
5
|
+
//#region src/plugins/timeline/components/badge.tsx
|
|
6
|
+
var i = @r() class extends n {
|
|
7
|
+
render() {
|
|
8
|
+
let n = e[this.props.type];
|
|
9
|
+
return /* @__PURE__ */ t("span", {
|
|
10
|
+
class: `text-[9px] px-[6px] py-[2px] rounded font-bold uppercase tracking-[0.07em] shrink-0 ${n.cls}`,
|
|
11
|
+
children: n.label
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
//#endregion
|
|
16
|
+
export { i as Badge };
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { time as e } from "../../../utils/format-time.js";
|
|
2
|
+
import { Badge as t } from "./badge.js";
|
|
3
|
+
import { _$event as n, _$html as r, _$insert as i, _$insertOnly as a, _$onEvent as o, _$prop as s, _$reactive as c, _$reactiveOnly as l, _$scope as u } from "@praxisjs/runtime";
|
|
4
|
+
import { jsx as d } from "@praxisjs/jsx/jsx-runtime";
|
|
5
|
+
import { StatefulComponent as f } from "@praxisjs/core";
|
|
6
|
+
import { Component as p, Prop as m, State as h } from "@praxisjs/decorators";
|
|
7
|
+
//#region src/plugins/timeline/components/timeline-row.tsx
|
|
8
|
+
var g = /* @__PURE__ */ n("onClick"), _ = /* @__PURE__ */ r("<div class=\"border-b border-border\"><div><span class=\"text-subtle text-[10px] w-10 shrink-0 text-right tabular-nums font-mono\"></span><span class=\"flex-1 text-text font-mono text-[11px] truncate\"></span></div><!---->"), v = /* @__PURE__ */ r("<span class=\"text-subtle text-[11px] shrink-0 w-4 text-center\">"), y = /* @__PURE__ */ r("<div class=\"px-3 py-2 pl-[56px] font-mono text-[11px] bg-section border-t border-border\">"), b = /* @__PURE__ */ r("<div class=\"flex gap-3 py-[3px]\"><span class=\"text-accent shrink-0\">:</span><span class=\"text-muted truncate\">"), x = @p() class extends f {
|
|
9
|
+
@h() open = !1;
|
|
10
|
+
@m() entry;
|
|
11
|
+
render() {
|
|
12
|
+
let n = this.entry;
|
|
13
|
+
if (!n) return null;
|
|
14
|
+
let r = Object.keys(n.data).length > 0;
|
|
15
|
+
{
|
|
16
|
+
let f = u(), p = _(), m = p.firstChild, h = m.nextSibling, x = m.firstChild, S = x.nextSibling;
|
|
17
|
+
return a(x, e(n.timestamp), f), i(m, /* @__PURE__ */ d(t, { type: n.type }), S, f), a(S, n.label, f), i(m, r && ((e) => {
|
|
18
|
+
let t = v();
|
|
19
|
+
return l(t, () => this.open ? "▾" : "▸", e), t;
|
|
20
|
+
})(u()), null, f), o(m, g, () => {
|
|
21
|
+
r && (this.open = !this.open);
|
|
22
|
+
}, f), s(m, "class", `flex items-center gap-2 px-3 py-2 transition-colors duration-100 ${r ? "cursor-pointer hover:bg-section" : "cursor-default"}`, f), c(h, () => this.open && r ? ((e) => {
|
|
23
|
+
let t = y();
|
|
24
|
+
return a(t, Object.entries(n.data).map(([e, t]) => {
|
|
25
|
+
let n = u(), r = b(), o = r.firstChild, s = o.nextSibling, c = o.firstChild;
|
|
26
|
+
return i(o, e, c, n), a(s, typeof t == "object" ? JSON.stringify(t) : String(t), n), r;
|
|
27
|
+
}), e), t;
|
|
28
|
+
})(u()) : null, f), p;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
//#endregion
|
|
33
|
+
export { x as TimelineRow };
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
//#region src/plugins/timeline/constants.ts
|
|
2
|
+
var e = {
|
|
3
|
+
"signal:change": {
|
|
4
|
+
label: "signal",
|
|
5
|
+
cls: "text-[#9b90e6] bg-[rgba(155,144,230,0.14)]"
|
|
6
|
+
},
|
|
7
|
+
"component:render": {
|
|
8
|
+
label: "render",
|
|
9
|
+
cls: "text-[#4ade80] bg-[rgba(74,222,128,0.12)]"
|
|
10
|
+
},
|
|
11
|
+
"component:mount": {
|
|
12
|
+
label: "mount",
|
|
13
|
+
cls: "text-[#0ea57a] bg-[rgba(14,165,122,0.14)]"
|
|
14
|
+
},
|
|
15
|
+
"component:unmount": {
|
|
16
|
+
label: "unmount",
|
|
17
|
+
cls: "text-[#dc2626] bg-[rgba(220,38,38,0.14)]"
|
|
18
|
+
},
|
|
19
|
+
lifecycle: {
|
|
20
|
+
label: "lifecycle",
|
|
21
|
+
cls: "text-[#d97706] bg-[rgba(217,119,6,0.14)]"
|
|
22
|
+
},
|
|
23
|
+
"method:call": {
|
|
24
|
+
label: "method",
|
|
25
|
+
cls: "text-[#7c6dd6] bg-[rgba(124,109,214,0.14)]"
|
|
26
|
+
}
|
|
27
|
+
}, t = [
|
|
28
|
+
{
|
|
29
|
+
value: "all",
|
|
30
|
+
label: "All"
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
value: "signal:change",
|
|
34
|
+
label: "Signals"
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
value: "component:render",
|
|
38
|
+
label: "Renders"
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
value: "component:mount",
|
|
42
|
+
label: "Mount"
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
value: "lifecycle",
|
|
46
|
+
label: "Lifecycle"
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
value: "method:call",
|
|
50
|
+
label: "Methods"
|
|
51
|
+
}
|
|
52
|
+
];
|
|
53
|
+
//#endregion
|
|
54
|
+
export { t as FILTERS, e as TYPE_META };
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { EmptyState as e } from "../../ui/shared/empty-state.js";
|
|
2
|
+
import { FILTERS as t } from "./constants.js";
|
|
3
|
+
import { TimelineRow as n } from "./components/timeline-row.js";
|
|
4
|
+
import { _$className as r, _$event as i, _$html as a, _$insert as o, _$onEvent as s, _$reactiveOnly as c, _$scope as l } from "@praxisjs/runtime";
|
|
5
|
+
import { jsx as u } from "@praxisjs/jsx/jsx-runtime";
|
|
6
|
+
import { StatefulComponent as d } from "@praxisjs/core";
|
|
7
|
+
import { Component as f, Prop as p, State as m } from "@praxisjs/decorators";
|
|
8
|
+
//#region src/plugins/timeline/timeline-tab.tsx
|
|
9
|
+
var h = /* @__PURE__ */ i("onClick"), g = /* @__PURE__ */ a("<div class=\"flex flex-col h-full overflow-hidden\"><div class=\"flex items-center gap-[3px] px-2 py-2 border-b border-border bg-bg shrink-0 flex-wrap\"><div class=flex-1></div><button></button><button class=\"text-[11px] px-2 py-[3px] rounded cursor-pointer font-sans border border-border text-muted hover:text-text transition-colors duration-150\">Clear</button></div><div class=\"flex-1 overflow-y-auto\">"), _ = @f() class extends d {
|
|
10
|
+
@m() entries = [];
|
|
11
|
+
@m() filter = "all";
|
|
12
|
+
@m() paused = !1;
|
|
13
|
+
@p() registry;
|
|
14
|
+
_handlers = [];
|
|
15
|
+
onMount() {
|
|
16
|
+
this.entries = this.registry.getTimeline(), this._handlers = [this.registry.bus.on("timeline:push", () => {
|
|
17
|
+
this.paused || (this.entries = this.registry.getTimeline());
|
|
18
|
+
})];
|
|
19
|
+
}
|
|
20
|
+
onUnmount() {
|
|
21
|
+
this._handlers.forEach((e) => {
|
|
22
|
+
e();
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
render() {
|
|
26
|
+
{
|
|
27
|
+
let i = l(), a = g(), d = a.firstChild, f = d.nextSibling, p = d.firstChild, m = p.nextSibling, _ = m.nextSibling;
|
|
28
|
+
return o(d, t.map((e) => /* @__PURE__ */ u("button", {
|
|
29
|
+
onClick: () => {
|
|
30
|
+
this.filter = e.value;
|
|
31
|
+
},
|
|
32
|
+
class: () => this.filter === e.value ? "text-[11px] px-2 py-[3px] rounded cursor-pointer font-sans bg-soft text-accent font-semibold" : "text-[11px] px-2 py-[3px] rounded cursor-pointer font-sans text-muted hover:text-text hover:bg-section transition-colors duration-150",
|
|
33
|
+
children: e.label
|
|
34
|
+
}, e.value)), p, i), c(m, () => this.paused ? "Resume" : "Pause", i), s(m, h, () => {
|
|
35
|
+
this.paused && (this.entries = this.registry.getTimeline()), this.paused = !this.paused;
|
|
36
|
+
}, i), r(m, () => `text-[11px] px-2 py-[3px] rounded cursor-pointer font-sans border border-border transition-colors duration-150 ${this.paused ? "text-warn border-warn" : "text-muted hover:text-text"}`, i), s(_, h, () => {
|
|
37
|
+
this.entries = [];
|
|
38
|
+
}, i), c(f, () => {
|
|
39
|
+
let t = this.filter, r = t === "all" ? this.entries : this.entries.filter((e) => e.type === t || t === "component:mount" && e.type === "component:unmount");
|
|
40
|
+
return r.length === 0 ? /* @__PURE__ */ u(e, { message: "No events yet. Interact with your app to see the timeline." }) : [...r].reverse().map((e) => /* @__PURE__ */ u(n, { entry: e }, e.id));
|
|
41
|
+
}, i), a;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
//#endregion
|
|
46
|
+
export { _ as TimelineTab };
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import e from "../assets/logo.svg.js";
|
|
2
|
+
import t from "../assets/uno.generated.css.js";
|
|
3
|
+
import { Registry as n } from "../core/registry.js";
|
|
4
|
+
import { ComponentsPlugin as r } from "../plugins/components/index.js";
|
|
5
|
+
import { PerformancePlugin as i } from "../plugins/performance/index.js";
|
|
6
|
+
import { SignalsPlugin as a } from "../plugins/signals/index.js";
|
|
7
|
+
import { TimelinePlugin as o } from "../plugins/timeline/index.js";
|
|
8
|
+
import s from "../assets/tailwind-v4.css.js";
|
|
9
|
+
import { Panel as c } from "./panel.js";
|
|
10
|
+
import { _$event as l, _$html as u, _$onEvent as d, _$prop as f, _$reactiveOnly as p, _$scope as m, render as h } from "@praxisjs/runtime";
|
|
11
|
+
import { jsx as g } from "@praxisjs/jsx/jsx-runtime";
|
|
12
|
+
import { StatefulComponent as _ } from "@praxisjs/core";
|
|
13
|
+
import { Component as v, Prop as y, State as b } from "@praxisjs/decorators";
|
|
14
|
+
//#region src/ui/dev-tools.tsx
|
|
15
|
+
var x = /* @__PURE__ */ l("onClick"), S = /* @__PURE__ */ u("<div>"), C = /* @__PURE__ */ u("<button class=\"fixed bottom-5 right-5 z-[2147483647] flex items-center gap-1 pl-[10px] pr-[14px] h-[32px] rounded-xl font-sans font-semibold text-[12px] text-accent bg-header border border-border cursor-pointer select-none shadow-[0_8px_32px_rgba(0,0,0,0.7)] transition-all duration-200 hover:border-accent hover:bg-soft hover:shadow-[0_8px_32px_rgba(56,189,248,0.1)]\"><img class=\"h-[13px] w-[13px]\"><span class=\"text-[10px] font-bold text-accent tracking-widest uppercase\">devtools"), w = [
|
|
16
|
+
a,
|
|
17
|
+
r,
|
|
18
|
+
o,
|
|
19
|
+
i
|
|
20
|
+
], T = @v() class extends _ {
|
|
21
|
+
@b() open = !1;
|
|
22
|
+
@y() plugins = [];
|
|
23
|
+
@y() registry;
|
|
24
|
+
render() {
|
|
25
|
+
{
|
|
26
|
+
let t = m(), n = S();
|
|
27
|
+
return p(n, () => this.open ? /* @__PURE__ */ g(c, {
|
|
28
|
+
plugins: this.plugins,
|
|
29
|
+
registry: this.registry,
|
|
30
|
+
onClose: () => {
|
|
31
|
+
this.open = !1;
|
|
32
|
+
}
|
|
33
|
+
}) : ((t) => {
|
|
34
|
+
let n = C(), r = n.firstChild;
|
|
35
|
+
return f(r, "src", e, t), d(n, x, () => {
|
|
36
|
+
this.open = !0;
|
|
37
|
+
}, t), n;
|
|
38
|
+
})(m()), t), n;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}, E = {
|
|
42
|
+
plugins: [...w],
|
|
43
|
+
host: null,
|
|
44
|
+
cleanup: null,
|
|
45
|
+
init(e = {}) {
|
|
46
|
+
if (this.host) return;
|
|
47
|
+
e.plugins && (this.plugins = e.plugins), this.plugins.forEach((e) => e.setup?.(n.instance)), globalThis.__PRAXIS_DEVTOOLS__ = n.instance;
|
|
48
|
+
let r = document.createElement("div");
|
|
49
|
+
r.id = "praxisjs-devtools", document.body.appendChild(r), this.host = r;
|
|
50
|
+
let i = r.attachShadow({ mode: "open" }), a = document.createElement("style");
|
|
51
|
+
a.textContent = s + t, i.appendChild(a);
|
|
52
|
+
let o = document.createElement("div");
|
|
53
|
+
i.appendChild(o), this.cleanup = h(() => /* @__PURE__ */ g(T, {
|
|
54
|
+
plugins: this.plugins,
|
|
55
|
+
registry: n.instance
|
|
56
|
+
}), o);
|
|
57
|
+
},
|
|
58
|
+
registerPlugin(e) {
|
|
59
|
+
this.plugins.find((t) => t.id === e.id) || (e.setup?.(n.instance), this.plugins.push(e), this.remount());
|
|
60
|
+
},
|
|
61
|
+
get registry() {
|
|
62
|
+
return n.instance;
|
|
63
|
+
},
|
|
64
|
+
remount() {
|
|
65
|
+
if (!this.host) return;
|
|
66
|
+
let e = this.host.shadowRoot.querySelector("div:last-child");
|
|
67
|
+
this.cleanup?.(), this.cleanup = h(() => /* @__PURE__ */ g(T, {
|
|
68
|
+
plugins: this.plugins,
|
|
69
|
+
registry: n.instance
|
|
70
|
+
}), e);
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
//#endregion
|
|
74
|
+
export { E as DevTools };
|