@tanstack/pacer-devtools 1.1.4 → 1.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/dist/{PacerDevtools-C_K6wJJQ.js → components-CGEHIK6F.js} +345 -232
- package/dist/{PacerDevtools-DMClGMZE.js → components-vap4_VJT.js} +268 -167
- package/dist/index.d.ts +4 -8
- package/dist/index.js +3 -1
- package/dist/production/index.d.ts +3 -15
- package/dist/production/index.js +3 -1
- package/dist/production/server.js +3 -1
- package/dist/server.js +3 -1
- package/package.json +6 -6
- package/src/PacerContextProvider.tsx +96 -127
- package/src/components/ActionButtons.tsx +57 -33
- package/src/components/DetailsPanel.tsx +2 -1
- package/src/components/Shell.tsx +103 -34
- package/src/components/StateHeader.tsx +96 -66
- package/src/components/UtilStateJsonTree.tsx +25 -0
- package/src/{PacerDevtools.tsx → components/index.tsx} +4 -4
- package/src/core.tsx +4 -4
- package/src/index.ts +1 -1
- package/src/styles/use-styles.ts +22 -1
- package/src/utils/read-pacer-store-state.ts +33 -0
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { className, createComponent, delegateEvents, effect, insert, memo, setStyleProperty, template } from "solid-js/web";
|
|
1
|
+
import { className, createComponent, delegateEvents, effect, insert, memo, setStyleProperty, style, template, use } from "solid-js/web";
|
|
2
2
|
import { Header, HeaderLogo, JsonTree, MainPanel, ThemeContextProvider, useTheme } from "@tanstack/devtools-ui";
|
|
3
|
-
import { createStore } from "solid-js/store";
|
|
4
|
-
import { For, createContext, createEffect, createMemo, createSignal, onCleanup, onMount, useContext } from "solid-js";
|
|
5
|
-
import { pacerEventClient } from "@tanstack/pacer/event-client";
|
|
3
|
+
import { createStore, produce } from "solid-js/store";
|
|
4
|
+
import { For, Show, createContext, createEffect, createMemo, createSignal, onCleanup, onMount, useContext } from "solid-js";
|
|
5
|
+
import { getPacerDevtoolsInstance, pacerEventClient } from "@tanstack/pacer/event-client";
|
|
6
6
|
import * as goober from "goober";
|
|
7
7
|
import { useStore } from "@tanstack/solid-store";
|
|
8
8
|
import clsx from "clsx";
|
|
9
|
-
import {
|
|
9
|
+
import { emitChange } from "@tanstack/pacer";
|
|
10
10
|
import dayjs from "dayjs";
|
|
11
11
|
import relativeTime from "dayjs/plugin/relativeTime.js";
|
|
12
12
|
//#region src/PacerContextProvider.tsx
|
|
@@ -24,109 +24,78 @@ const initialPacerDevtoolsStore = {
|
|
|
24
24
|
lastUpdatedByKey: {}
|
|
25
25
|
};
|
|
26
26
|
const PacerDevtoolsContext = createContext([initialPacerDevtoolsStore, () => {}]);
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
27
|
+
/**
|
|
28
|
+
* Match TanStack Form devtools: subscribe with {@link pacerEventClient.on} per
|
|
29
|
+
* event suffix so the same `pacer:${suffix}` channel the library emits on is
|
|
30
|
+
* observed. `onAllPluginEvents` only sees `tanstack-devtools-global`, which is
|
|
31
|
+
* not always relayed the same way by the shell.
|
|
32
|
+
*
|
|
33
|
+
* `d-*` suffixes are used when the devtools panel pushes state back (see
|
|
34
|
+
* ActionButtons); those must update the panel store too.
|
|
35
|
+
*/
|
|
36
|
+
const PACER_DEVTOOLS_UTIL_EVENTS = [
|
|
37
|
+
{
|
|
38
|
+
listKey: "asyncBatchers",
|
|
39
|
+
suffixes: ["AsyncBatcher", "d-AsyncBatcher"]
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
listKey: "asyncDebouncers",
|
|
43
|
+
suffixes: ["AsyncDebouncer", "d-AsyncDebouncer"]
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
listKey: "asyncQueuers",
|
|
47
|
+
suffixes: ["AsyncQueuer", "d-AsyncQueuer"]
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
listKey: "asyncRateLimiters",
|
|
51
|
+
suffixes: ["AsyncRateLimiter", "d-AsyncRateLimiter"]
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
listKey: "asyncThrottlers",
|
|
55
|
+
suffixes: ["AsyncThrottler", "d-AsyncThrottler"]
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
listKey: "batchers",
|
|
59
|
+
suffixes: ["Batcher", "d-Batcher"]
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
listKey: "debouncers",
|
|
63
|
+
suffixes: ["Debouncer", "d-Debouncer"]
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
listKey: "queuers",
|
|
67
|
+
suffixes: ["Queuer", "d-Queuer"]
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
listKey: "rateLimiters",
|
|
71
|
+
suffixes: ["RateLimiter", "d-RateLimiter"]
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
listKey: "throttlers",
|
|
75
|
+
suffixes: ["Throttler", "d-Throttler"]
|
|
76
|
+
}
|
|
77
|
+
];
|
|
32
78
|
function PacerContextProvider(props) {
|
|
33
79
|
const [store, setStore] = createStore(initialPacerDevtoolsStore);
|
|
34
80
|
createEffect(() => {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
asyncDebouncers: updateOrAddToArray(store.asyncDebouncers, e.payload),
|
|
50
|
-
lastUpdatedByKey: {
|
|
51
|
-
...store.lastUpdatedByKey,
|
|
52
|
-
[e.payload.key]: Date.now()
|
|
53
|
-
}
|
|
54
|
-
});
|
|
55
|
-
break;
|
|
56
|
-
case "pacer:AsyncQueuer":
|
|
57
|
-
setStore({
|
|
58
|
-
asyncQueuers: updateOrAddToArray(store.asyncQueuers, e.payload),
|
|
59
|
-
lastUpdatedByKey: {
|
|
60
|
-
...store.lastUpdatedByKey,
|
|
61
|
-
[e.payload.key]: Date.now()
|
|
62
|
-
}
|
|
63
|
-
});
|
|
64
|
-
break;
|
|
65
|
-
case "pacer:AsyncRateLimiter":
|
|
66
|
-
setStore({
|
|
67
|
-
asyncRateLimiters: updateOrAddToArray(store.asyncRateLimiters, e.payload),
|
|
68
|
-
lastUpdatedByKey: {
|
|
69
|
-
...store.lastUpdatedByKey,
|
|
70
|
-
[e.payload.key]: Date.now()
|
|
71
|
-
}
|
|
72
|
-
});
|
|
73
|
-
break;
|
|
74
|
-
case "pacer:AsyncThrottler":
|
|
75
|
-
setStore({
|
|
76
|
-
asyncThrottlers: updateOrAddToArray(store.asyncThrottlers, e.payload),
|
|
77
|
-
lastUpdatedByKey: {
|
|
78
|
-
...store.lastUpdatedByKey,
|
|
79
|
-
[e.payload.key]: Date.now()
|
|
80
|
-
}
|
|
81
|
-
});
|
|
82
|
-
break;
|
|
83
|
-
case "pacer:Batcher":
|
|
84
|
-
setStore({
|
|
85
|
-
batchers: updateOrAddToArray(store.batchers, e.payload),
|
|
86
|
-
lastUpdatedByKey: {
|
|
87
|
-
...store.lastUpdatedByKey,
|
|
88
|
-
[e.payload.key]: Date.now()
|
|
89
|
-
}
|
|
90
|
-
});
|
|
91
|
-
break;
|
|
92
|
-
case "pacer:Debouncer":
|
|
93
|
-
setStore({
|
|
94
|
-
debouncers: updateOrAddToArray(store.debouncers, e.payload),
|
|
95
|
-
lastUpdatedByKey: {
|
|
96
|
-
...store.lastUpdatedByKey,
|
|
97
|
-
[e.payload.key]: Date.now()
|
|
98
|
-
}
|
|
99
|
-
});
|
|
100
|
-
break;
|
|
101
|
-
case "pacer:Queuer":
|
|
102
|
-
setStore({
|
|
103
|
-
queuers: updateOrAddToArray(store.queuers, e.payload),
|
|
104
|
-
lastUpdatedByKey: {
|
|
105
|
-
...store.lastUpdatedByKey,
|
|
106
|
-
[e.payload.key]: Date.now()
|
|
107
|
-
}
|
|
108
|
-
});
|
|
109
|
-
break;
|
|
110
|
-
case "pacer:RateLimiter":
|
|
111
|
-
setStore({
|
|
112
|
-
rateLimiters: updateOrAddToArray(store.rateLimiters, e.payload),
|
|
113
|
-
lastUpdatedByKey: {
|
|
114
|
-
...store.lastUpdatedByKey,
|
|
115
|
-
[e.payload.key]: Date.now()
|
|
116
|
-
}
|
|
117
|
-
});
|
|
118
|
-
break;
|
|
119
|
-
case "pacer:Throttler":
|
|
120
|
-
setStore({
|
|
121
|
-
throttlers: updateOrAddToArray(store.throttlers, e.payload),
|
|
122
|
-
lastUpdatedByKey: {
|
|
123
|
-
...store.lastUpdatedByKey,
|
|
124
|
-
[e.payload.key]: Date.now()
|
|
125
|
-
}
|
|
126
|
-
});
|
|
127
|
-
break;
|
|
128
|
-
}
|
|
81
|
+
const cleanups = [];
|
|
82
|
+
for (const { listKey, suffixes } of PACER_DEVTOOLS_UTIL_EVENTS) for (const suffix of suffixes) cleanups.push(pacerEventClient.on(suffix, (e) => {
|
|
83
|
+
const key = e.payload.key;
|
|
84
|
+
if (!key) return;
|
|
85
|
+
const instance = getPacerDevtoolsInstance(key);
|
|
86
|
+
if (!instance || typeof instance !== "object") return;
|
|
87
|
+
setStore(produce((draft) => {
|
|
88
|
+
const list = draft[listKey];
|
|
89
|
+
const index = list.findIndex((item) => item.key === key);
|
|
90
|
+
const inst = instance;
|
|
91
|
+
if (index !== -1) list[index] = inst;
|
|
92
|
+
else list.push(inst);
|
|
93
|
+
draft.lastUpdatedByKey[key] = Date.now();
|
|
94
|
+
}));
|
|
129
95
|
}));
|
|
96
|
+
onCleanup(() => {
|
|
97
|
+
for (const cleanup of cleanups) cleanup();
|
|
98
|
+
});
|
|
130
99
|
});
|
|
131
100
|
return createComponent(PacerDevtoolsContext.Provider, {
|
|
132
101
|
value: [store, setStore],
|
|
@@ -448,9 +417,25 @@ const stylesFactory = (theme) => {
|
|
|
448
417
|
const css = goober.css;
|
|
449
418
|
const t = (light, dark) => theme === "light" ? light : dark;
|
|
450
419
|
return {
|
|
420
|
+
shellRoot: css`
|
|
421
|
+
display: flex;
|
|
422
|
+
flex-direction: column;
|
|
423
|
+
box-sizing: border-box;
|
|
424
|
+
width: 100%;
|
|
425
|
+
height: 100%;
|
|
426
|
+
min-height: 0;
|
|
427
|
+
overflow: hidden;
|
|
428
|
+
`,
|
|
429
|
+
mainPanelShell: css`
|
|
430
|
+
display: flex;
|
|
431
|
+
flex-direction: column;
|
|
432
|
+
flex: 1 1 0%;
|
|
433
|
+
min-height: 0;
|
|
434
|
+
overflow: hidden !important;
|
|
435
|
+
`,
|
|
451
436
|
mainContainer: css`
|
|
452
437
|
display: flex;
|
|
453
|
-
flex: 1
|
|
438
|
+
flex: 1 1 0%;
|
|
454
439
|
min-height: 0;
|
|
455
440
|
overflow: hidden;
|
|
456
441
|
padding: ${size[2]};
|
|
@@ -852,7 +837,7 @@ const UTIL_GROUPS = [
|
|
|
852
837
|
];
|
|
853
838
|
//#endregion
|
|
854
839
|
//#region src/components/UtilList.tsx
|
|
855
|
-
var _tmpl$$4 = /* @__PURE__ */ template(`<div>`), _tmpl$2$
|
|
840
|
+
var _tmpl$$4 = /* @__PURE__ */ template(`<div>`), _tmpl$2$3 = /* @__PURE__ */ template(`<div><div>`), _tmpl$3$2 = /* @__PURE__ */ template(`<div><div></div><div>`);
|
|
856
841
|
function UtilList(props) {
|
|
857
842
|
const styles = useStyles();
|
|
858
843
|
return (() => {
|
|
@@ -860,7 +845,7 @@ function UtilList(props) {
|
|
|
860
845
|
insert(_el$, createComponent(For, {
|
|
861
846
|
each: UTIL_GROUPS,
|
|
862
847
|
children: (group) => memo(() => memo(() => props.utilState()[group.key].length > 0)() && (() => {
|
|
863
|
-
var _el$2 = _tmpl$2$
|
|
848
|
+
var _el$2 = _tmpl$2$3(), _el$3 = _el$2.firstChild;
|
|
864
849
|
insert(_el$3, () => group.label);
|
|
865
850
|
insert(_el$2, createComponent(For, {
|
|
866
851
|
get each() {
|
|
@@ -913,16 +898,42 @@ function UtilList(props) {
|
|
|
913
898
|
}
|
|
914
899
|
delegateEvents(["click"]);
|
|
915
900
|
//#endregion
|
|
901
|
+
//#region src/utils/read-pacer-store-state.ts
|
|
902
|
+
/** TanStack Store (`@tanstack/store`) used by pacer utils. */
|
|
903
|
+
function isPacerUtilTanStackStore(x) {
|
|
904
|
+
return typeof x === "object" && x !== null && "subscribe" in x && typeof x.subscribe === "function" && "get" in x && typeof x.get === "function";
|
|
905
|
+
}
|
|
906
|
+
/**
|
|
907
|
+
* Core pacer utils expose TanStack `Store` (`get()` / `state`). React hooks may
|
|
908
|
+
* also expose a reactive `state` field. Use this for devtools UI that needs a
|
|
909
|
+
* plain snapshot (e.g. JsonTree).
|
|
910
|
+
*/
|
|
911
|
+
function getPacerUtilStoreState(instance) {
|
|
912
|
+
const store = instance.store;
|
|
913
|
+
if (store && typeof store.get === "function") return store.get();
|
|
914
|
+
if (store && store.state !== void 0) return store.state;
|
|
915
|
+
return instance.state;
|
|
916
|
+
}
|
|
917
|
+
//#endregion
|
|
916
918
|
//#region src/components/ActionButtons.tsx
|
|
917
|
-
var _tmpl$$3 = /* @__PURE__ */ template(`<div>No actions available for this util`), _tmpl$2$
|
|
919
|
+
var _tmpl$$3 = /* @__PURE__ */ template(`<div>No actions available for this util`), _tmpl$2$2 = /* @__PURE__ */ template(`<div>`), _tmpl$3$1 = /* @__PURE__ */ template(`<button><span>`), _tmpl$4 = /* @__PURE__ */ template(`<button><span></span>Flush`), _tmpl$5 = /* @__PURE__ */ template(`<button><span></span>Cancel`), _tmpl$6 = /* @__PURE__ */ template(`<button><span></span>Reset`), _tmpl$7 = /* @__PURE__ */ template(`<button><span></span>Clear`);
|
|
918
920
|
function ActionButtons(props) {
|
|
919
921
|
const styles = useStyles();
|
|
920
922
|
const utilInstance = props.instance;
|
|
921
|
-
const
|
|
922
|
-
const
|
|
923
|
-
const
|
|
924
|
-
const
|
|
925
|
-
|
|
923
|
+
const store = utilInstance?.store;
|
|
924
|
+
const stateAccessor = isPacerUtilTanStackStore(store) ? useStore(store, (s) => s !== null && s !== void 0 && typeof s === "object" ? s : {}) : () => getPacerUtilStoreState(utilInstance);
|
|
925
|
+
const getState = () => stateAccessor();
|
|
926
|
+
const hasPending = () => {
|
|
927
|
+
return "isPending" in getState();
|
|
928
|
+
};
|
|
929
|
+
const isPending = () => {
|
|
930
|
+
const u = getState();
|
|
931
|
+
return "isPending" in u ? !!u.isPending : false;
|
|
932
|
+
};
|
|
933
|
+
const isEmptyFlag = () => {
|
|
934
|
+
const u = getState();
|
|
935
|
+
return "isEmpty" in u ? !!u.isEmpty : false;
|
|
936
|
+
};
|
|
926
937
|
const hasFlush = typeof utilInstance.flush === "function";
|
|
927
938
|
const hasCancel = typeof utilInstance.cancel === "function";
|
|
928
939
|
const hasReset = typeof utilInstance.reset === "function";
|
|
@@ -930,45 +941,52 @@ function ActionButtons(props) {
|
|
|
930
941
|
const hasStart = typeof utilInstance.start === "function";
|
|
931
942
|
const hasStop = typeof utilInstance.stop === "function";
|
|
932
943
|
const hasStartStop = hasStart && hasStop;
|
|
933
|
-
const isRunning =
|
|
934
|
-
|
|
944
|
+
const isRunning = () => {
|
|
945
|
+
const u = getState();
|
|
946
|
+
return "isRunning" in u ? !!u.isRunning : true;
|
|
947
|
+
};
|
|
948
|
+
if (!hasPending() && !hasFlush && !hasCancel && !hasReset && !hasClear) return (() => {
|
|
935
949
|
var _el$ = _tmpl$$3();
|
|
936
950
|
effect(() => className(_el$, styles().sectionEmpty));
|
|
937
951
|
return _el$;
|
|
938
952
|
})();
|
|
939
953
|
const emitName = `d-${props.utilName}`;
|
|
954
|
+
const utilNameLower = props.utilName.toLowerCase();
|
|
940
955
|
return (() => {
|
|
941
|
-
var _el$2 = _tmpl$2$
|
|
942
|
-
insert(_el$2,
|
|
943
|
-
var
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
956
|
+
var _el$2 = _tmpl$2$2();
|
|
957
|
+
insert(_el$2, (() => {
|
|
958
|
+
var _c$ = memo(() => !!hasPending());
|
|
959
|
+
return () => _c$() && (() => {
|
|
960
|
+
var _el$3 = _tmpl$3$1(), _el$4 = _el$3.firstChild;
|
|
961
|
+
_el$3.$$mousedown = () => {
|
|
962
|
+
const next = !isPending();
|
|
963
|
+
utilInstance.store.setState((prev) => ({
|
|
964
|
+
...prev,
|
|
965
|
+
isPending: next
|
|
966
|
+
}));
|
|
967
|
+
emitChange(emitName, utilInstance);
|
|
968
|
+
};
|
|
969
|
+
insert(_el$3, () => isPending() ? "Restore Pending" : "Trigger Pending", null);
|
|
970
|
+
effect((_p$) => {
|
|
971
|
+
var _v$ = styles().actionButton, _v$2 = styles().actionDotBlue;
|
|
972
|
+
_v$ !== _p$.e && className(_el$3, _p$.e = _v$);
|
|
973
|
+
_v$2 !== _p$.t && className(_el$4, _p$.t = _v$2);
|
|
974
|
+
return _p$;
|
|
975
|
+
}, {
|
|
976
|
+
e: void 0,
|
|
977
|
+
t: void 0
|
|
978
|
+
});
|
|
979
|
+
return _el$3;
|
|
980
|
+
})();
|
|
963
981
|
})(), null);
|
|
964
982
|
insert(_el$2, hasFlush && (() => {
|
|
965
983
|
var _el$5 = _tmpl$4(), _el$6 = _el$5.firstChild;
|
|
966
984
|
_el$5.$$mousedown = () => {
|
|
967
985
|
utilInstance.flush();
|
|
968
|
-
|
|
986
|
+
emitChange(emitName, utilInstance);
|
|
969
987
|
};
|
|
970
988
|
effect((_p$) => {
|
|
971
|
-
var _v$3 = styles().actionButton, _v$4 =
|
|
989
|
+
var _v$3 = styles().actionButton, _v$4 = utilNameLower.includes("debouncer") && !isPending() || utilNameLower.includes("throttler") && !isPending() || utilNameLower.includes("batcher") && isEmptyFlag() || utilNameLower.includes("queuer") && isEmptyFlag(), _v$5 = styles().actionDotGreen;
|
|
972
990
|
_v$3 !== _p$.e && className(_el$5, _p$.e = _v$3);
|
|
973
991
|
_v$4 !== _p$.t && (_el$5.disabled = _p$.t = _v$4);
|
|
974
992
|
_v$5 !== _p$.a && className(_el$6, _p$.a = _v$5);
|
|
@@ -984,10 +1002,10 @@ function ActionButtons(props) {
|
|
|
984
1002
|
var _el$7 = _tmpl$5(), _el$8 = _el$7.firstChild;
|
|
985
1003
|
_el$7.$$mousedown = () => {
|
|
986
1004
|
utilInstance.cancel();
|
|
987
|
-
|
|
1005
|
+
emitChange(emitName, utilInstance);
|
|
988
1006
|
};
|
|
989
1007
|
effect((_p$) => {
|
|
990
|
-
var _v$6 = styles().actionButton, _v$7 =
|
|
1008
|
+
var _v$6 = styles().actionButton, _v$7 = utilNameLower.includes("debouncer") && !isPending() || utilNameLower.includes("throttler") && !isPending() || utilNameLower.includes("batcher") && isEmptyFlag() || utilNameLower.includes("queuer") && isEmptyFlag(), _v$8 = styles().actionDotRed;
|
|
991
1009
|
_v$6 !== _p$.e && className(_el$7, _p$.e = _v$6);
|
|
992
1010
|
_v$7 !== _p$.t && (_el$7.disabled = _p$.t = _v$7);
|
|
993
1011
|
_v$8 !== _p$.a && className(_el$8, _p$.a = _v$8);
|
|
@@ -1003,7 +1021,7 @@ function ActionButtons(props) {
|
|
|
1003
1021
|
var _el$9 = _tmpl$6(), _el$0 = _el$9.firstChild;
|
|
1004
1022
|
_el$9.$$mousedown = () => {
|
|
1005
1023
|
utilInstance.reset();
|
|
1006
|
-
|
|
1024
|
+
emitChange(emitName, utilInstance);
|
|
1007
1025
|
};
|
|
1008
1026
|
effect((_p$) => {
|
|
1009
1027
|
var _v$9 = styles().actionButton, _v$0 = styles().actionDotYellow;
|
|
@@ -1020,10 +1038,10 @@ function ActionButtons(props) {
|
|
|
1020
1038
|
var _el$1 = _tmpl$7(), _el$10 = _el$1.firstChild;
|
|
1021
1039
|
_el$1.$$mousedown = () => {
|
|
1022
1040
|
utilInstance.clear();
|
|
1023
|
-
|
|
1041
|
+
emitChange(emitName, utilInstance);
|
|
1024
1042
|
};
|
|
1025
1043
|
effect((_p$) => {
|
|
1026
|
-
var _v$1 = styles().actionButton, _v$10 =
|
|
1044
|
+
var _v$1 = styles().actionButton, _v$10 = utilNameLower.includes("batcher") && isEmptyFlag() || utilNameLower.includes("queuer") && isEmptyFlag(), _v$11 = styles().actionDotOrange;
|
|
1027
1045
|
_v$1 !== _p$.e && className(_el$1, _p$.e = _v$1);
|
|
1028
1046
|
_v$10 !== _p$.t && (_el$1.disabled = _p$.t = _v$10);
|
|
1029
1047
|
_v$11 !== _p$.a && className(_el$10, _p$.a = _v$11);
|
|
@@ -1038,11 +1056,11 @@ function ActionButtons(props) {
|
|
|
1038
1056
|
insert(_el$2, hasStartStop && (() => {
|
|
1039
1057
|
var _el$11 = _tmpl$3$1(), _el$12 = _el$11.firstChild;
|
|
1040
1058
|
_el$11.$$mousedown = () => {
|
|
1041
|
-
if (isRunning) utilInstance.stop();
|
|
1059
|
+
if (isRunning()) utilInstance.stop();
|
|
1042
1060
|
else utilInstance.start();
|
|
1043
|
-
|
|
1061
|
+
emitChange(emitName, utilInstance);
|
|
1044
1062
|
};
|
|
1045
|
-
insert(_el$11, isRunning ? "Stop" : "Start", null);
|
|
1063
|
+
insert(_el$11, () => isRunning() ? "Stop" : "Start", null);
|
|
1046
1064
|
effect((_p$) => {
|
|
1047
1065
|
var _v$12 = styles().actionButton, _v$13 = styles().actionDotPurple;
|
|
1048
1066
|
_v$12 !== _p$.e && className(_el$11, _p$.e = _v$12);
|
|
@@ -1063,56 +1081,46 @@ delegateEvents(["mousedown"]);
|
|
|
1063
1081
|
//#region src/components/StateHeader.tsx
|
|
1064
1082
|
var _tmpl$$2 = /* @__PURE__ */ template(`<div><div></div><div style=display:flex;align-items:center;gap:16px><div><div>Key</div><div></div><div>Last Updated</div><div> (<!>)</div></div><div style=margin-left:auto;font-weight:bold>% reduction`);
|
|
1065
1083
|
dayjs.extend(relativeTime);
|
|
1066
|
-
function
|
|
1067
|
-
|
|
1068
|
-
const
|
|
1069
|
-
|
|
1070
|
-
const
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
if (
|
|
1079
|
-
const
|
|
1080
|
-
|
|
1084
|
+
function reductionFromState(entry, state) {
|
|
1085
|
+
if (!state) return 0;
|
|
1086
|
+
const completedExecutions = entry.type.toLowerCase().includes("async") ? Number(state.settleCount) || 0 : Number(state.executionCount) || 0;
|
|
1087
|
+
if (entry.type.toLowerCase().includes("batcher")) {
|
|
1088
|
+
const totalItemsProcessed = Number(state.totalItemsProcessed) || 0;
|
|
1089
|
+
if (totalItemsProcessed === 0) return 0;
|
|
1090
|
+
return Math.round((totalItemsProcessed - completedExecutions) / totalItemsProcessed * 100);
|
|
1091
|
+
}
|
|
1092
|
+
let requestCount = 0;
|
|
1093
|
+
if (state.maybeExecuteCount !== void 0) requestCount = Number(state.maybeExecuteCount) || 0;
|
|
1094
|
+
else if (state.addItemCount !== void 0) requestCount = Number(state.addItemCount) || 0;
|
|
1095
|
+
else return 0;
|
|
1096
|
+
if (requestCount === 0) return 0;
|
|
1097
|
+
const reduction = requestCount - completedExecutions;
|
|
1098
|
+
return Math.round(reduction / requestCount * 100);
|
|
1099
|
+
}
|
|
1100
|
+
function StateHeaderInner(props) {
|
|
1101
|
+
const key = props.entry.instance.key;
|
|
1102
|
+
const updatedAt = () => props.utilState().lastUpdatedByKey[key] ?? Date.now();
|
|
1081
1103
|
const getRelativeTime = () => {
|
|
1082
|
-
const
|
|
1104
|
+
const at = updatedAt();
|
|
1105
|
+
const diffMs = props.now() - at;
|
|
1083
1106
|
const diffSeconds = Math.floor(diffMs / 1e3);
|
|
1084
1107
|
if (diffSeconds < 60) return `${diffSeconds} second${diffSeconds !== 1 ? "s" : ""} ago`;
|
|
1085
|
-
return dayjs(
|
|
1108
|
+
return dayjs(at).fromNow();
|
|
1086
1109
|
};
|
|
1087
|
-
const
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
const completedExecutions = entry.type.toLowerCase().includes("async") ? state.settleCount || 0 : state.executionCount || 0;
|
|
1091
|
-
if (entry.type.toLowerCase().includes("batcher")) {
|
|
1092
|
-
const totalItemsProcessed = state.totalItemsProcessed || 0;
|
|
1093
|
-
if (totalItemsProcessed === 0) return 0;
|
|
1094
|
-
return Math.round((totalItemsProcessed - completedExecutions) / totalItemsProcessed * 100);
|
|
1095
|
-
}
|
|
1096
|
-
let requestCount = 0;
|
|
1097
|
-
if (state.maybeExecuteCount !== void 0) requestCount = state.maybeExecuteCount;
|
|
1098
|
-
else if (state.addItemCount !== void 0) requestCount = state.addItemCount;
|
|
1099
|
-
else return 0;
|
|
1100
|
-
if (requestCount === 0) return 0;
|
|
1101
|
-
const reduction = requestCount - completedExecutions;
|
|
1102
|
-
return Math.round(reduction / requestCount * 100);
|
|
1103
|
-
};
|
|
1104
|
-
const reductionPercentage = getReductionPercentage();
|
|
1110
|
+
const store = props.entry.instance?.store;
|
|
1111
|
+
const stateAccessor = isPacerUtilTanStackStore(store) ? useStore(store, (s) => s !== null && s !== void 0 && typeof s === "object" ? s : {}) : () => getPacerUtilStoreState(props.entry.instance);
|
|
1112
|
+
const reductionPercentage = () => reductionFromState(props.entry, stateAccessor());
|
|
1105
1113
|
return (() => {
|
|
1106
1114
|
var _el$ = _tmpl$$2(), _el$2 = _el$.firstChild, _el$4 = _el$2.nextSibling.firstChild, _el$5 = _el$4.firstChild, _el$6 = _el$5.nextSibling, _el$7 = _el$6.nextSibling, _el$8 = _el$7.nextSibling, _el$9 = _el$8.firstChild, _el$1 = _el$9.nextSibling;
|
|
1107
1115
|
_el$1.nextSibling;
|
|
1108
1116
|
var _el$10 = _el$4.nextSibling, _el$11 = _el$10.firstChild;
|
|
1109
|
-
insert(_el$2, () => entry.type);
|
|
1117
|
+
insert(_el$2, () => props.entry.type);
|
|
1110
1118
|
insert(_el$6, key);
|
|
1111
|
-
insert(_el$8, () => new Date(updatedAt).toLocaleTimeString(), _el$9);
|
|
1119
|
+
insert(_el$8, () => new Date(updatedAt()).toLocaleTimeString(), _el$9);
|
|
1112
1120
|
insert(_el$8, getRelativeTime, _el$1);
|
|
1113
1121
|
insert(_el$10, reductionPercentage, _el$11);
|
|
1114
1122
|
effect((_p$) => {
|
|
1115
|
-
var _v$ = styles
|
|
1123
|
+
var _v$ = props.styles.stateHeader, _v$2 = props.styles.stateTitle, _v$3 = props.styles.infoGrid, _v$4 = props.styles.infoLabel, _v$5 = props.styles.infoValueMono, _v$6 = props.styles.infoLabel, _v$7 = props.styles.infoValueMono, _v$8 = props.styles.infoValueMono;
|
|
1116
1124
|
_v$ !== _p$.e && className(_el$, _p$.e = _v$);
|
|
1117
1125
|
_v$2 !== _p$.t && className(_el$2, _p$.t = _v$2);
|
|
1118
1126
|
_v$3 !== _p$.a && className(_el$4, _p$.a = _v$3);
|
|
@@ -1135,9 +1143,57 @@ function StateHeader(props) {
|
|
|
1135
1143
|
return _el$;
|
|
1136
1144
|
})();
|
|
1137
1145
|
}
|
|
1146
|
+
function StateHeader(props) {
|
|
1147
|
+
const styles = useStyles();
|
|
1148
|
+
const [now, setNow] = createSignal(Date.now());
|
|
1149
|
+
onMount(() => {
|
|
1150
|
+
const interval = setInterval(() => {
|
|
1151
|
+
setNow(Date.now());
|
|
1152
|
+
}, 1e3);
|
|
1153
|
+
onCleanup(() => {
|
|
1154
|
+
clearInterval(interval);
|
|
1155
|
+
});
|
|
1156
|
+
});
|
|
1157
|
+
return createComponent(Show, {
|
|
1158
|
+
get when() {
|
|
1159
|
+
return props.selectedInstance();
|
|
1160
|
+
},
|
|
1161
|
+
children: (entry) => createComponent(StateHeaderInner, {
|
|
1162
|
+
get entry() {
|
|
1163
|
+
return entry();
|
|
1164
|
+
},
|
|
1165
|
+
get styles() {
|
|
1166
|
+
return styles();
|
|
1167
|
+
},
|
|
1168
|
+
now,
|
|
1169
|
+
get utilState() {
|
|
1170
|
+
return props.utilState;
|
|
1171
|
+
}
|
|
1172
|
+
})
|
|
1173
|
+
});
|
|
1174
|
+
}
|
|
1175
|
+
//#endregion
|
|
1176
|
+
//#region src/components/UtilStateJsonTree.tsx
|
|
1177
|
+
/**
|
|
1178
|
+
* Subscribes to the util's TanStack Store so the tree updates live (same
|
|
1179
|
+
* idea as {@link UtilList} status). A plain snapshot would only refresh when the
|
|
1180
|
+
* devtools shell re-renders, which does not happen on nested store updates.
|
|
1181
|
+
*/
|
|
1182
|
+
function UtilStateJsonTree(props) {
|
|
1183
|
+
const store = props.instance?.store;
|
|
1184
|
+
if (isPacerUtilTanStackStore(store)) {
|
|
1185
|
+
const snapshot = useStore(store, (s) => s);
|
|
1186
|
+
return createComponent(JsonTree, { get value() {
|
|
1187
|
+
return snapshot();
|
|
1188
|
+
} });
|
|
1189
|
+
}
|
|
1190
|
+
return createComponent(JsonTree, { get value() {
|
|
1191
|
+
return getPacerUtilStoreState(props.instance);
|
|
1192
|
+
} });
|
|
1193
|
+
}
|
|
1138
1194
|
//#endregion
|
|
1139
1195
|
//#region src/components/DetailsPanel.tsx
|
|
1140
|
-
var _tmpl$$1 = /* @__PURE__ */ template(`<div>`), _tmpl$2 = /* @__PURE__ */ template(`<div>Select a util from the left panel to view its state`), _tmpl$3 = /* @__PURE__ */ template(`<div><div><div>Actions</div></div><div><div>State</div><div></div></div><div><div>Options</div><div>`);
|
|
1196
|
+
var _tmpl$$1 = /* @__PURE__ */ template(`<div>`), _tmpl$2$1 = /* @__PURE__ */ template(`<div>Select a util from the left panel to view its state`), _tmpl$3 = /* @__PURE__ */ template(`<div><div><div>Actions</div></div><div><div>State</div><div></div></div><div><div>Options</div><div>`);
|
|
1141
1197
|
function DetailsPanel(props) {
|
|
1142
1198
|
const styles = useStyles();
|
|
1143
1199
|
return (() => {
|
|
@@ -1145,7 +1201,7 @@ function DetailsPanel(props) {
|
|
|
1145
1201
|
insert(_el$, () => {
|
|
1146
1202
|
const entry = props.selectedInstance();
|
|
1147
1203
|
if (!entry) return (() => {
|
|
1148
|
-
var _el$2 = _tmpl$2();
|
|
1204
|
+
var _el$2 = _tmpl$2$1();
|
|
1149
1205
|
effect(() => className(_el$2, styles().noSelection));
|
|
1150
1206
|
return _el$2;
|
|
1151
1207
|
})();
|
|
@@ -1166,8 +1222,8 @@ function DetailsPanel(props) {
|
|
|
1166
1222
|
return entry.type;
|
|
1167
1223
|
}
|
|
1168
1224
|
}), null);
|
|
1169
|
-
insert(_el$8, createComponent(
|
|
1170
|
-
return entry.instance
|
|
1225
|
+
insert(_el$8, createComponent(UtilStateJsonTree, { get instance() {
|
|
1226
|
+
return entry.instance;
|
|
1171
1227
|
} }));
|
|
1172
1228
|
insert(_el$1, createComponent(JsonTree, { get value() {
|
|
1173
1229
|
return entry.instance.options;
|
|
@@ -1204,7 +1260,18 @@ function DetailsPanel(props) {
|
|
|
1204
1260
|
}
|
|
1205
1261
|
//#endregion
|
|
1206
1262
|
//#region src/components/Shell.tsx
|
|
1207
|
-
var _tmpl$ = /* @__PURE__ */ template(`<div><div style=min-width:150px;max-width:800px></div><div></div><div style=flex:1><div>Details`);
|
|
1263
|
+
var _tmpl$ = /* @__PURE__ */ template(`<div><div style=min-width:150px;max-width:800px></div><div></div><div style=flex:1><div>Details`), _tmpl$2 = /* @__PURE__ */ template(`<div>`);
|
|
1264
|
+
function readResizeObserverBlockSize(entry) {
|
|
1265
|
+
const [box] = entry.borderBoxSize;
|
|
1266
|
+
if (box !== void 0) return box.blockSize;
|
|
1267
|
+
return entry.contentRect.height;
|
|
1268
|
+
}
|
|
1269
|
+
/** Outer plugin slot from TanStack Devtools (not the inner React mount div, which can report a tiny height). */
|
|
1270
|
+
function resolvePluginHost(el) {
|
|
1271
|
+
const byId = el.closest("[id^=\"plugin-container-\"]");
|
|
1272
|
+
if (byId) return byId;
|
|
1273
|
+
return el.parentElement?.parentElement?.parentElement ?? el.parentElement?.parentElement ?? el.parentElement;
|
|
1274
|
+
}
|
|
1208
1275
|
function Shell() {
|
|
1209
1276
|
const styles = useStyles();
|
|
1210
1277
|
const state = usePacerDevtoolsState();
|
|
@@ -1212,6 +1279,8 @@ function Shell() {
|
|
|
1212
1279
|
const [selectedKey, setSelectedKey] = createSignal(null);
|
|
1213
1280
|
const [leftPanelWidth, setLeftPanelWidth] = createSignal(300);
|
|
1214
1281
|
const [isDragging, setIsDragging] = createSignal(false);
|
|
1282
|
+
const [shellRootEl, setShellRootEl] = createSignal(null);
|
|
1283
|
+
const [slotHeightPx, setSlotHeightPx] = createSignal(void 0);
|
|
1215
1284
|
const selectedInstance = createMemo(() => {
|
|
1216
1285
|
const key = selectedKey();
|
|
1217
1286
|
if (!key) return null;
|
|
@@ -1254,51 +1323,95 @@ function Shell() {
|
|
|
1254
1323
|
document.removeEventListener("mousemove", handleMouseMove);
|
|
1255
1324
|
document.removeEventListener("mouseup", handleMouseUp);
|
|
1256
1325
|
});
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1326
|
+
/**
|
|
1327
|
+
* TanStack Devtools plugin slots use height: 100% without min-height: 0 on flex
|
|
1328
|
+
* ancestors, so percentage height often never constrains our tree. Observing the
|
|
1329
|
+
* outer `plugin-container-*` host (not the immediate React mount wrapper) and
|
|
1330
|
+
* setting an explicit pixel height makes inner overflow-y regions scroll without
|
|
1331
|
+
* capping to the mount div’s collapsed height.
|
|
1332
|
+
*/
|
|
1333
|
+
createEffect(() => {
|
|
1334
|
+
const el = shellRootEl();
|
|
1335
|
+
if (!el || typeof ResizeObserver === "undefined") return;
|
|
1336
|
+
const pluginSlot = resolvePluginHost(el);
|
|
1337
|
+
if (!pluginSlot) return;
|
|
1338
|
+
const ro = new ResizeObserver((entries) => {
|
|
1339
|
+
const entry = entries[0];
|
|
1340
|
+
if (!entry) return;
|
|
1341
|
+
const target = entry.target;
|
|
1342
|
+
const h = target.clientHeight > 0 ? target.clientHeight : readResizeObserverBlockSize(entry);
|
|
1343
|
+
if (h > 0) setSlotHeightPx(Math.round(h));
|
|
1344
|
+
});
|
|
1345
|
+
ro.observe(pluginSlot);
|
|
1346
|
+
onCleanup(() => ro.disconnect());
|
|
1347
|
+
});
|
|
1348
|
+
return (() => {
|
|
1349
|
+
var _el$ = _tmpl$2();
|
|
1350
|
+
use(setShellRootEl, _el$);
|
|
1351
|
+
insert(_el$, createComponent(MainPanel, {
|
|
1352
|
+
get ["class"]() {
|
|
1353
|
+
return styles().mainPanelShell;
|
|
1354
|
+
},
|
|
1355
|
+
get children() {
|
|
1356
|
+
return [createComponent(Header, { get children() {
|
|
1357
|
+
return createComponent(HeaderLogo, {
|
|
1358
|
+
flavor: {
|
|
1359
|
+
light: "#84cc16",
|
|
1360
|
+
dark: "#84cc16"
|
|
1361
|
+
},
|
|
1362
|
+
children: "TanStack Pacer"
|
|
1363
|
+
});
|
|
1364
|
+
} }), (() => {
|
|
1365
|
+
var _el$2 = _tmpl$(), _el$3 = _el$2.firstChild, _el$4 = _el$3.nextSibling, _el$5 = _el$4.nextSibling, _el$6 = _el$5.firstChild;
|
|
1366
|
+
insert(_el$3, createComponent(UtilList, {
|
|
1367
|
+
selectedKey,
|
|
1368
|
+
setSelectedKey,
|
|
1369
|
+
utilState
|
|
1370
|
+
}));
|
|
1371
|
+
_el$4.$$mousedown = handleMouseDown;
|
|
1372
|
+
insert(_el$5, createComponent(DetailsPanel, {
|
|
1373
|
+
selectedInstance,
|
|
1374
|
+
utilState
|
|
1375
|
+
}), null);
|
|
1376
|
+
effect((_p$) => {
|
|
1377
|
+
var _v$ = styles().mainContainer, _v$2 = styles().leftPanel, _v$3 = `${leftPanelWidth()}px`, _v$4 = `${styles().dragHandle} ${isDragging() ? "dragging" : ""}`, _v$5 = styles().rightPanel, _v$6 = styles().panelHeader;
|
|
1378
|
+
_v$ !== _p$.e && className(_el$2, _p$.e = _v$);
|
|
1379
|
+
_v$2 !== _p$.t && className(_el$3, _p$.t = _v$2);
|
|
1380
|
+
_v$3 !== _p$.a && setStyleProperty(_el$3, "width", _p$.a = _v$3);
|
|
1381
|
+
_v$4 !== _p$.o && className(_el$4, _p$.o = _v$4);
|
|
1382
|
+
_v$5 !== _p$.i && className(_el$5, _p$.i = _v$5);
|
|
1383
|
+
_v$6 !== _p$.n && className(_el$6, _p$.n = _v$6);
|
|
1384
|
+
return _p$;
|
|
1385
|
+
}, {
|
|
1386
|
+
e: void 0,
|
|
1387
|
+
t: void 0,
|
|
1388
|
+
a: void 0,
|
|
1389
|
+
o: void 0,
|
|
1390
|
+
i: void 0,
|
|
1391
|
+
n: void 0
|
|
1392
|
+
});
|
|
1393
|
+
return _el$2;
|
|
1394
|
+
})()];
|
|
1395
|
+
}
|
|
1396
|
+
}));
|
|
1397
|
+
effect((_p$) => {
|
|
1398
|
+
var _v$7 = styles().shellRoot, _v$8 = { ...slotHeightPx() !== void 0 ? {
|
|
1399
|
+
height: `${slotHeightPx()}px`,
|
|
1400
|
+
"max-height": `${slotHeightPx()}px`
|
|
1401
|
+
} : {} };
|
|
1402
|
+
_v$7 !== _p$.e && className(_el$, _p$.e = _v$7);
|
|
1403
|
+
_p$.t = style(_el$, _v$8, _p$.t);
|
|
1404
|
+
return _p$;
|
|
1405
|
+
}, {
|
|
1406
|
+
e: void 0,
|
|
1407
|
+
t: void 0
|
|
1408
|
+
});
|
|
1409
|
+
return _el$;
|
|
1410
|
+
})();
|
|
1298
1411
|
}
|
|
1299
1412
|
delegateEvents(["mousedown"]);
|
|
1300
1413
|
//#endregion
|
|
1301
|
-
//#region src/
|
|
1414
|
+
//#region src/components/index.tsx
|
|
1302
1415
|
function PacerDevtools(props) {
|
|
1303
1416
|
return createComponent(ThemeContextProvider, {
|
|
1304
1417
|
get theme() {
|