@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.
Files changed (65) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/assets/uno.generated.css +5 -0
  3. package/dist/assets/logo.svg.js +4 -0
  4. package/dist/assets/tailwind-v4.css.js +4 -0
  5. package/dist/assets/uno.generated.css.js +4 -0
  6. package/dist/core/event-bus.js +21 -0
  7. package/dist/core/profiler.js +79 -0
  8. package/dist/core/registry.js +147 -0
  9. package/dist/decorators/debug.js +126 -0
  10. package/dist/decorators/trace.js +45 -0
  11. package/dist/icons/ellipsis-vertical.js +38 -0
  12. package/dist/icons/panel-bottom.js +28 -0
  13. package/dist/icons/panel-left.js +28 -0
  14. package/dist/icons/panel-right.js +28 -0
  15. package/dist/icons/panel-top.js +28 -0
  16. package/dist/icons/x.js +22 -0
  17. package/dist/index.d.ts +96 -3
  18. package/dist/index.js +10 -1157
  19. package/dist/plugins/components/components/component-detail.js +53 -0
  20. package/dist/plugins/components/components/component-row.js +22 -0
  21. package/dist/plugins/components/components/detail-row.js +15 -0
  22. package/dist/plugins/components/components/detail-section.js +14 -0
  23. package/dist/plugins/components/components/status-dot.js +15 -0
  24. package/dist/plugins/components/components-tab.js +54 -0
  25. package/dist/plugins/components/index.js +9 -0
  26. package/dist/plugins/performance/index.js +9 -0
  27. package/dist/plugins/performance/performance-tab.js +62 -0
  28. package/dist/plugins/signals/components/signal-detail.js +29 -0
  29. package/dist/plugins/signals/components/signal-row.js +17 -0
  30. package/dist/plugins/signals/index.js +9 -0
  31. package/dist/plugins/signals/signals-tab.js +53 -0
  32. package/dist/plugins/timeline/components/badge.js +16 -0
  33. package/dist/plugins/timeline/components/timeline-row.js +33 -0
  34. package/dist/plugins/timeline/constants.js +54 -0
  35. package/dist/plugins/timeline/index.js +9 -0
  36. package/dist/plugins/timeline/timeline-tab.js +46 -0
  37. package/dist/ui/dev-tools.js +74 -0
  38. package/dist/ui/panel.js +106 -0
  39. package/dist/ui/shared/empty-state.js +14 -0
  40. package/dist/ui/shared/icon-button.js +17 -0
  41. package/dist/ui/shared/panel-section.js +14 -0
  42. package/dist/ui/shared/search-input.js +17 -0
  43. package/dist/ui/shared/side-panel.js +15 -0
  44. package/dist/utils/format-time.js +7 -0
  45. package/dist/utils/format-value.js +14 -0
  46. package/package.json +14 -6
  47. package/src/core/profiler.ts +162 -0
  48. package/src/core/registry.ts +23 -21
  49. package/src/decorators/debug.ts +96 -32
  50. package/src/decorators/index.ts +1 -0
  51. package/src/decorators/trace.ts +27 -4
  52. package/src/index.ts +5 -0
  53. package/src/plugins/performance/index.ts +9 -0
  54. package/src/plugins/performance/performance-tab.tsx +111 -0
  55. package/src/ui/dev-tools.tsx +5 -0
  56. package/src/__tests__/debug.test.ts +0 -333
  57. package/src/__tests__/event-bus.test.ts +0 -123
  58. package/src/__tests__/format-time.test.ts +0 -52
  59. package/src/__tests__/format-value.test.ts +0 -46
  60. package/src/__tests__/plugins.test.ts +0 -101
  61. package/src/__tests__/registry.test.ts +0 -386
  62. package/src/__tests__/trace.test.ts +0 -200
  63. package/tsconfig.json +0 -21
  64. package/uno.config.ts +0 -57
  65. 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,9 @@
1
+ import { ComponentsTab as e } from "./components-tab.js";
2
+ //#region src/plugins/components/index.ts
3
+ var t = {
4
+ id: "components",
5
+ label: "Components",
6
+ component: e
7
+ };
8
+ //#endregion
9
+ export { t as ComponentsPlugin };
@@ -0,0 +1,9 @@
1
+ import { PerformanceTab as e } from "./performance-tab.js";
2
+ //#region src/plugins/performance/index.ts
3
+ var t = {
4
+ id: "performance",
5
+ label: "Performance",
6
+ component: e
7
+ };
8
+ //#endregion
9
+ export { t as PerformancePlugin };
@@ -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,9 @@
1
+ import { SignalsTab as e } from "./signals-tab.js";
2
+ //#region src/plugins/signals/index.ts
3
+ var t = {
4
+ id: "signals",
5
+ label: "Signals",
6
+ component: e
7
+ };
8
+ //#endregion
9
+ export { t as SignalsPlugin };
@@ -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,9 @@
1
+ import { TimelineTab as e } from "./timeline-tab.js";
2
+ //#region src/plugins/timeline/index.ts
3
+ var t = {
4
+ id: "timeline",
5
+ label: "Timeline",
6
+ component: e
7
+ };
8
+ //#endregion
9
+ export { t as TimelinePlugin };
@@ -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 };