@pulse-js/tools 0.1.1
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/README.md +55 -0
- package/dist/index.cjs +349 -0
- package/dist/index.d.cts +5 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +324 -0
- package/package.json +23 -0
package/README.md
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# @pulse-js/tools
|
|
2
|
+
|
|
3
|
+
The official visual debugging suite for the Pulse ecosystem. Inspect, monitor, and debug your reactive graph in real-time with a premium, developer-focused UI.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Draggable UI**: A floating widget that lives anywhere on your screen.
|
|
8
|
+
- **Quadrant-Aware Anchoring**: Intelligent positioning system. The panel automatically expands from the corner closest to its current position (top-left, bottom-right, etc.), ensuring the UI never jumps or floats awkwardly.
|
|
9
|
+
- **Persistent State**: innovative positioning engine remembers exactly where you left the widget, persisting across page reloads and HMR updates.
|
|
10
|
+
- **Real-Time Inspection**: Visualize the status (OK, FAIL, PENDING) and values of all registered Sources and Guards instantly.
|
|
11
|
+
- **Glassmorphism Design**: A modern, dark-themed aesthetic that fits seamlessly into developer workflows without obstructing functionality.
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install @pulse-js/tools
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
### React Integration
|
|
22
|
+
|
|
23
|
+
Simply inject the `<PulseDevTools />` component anywhere in your root application tree. It renders as a portal and will not affect your layout.
|
|
24
|
+
|
|
25
|
+
```tsx
|
|
26
|
+
import React from "react";
|
|
27
|
+
import ReactDOM from "react-dom/client";
|
|
28
|
+
import { PulseDevTools } from "@pulse-js/tools";
|
|
29
|
+
import App from "./App";
|
|
30
|
+
|
|
31
|
+
ReactDOM.createRoot(document.getElementById("root")!).render(
|
|
32
|
+
<React.StrictMode>
|
|
33
|
+
<App />
|
|
34
|
+
<PulseDevTools shortcut="Ctrl+D" />
|
|
35
|
+
</React.StrictMode>
|
|
36
|
+
);
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Configuration Props
|
|
40
|
+
|
|
41
|
+
| Prop | Type | Default | Description |
|
|
42
|
+
| ---------- | -------- | ---------- | -------------------------------------------------------- |
|
|
43
|
+
| `shortcut` | `string` | `'Ctrl+D'` | Keyboard shortcut to toggle the visibility of the panel. |
|
|
44
|
+
|
|
45
|
+
## Tips
|
|
46
|
+
|
|
47
|
+
- **Naming Matters**: Ensure you provide string names to your Sources and Guards (e.g., `source(val, { name: 'my-source' })`). The DevTools rely on these names to provide meaningful debugging information. Unnamed units will appear but are harder to trace.
|
|
48
|
+
- **Status Indicators**:
|
|
49
|
+
- 🟢 **Green**: OK / Active
|
|
50
|
+
- 🔴 **Red**: Fails (Hover to see semantic failure reasons)
|
|
51
|
+
- 🟡 **Yellow**: Pending (Async operations in flight)
|
|
52
|
+
|
|
53
|
+
## Architecture
|
|
54
|
+
|
|
55
|
+
The DevTools communicate with the core library via the global `PulseRegistry`. This means it works seamlessly even if your application code is split across multiple modules or bundles, as long as they share the same Pulse instance.
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.tsx
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
PulseDevTools: () => PulseDevTools
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(index_exports);
|
|
26
|
+
var import_react = require("react");
|
|
27
|
+
var import_core = require("@pulse-js/core");
|
|
28
|
+
var import_react2 = require("@pulse-js/react");
|
|
29
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
30
|
+
var COLORS = {
|
|
31
|
+
bg: "rgba(13, 13, 18, 0.85)",
|
|
32
|
+
border: "rgba(255, 255, 255, 0.1)",
|
|
33
|
+
accent: "linear-gradient(135deg, #00d2ff 0%, #3a7bd5 100%)",
|
|
34
|
+
error: "#ff4b2b",
|
|
35
|
+
success: "#00f260",
|
|
36
|
+
pending: "#fdbb2d",
|
|
37
|
+
text: "#ffffff",
|
|
38
|
+
secondaryText: "#a0a0a0",
|
|
39
|
+
cardBg: "rgba(255, 255, 255, 0.05)"
|
|
40
|
+
};
|
|
41
|
+
var SCROLLBAR_CSS = `
|
|
42
|
+
.pulse-devtools-list::-webkit-scrollbar {
|
|
43
|
+
width: 6px;
|
|
44
|
+
}
|
|
45
|
+
.pulse-devtools-list::-webkit-scrollbar-track {
|
|
46
|
+
background: transparent;
|
|
47
|
+
}
|
|
48
|
+
.pulse-devtools-list::-webkit-scrollbar-thumb {
|
|
49
|
+
background: rgba(255, 255, 255, 0.1);
|
|
50
|
+
border-radius: 10px;
|
|
51
|
+
}
|
|
52
|
+
.pulse-devtools-list::-webkit-scrollbar-thumb:hover {
|
|
53
|
+
background: rgba(255, 255, 255, 0.2);
|
|
54
|
+
}
|
|
55
|
+
`;
|
|
56
|
+
var HeaderStyle = {
|
|
57
|
+
padding: "12px 16px",
|
|
58
|
+
background: "rgba(0,0,0,0.3)",
|
|
59
|
+
borderBottom: `1px solid ${COLORS.border}`,
|
|
60
|
+
display: "flex",
|
|
61
|
+
justifyContent: "space-between",
|
|
62
|
+
alignItems: "center",
|
|
63
|
+
cursor: "grab",
|
|
64
|
+
userSelect: "none"
|
|
65
|
+
};
|
|
66
|
+
var ListStyle = {
|
|
67
|
+
padding: "12px",
|
|
68
|
+
overflowY: "auto",
|
|
69
|
+
maxHeight: "320px",
|
|
70
|
+
// Limits height after ~4-5 items
|
|
71
|
+
flex: "0 1 auto"
|
|
72
|
+
};
|
|
73
|
+
var UnitStyle = (status, isError) => ({
|
|
74
|
+
padding: "10px",
|
|
75
|
+
marginBottom: "10px",
|
|
76
|
+
borderRadius: "10px",
|
|
77
|
+
backgroundColor: COLORS.cardBg,
|
|
78
|
+
border: `1px solid ${isError ? COLORS.error : COLORS.border}`,
|
|
79
|
+
fontSize: "13px",
|
|
80
|
+
transition: "all 0.2s ease",
|
|
81
|
+
boxShadow: isError ? `0 0 10px ${COLORS.error}44` : "none"
|
|
82
|
+
});
|
|
83
|
+
var StatusDot = (status) => ({
|
|
84
|
+
width: "8px",
|
|
85
|
+
height: "8px",
|
|
86
|
+
borderRadius: "50%",
|
|
87
|
+
display: "inline-block",
|
|
88
|
+
marginRight: "8px",
|
|
89
|
+
backgroundColor: status === "ok" ? COLORS.success : status === "fail" ? COLORS.error : COLORS.pending,
|
|
90
|
+
boxShadow: `0 0 8px ${status === "ok" ? COLORS.success : status === "fail" ? COLORS.error : COLORS.pending}`
|
|
91
|
+
});
|
|
92
|
+
var STORAGE_KEY = "pulse-devtools-pos";
|
|
93
|
+
var clamp = (val, min, max) => Math.max(min, Math.min(max, val));
|
|
94
|
+
function useDraggable() {
|
|
95
|
+
const [position, setPositionState] = (0, import_react.useState)(() => {
|
|
96
|
+
try {
|
|
97
|
+
const saved = localStorage.getItem(STORAGE_KEY);
|
|
98
|
+
if (saved) return JSON.parse(saved);
|
|
99
|
+
} catch (e) {
|
|
100
|
+
}
|
|
101
|
+
return { x: window.innerWidth - 140, y: window.innerHeight - 65 };
|
|
102
|
+
});
|
|
103
|
+
const isDragging = (0, import_react.useRef)(false);
|
|
104
|
+
const startMousePos = (0, import_react.useRef)({ x: 0, y: 0 });
|
|
105
|
+
const offset = (0, import_react.useRef)({ x: 0, y: 0 });
|
|
106
|
+
const totalMovement = (0, import_react.useRef)(0);
|
|
107
|
+
const updatePosition = (0, import_react.useCallback)((newPos) => {
|
|
108
|
+
setPositionState(newPos);
|
|
109
|
+
try {
|
|
110
|
+
localStorage.setItem(STORAGE_KEY, JSON.stringify(newPos));
|
|
111
|
+
} catch (e) {
|
|
112
|
+
}
|
|
113
|
+
}, []);
|
|
114
|
+
const onMouseMove = (0, import_react.useCallback)((e, currentW, currentH) => {
|
|
115
|
+
if (!isDragging.current) return;
|
|
116
|
+
totalMovement.current += Math.abs(e.clientX - startMousePos.current.x) + Math.abs(e.clientY - startMousePos.current.y);
|
|
117
|
+
startMousePos.current = { x: e.clientX, y: e.clientY };
|
|
118
|
+
updatePosition({
|
|
119
|
+
x: clamp(e.clientX - offset.current.x, 0, Math.max(0, window.innerWidth - currentW)),
|
|
120
|
+
y: clamp(e.clientY - offset.current.y, 0, Math.max(0, window.innerHeight - currentH))
|
|
121
|
+
});
|
|
122
|
+
}, [updatePosition]);
|
|
123
|
+
const startDragging = (0, import_react.useCallback)((e, w, h) => {
|
|
124
|
+
isDragging.current = true;
|
|
125
|
+
totalMovement.current = 0;
|
|
126
|
+
startMousePos.current = { x: e.clientX, y: e.clientY };
|
|
127
|
+
offset.current = {
|
|
128
|
+
x: e.clientX - position.x,
|
|
129
|
+
y: e.clientY - position.y
|
|
130
|
+
};
|
|
131
|
+
const handleMove = (moveEv) => onMouseMove(moveEv, w, h);
|
|
132
|
+
const handleUp = () => {
|
|
133
|
+
isDragging.current = false;
|
|
134
|
+
document.removeEventListener("mousemove", handleMove);
|
|
135
|
+
document.removeEventListener("mouseup", handleUp);
|
|
136
|
+
};
|
|
137
|
+
document.addEventListener("mousemove", handleMove);
|
|
138
|
+
document.addEventListener("mouseup", handleUp);
|
|
139
|
+
}, [position, onMouseMove]);
|
|
140
|
+
return { position, updatePosition, startDragging, totalMovement };
|
|
141
|
+
}
|
|
142
|
+
var UnitItem = ({ unit }) => {
|
|
143
|
+
const isGuard = "state" in unit;
|
|
144
|
+
const name = unit._name || (isGuard ? "Unnamed Guard" : "Unnamed Source");
|
|
145
|
+
const hasWarning = !unit._name;
|
|
146
|
+
if (isGuard) {
|
|
147
|
+
const state = (0, import_react2.usePulse)(unit);
|
|
148
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: UnitStyle(state.status, state.status === "fail"), children: [
|
|
149
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: "6px" }, children: [
|
|
150
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
|
|
151
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: StatusDot(state.status) }),
|
|
152
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { style: { color: COLORS.text }, children: name })
|
|
153
|
+
] }),
|
|
154
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { fontSize: "10px", color: COLORS.secondaryText }, children: "GUARD" })
|
|
155
|
+
] }),
|
|
156
|
+
state.status === "ok" && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { wordBreak: "break-all", color: COLORS.secondaryText }, children: [
|
|
157
|
+
"Value: ",
|
|
158
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", { style: { color: COLORS.success }, children: JSON.stringify(state.value) })
|
|
159
|
+
] }),
|
|
160
|
+
state.status === "fail" && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { color: COLORS.error, fontSize: "11px", marginTop: "4px", background: `${COLORS.error}11`, padding: "4px", borderRadius: "4px" }, children: [
|
|
161
|
+
"Error: ",
|
|
162
|
+
state.reason
|
|
163
|
+
] }),
|
|
164
|
+
hasWarning && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { fontSize: "10px", color: COLORS.pending, marginTop: "5px", opacity: 0.8 }, children: "\u26A0\uFE0F Best practice: Give guards a name for better debugging." })
|
|
165
|
+
] });
|
|
166
|
+
} else {
|
|
167
|
+
const value = (0, import_react2.usePulse)(unit);
|
|
168
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: UnitStyle("ok", false), children: [
|
|
169
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: "6px" }, children: [
|
|
170
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
|
|
171
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: StatusDot("ok") }),
|
|
172
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { style: { color: COLORS.text }, children: name })
|
|
173
|
+
] }),
|
|
174
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { fontSize: "10px", color: COLORS.secondaryText }, children: "SOURCE" })
|
|
175
|
+
] }),
|
|
176
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { wordBreak: "break-all", color: COLORS.secondaryText }, children: [
|
|
177
|
+
"Value: ",
|
|
178
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", { style: { color: "#00d2ff" }, children: JSON.stringify(value) })
|
|
179
|
+
] }),
|
|
180
|
+
hasWarning && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { fontSize: "10px", color: COLORS.pending, marginTop: "5px", opacity: 0.8 }, children: [
|
|
181
|
+
"\u26A0\uFE0F Tip: Name this source in `source(val, ",
|
|
182
|
+
"{ name: '...' }",
|
|
183
|
+
")`"
|
|
184
|
+
] })
|
|
185
|
+
] });
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
var PulseDevTools = ({ shortcut = "Ctrl+D" }) => {
|
|
189
|
+
const [units, setUnits] = (0, import_react.useState)(() => import_core.PulseRegistry.getAll());
|
|
190
|
+
const [isOpen, setIsOpen] = (0, import_react.useState)(false);
|
|
191
|
+
const W = 350;
|
|
192
|
+
const H = 450;
|
|
193
|
+
const BTN_W = 120;
|
|
194
|
+
const BTN_H = 45;
|
|
195
|
+
const { position, updatePosition, startDragging, totalMovement } = useDraggable();
|
|
196
|
+
const handleToggle = (0, import_react.useCallback)(() => {
|
|
197
|
+
if (totalMovement.current < 5) {
|
|
198
|
+
setIsOpen((prev) => {
|
|
199
|
+
const next = !prev;
|
|
200
|
+
const isLeft = position.x < window.innerWidth / 2;
|
|
201
|
+
const isTop = position.y < window.innerHeight / 2;
|
|
202
|
+
if (next) {
|
|
203
|
+
updatePosition({
|
|
204
|
+
x: clamp(isLeft ? position.x : position.x + BTN_W - W, 0, window.innerWidth - W),
|
|
205
|
+
y: clamp(isTop ? position.y : position.y + BTN_H - H, 0, window.innerHeight - H)
|
|
206
|
+
});
|
|
207
|
+
} else {
|
|
208
|
+
updatePosition({
|
|
209
|
+
x: clamp(isLeft ? position.x : position.x + W - BTN_W, 0, window.innerWidth - BTN_W),
|
|
210
|
+
y: clamp(isTop ? position.y : position.y + H - BTN_H, 0, window.innerHeight - BTN_H)
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
return next;
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
}, [totalMovement, updatePosition, position.x, position.y]);
|
|
217
|
+
(0, import_react.useEffect)(() => {
|
|
218
|
+
const style = document.createElement("style");
|
|
219
|
+
style.innerHTML = SCROLLBAR_CSS;
|
|
220
|
+
document.head.appendChild(style);
|
|
221
|
+
return () => {
|
|
222
|
+
document.head.removeChild(style);
|
|
223
|
+
};
|
|
224
|
+
}, []);
|
|
225
|
+
(0, import_react.useEffect)(() => {
|
|
226
|
+
const unsubscribe = import_core.PulseRegistry.onRegister((newUnit) => {
|
|
227
|
+
setUnits((prev) => {
|
|
228
|
+
const name = newUnit._name;
|
|
229
|
+
if (name) {
|
|
230
|
+
const index = prev.findIndex((u) => u._name === name);
|
|
231
|
+
if (index !== -1) {
|
|
232
|
+
const next = [...prev];
|
|
233
|
+
next[index] = newUnit;
|
|
234
|
+
return next;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
return [...prev, newUnit];
|
|
238
|
+
});
|
|
239
|
+
});
|
|
240
|
+
return () => {
|
|
241
|
+
unsubscribe();
|
|
242
|
+
};
|
|
243
|
+
}, []);
|
|
244
|
+
(0, import_react.useEffect)(() => {
|
|
245
|
+
const handleKey = (e) => {
|
|
246
|
+
const parts = shortcut.toLowerCase().split("+");
|
|
247
|
+
const needsCtrl = parts.includes("ctrl");
|
|
248
|
+
const key = parts[parts.length - 1];
|
|
249
|
+
if (needsCtrl && e.ctrlKey && e.key.toLowerCase() === key) {
|
|
250
|
+
e.preventDefault();
|
|
251
|
+
handleToggle();
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
window.addEventListener("keydown", handleKey);
|
|
255
|
+
return () => window.removeEventListener("keydown", handleKey);
|
|
256
|
+
}, [shortcut, handleToggle]);
|
|
257
|
+
if (!isOpen) {
|
|
258
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
259
|
+
"button",
|
|
260
|
+
{
|
|
261
|
+
onMouseDown: (e) => startDragging(e, BTN_W, BTN_H),
|
|
262
|
+
onClick: handleToggle,
|
|
263
|
+
title: `Toggle Pulse DevTools (${shortcut})`,
|
|
264
|
+
style: {
|
|
265
|
+
position: "fixed",
|
|
266
|
+
left: `${position.x}px`,
|
|
267
|
+
top: `${position.y}px`,
|
|
268
|
+
width: `${BTN_W}px`,
|
|
269
|
+
height: `${BTN_H}px`,
|
|
270
|
+
padding: 0,
|
|
271
|
+
borderRadius: "30px",
|
|
272
|
+
border: `1px solid ${COLORS.border}`,
|
|
273
|
+
background: COLORS.bg,
|
|
274
|
+
backdropFilter: "blur(15px)",
|
|
275
|
+
color: "white",
|
|
276
|
+
fontWeight: 600,
|
|
277
|
+
cursor: "grab",
|
|
278
|
+
boxShadow: "0 8px 32px rgba(0,0,0,0.4)",
|
|
279
|
+
zIndex: 9999,
|
|
280
|
+
fontSize: "13px",
|
|
281
|
+
display: "flex",
|
|
282
|
+
alignItems: "center",
|
|
283
|
+
justifyContent: "center",
|
|
284
|
+
userSelect: "none"
|
|
285
|
+
},
|
|
286
|
+
children: [
|
|
287
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { width: "10px", height: "10px", borderRadius: "50%", background: COLORS.accent, marginRight: "10px", boxShadow: `0 0 10px #00d2ff` } }),
|
|
288
|
+
"Pulse (",
|
|
289
|
+
units.length,
|
|
290
|
+
")"
|
|
291
|
+
]
|
|
292
|
+
}
|
|
293
|
+
);
|
|
294
|
+
}
|
|
295
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
296
|
+
"div",
|
|
297
|
+
{
|
|
298
|
+
style: {
|
|
299
|
+
position: "fixed",
|
|
300
|
+
left: `${position.x}px`,
|
|
301
|
+
top: `${position.y}px`,
|
|
302
|
+
width: `${W}px`,
|
|
303
|
+
maxHeight: `${H}px`,
|
|
304
|
+
backgroundColor: COLORS.bg,
|
|
305
|
+
backdropFilter: "blur(15px)",
|
|
306
|
+
border: `1px solid ${COLORS.border}`,
|
|
307
|
+
borderRadius: "16px",
|
|
308
|
+
boxShadow: "0 20px 40px rgba(0,0,0,0.4)",
|
|
309
|
+
display: "flex",
|
|
310
|
+
flexDirection: "column",
|
|
311
|
+
zIndex: 9999,
|
|
312
|
+
overflow: "hidden"
|
|
313
|
+
},
|
|
314
|
+
children: [
|
|
315
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: HeaderStyle, onMouseDown: (e) => startDragging(e, W, H), children: [
|
|
316
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", alignItems: "center" }, children: [
|
|
317
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { width: "12px", height: "12px", borderRadius: "4px", background: COLORS.accent, marginRight: "10px", boxShadow: `0 0 8px #00d2ff` } }),
|
|
318
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { color: "white", fontWeight: 600 }, children: "Pulse Inspector" })
|
|
319
|
+
] }),
|
|
320
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
321
|
+
"button",
|
|
322
|
+
{
|
|
323
|
+
onClick: handleToggle,
|
|
324
|
+
style: { background: "transparent", border: "none", color: COLORS.secondaryText, cursor: "pointer", fontSize: "20px", padding: "0 5px" },
|
|
325
|
+
children: "\xD7"
|
|
326
|
+
}
|
|
327
|
+
)
|
|
328
|
+
] }),
|
|
329
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: ListStyle, className: "pulse-devtools-list", children: units.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { textAlign: "center", color: COLORS.secondaryText, marginTop: "40px", fontSize: "14px" }, children: [
|
|
330
|
+
"No reactive units detected.",
|
|
331
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("br", {}),
|
|
332
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { fontSize: "11px", opacity: 0.6 }, children: "Use source() or guard() to begin." })
|
|
333
|
+
] }) : units.map((u, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(UnitItem, { unit: u }, u._name || i)) }),
|
|
334
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { padding: "10px", background: "rgba(0,0,0,0.2)", display: "flex", justifyContent: "space-between", fontSize: "10px", color: COLORS.secondaryText }, children: [
|
|
335
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "v0.1.0" }),
|
|
336
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
|
|
337
|
+
"Drag header to move \u2022 ",
|
|
338
|
+
shortcut,
|
|
339
|
+
" to toggle"
|
|
340
|
+
] })
|
|
341
|
+
] })
|
|
342
|
+
]
|
|
343
|
+
}
|
|
344
|
+
);
|
|
345
|
+
};
|
|
346
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
347
|
+
0 && (module.exports = {
|
|
348
|
+
PulseDevTools
|
|
349
|
+
});
|
package/dist/index.d.cts
ADDED
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
// src/index.tsx
|
|
2
|
+
import { useState, useEffect, useRef, useCallback } from "react";
|
|
3
|
+
import { PulseRegistry } from "@pulse-js/core";
|
|
4
|
+
import { usePulse } from "@pulse-js/react";
|
|
5
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
+
var COLORS = {
|
|
7
|
+
bg: "rgba(13, 13, 18, 0.85)",
|
|
8
|
+
border: "rgba(255, 255, 255, 0.1)",
|
|
9
|
+
accent: "linear-gradient(135deg, #00d2ff 0%, #3a7bd5 100%)",
|
|
10
|
+
error: "#ff4b2b",
|
|
11
|
+
success: "#00f260",
|
|
12
|
+
pending: "#fdbb2d",
|
|
13
|
+
text: "#ffffff",
|
|
14
|
+
secondaryText: "#a0a0a0",
|
|
15
|
+
cardBg: "rgba(255, 255, 255, 0.05)"
|
|
16
|
+
};
|
|
17
|
+
var SCROLLBAR_CSS = `
|
|
18
|
+
.pulse-devtools-list::-webkit-scrollbar {
|
|
19
|
+
width: 6px;
|
|
20
|
+
}
|
|
21
|
+
.pulse-devtools-list::-webkit-scrollbar-track {
|
|
22
|
+
background: transparent;
|
|
23
|
+
}
|
|
24
|
+
.pulse-devtools-list::-webkit-scrollbar-thumb {
|
|
25
|
+
background: rgba(255, 255, 255, 0.1);
|
|
26
|
+
border-radius: 10px;
|
|
27
|
+
}
|
|
28
|
+
.pulse-devtools-list::-webkit-scrollbar-thumb:hover {
|
|
29
|
+
background: rgba(255, 255, 255, 0.2);
|
|
30
|
+
}
|
|
31
|
+
`;
|
|
32
|
+
var HeaderStyle = {
|
|
33
|
+
padding: "12px 16px",
|
|
34
|
+
background: "rgba(0,0,0,0.3)",
|
|
35
|
+
borderBottom: `1px solid ${COLORS.border}`,
|
|
36
|
+
display: "flex",
|
|
37
|
+
justifyContent: "space-between",
|
|
38
|
+
alignItems: "center",
|
|
39
|
+
cursor: "grab",
|
|
40
|
+
userSelect: "none"
|
|
41
|
+
};
|
|
42
|
+
var ListStyle = {
|
|
43
|
+
padding: "12px",
|
|
44
|
+
overflowY: "auto",
|
|
45
|
+
maxHeight: "320px",
|
|
46
|
+
// Limits height after ~4-5 items
|
|
47
|
+
flex: "0 1 auto"
|
|
48
|
+
};
|
|
49
|
+
var UnitStyle = (status, isError) => ({
|
|
50
|
+
padding: "10px",
|
|
51
|
+
marginBottom: "10px",
|
|
52
|
+
borderRadius: "10px",
|
|
53
|
+
backgroundColor: COLORS.cardBg,
|
|
54
|
+
border: `1px solid ${isError ? COLORS.error : COLORS.border}`,
|
|
55
|
+
fontSize: "13px",
|
|
56
|
+
transition: "all 0.2s ease",
|
|
57
|
+
boxShadow: isError ? `0 0 10px ${COLORS.error}44` : "none"
|
|
58
|
+
});
|
|
59
|
+
var StatusDot = (status) => ({
|
|
60
|
+
width: "8px",
|
|
61
|
+
height: "8px",
|
|
62
|
+
borderRadius: "50%",
|
|
63
|
+
display: "inline-block",
|
|
64
|
+
marginRight: "8px",
|
|
65
|
+
backgroundColor: status === "ok" ? COLORS.success : status === "fail" ? COLORS.error : COLORS.pending,
|
|
66
|
+
boxShadow: `0 0 8px ${status === "ok" ? COLORS.success : status === "fail" ? COLORS.error : COLORS.pending}`
|
|
67
|
+
});
|
|
68
|
+
var STORAGE_KEY = "pulse-devtools-pos";
|
|
69
|
+
var clamp = (val, min, max) => Math.max(min, Math.min(max, val));
|
|
70
|
+
function useDraggable() {
|
|
71
|
+
const [position, setPositionState] = useState(() => {
|
|
72
|
+
try {
|
|
73
|
+
const saved = localStorage.getItem(STORAGE_KEY);
|
|
74
|
+
if (saved) return JSON.parse(saved);
|
|
75
|
+
} catch (e) {
|
|
76
|
+
}
|
|
77
|
+
return { x: window.innerWidth - 140, y: window.innerHeight - 65 };
|
|
78
|
+
});
|
|
79
|
+
const isDragging = useRef(false);
|
|
80
|
+
const startMousePos = useRef({ x: 0, y: 0 });
|
|
81
|
+
const offset = useRef({ x: 0, y: 0 });
|
|
82
|
+
const totalMovement = useRef(0);
|
|
83
|
+
const updatePosition = useCallback((newPos) => {
|
|
84
|
+
setPositionState(newPos);
|
|
85
|
+
try {
|
|
86
|
+
localStorage.setItem(STORAGE_KEY, JSON.stringify(newPos));
|
|
87
|
+
} catch (e) {
|
|
88
|
+
}
|
|
89
|
+
}, []);
|
|
90
|
+
const onMouseMove = useCallback((e, currentW, currentH) => {
|
|
91
|
+
if (!isDragging.current) return;
|
|
92
|
+
totalMovement.current += Math.abs(e.clientX - startMousePos.current.x) + Math.abs(e.clientY - startMousePos.current.y);
|
|
93
|
+
startMousePos.current = { x: e.clientX, y: e.clientY };
|
|
94
|
+
updatePosition({
|
|
95
|
+
x: clamp(e.clientX - offset.current.x, 0, Math.max(0, window.innerWidth - currentW)),
|
|
96
|
+
y: clamp(e.clientY - offset.current.y, 0, Math.max(0, window.innerHeight - currentH))
|
|
97
|
+
});
|
|
98
|
+
}, [updatePosition]);
|
|
99
|
+
const startDragging = useCallback((e, w, h) => {
|
|
100
|
+
isDragging.current = true;
|
|
101
|
+
totalMovement.current = 0;
|
|
102
|
+
startMousePos.current = { x: e.clientX, y: e.clientY };
|
|
103
|
+
offset.current = {
|
|
104
|
+
x: e.clientX - position.x,
|
|
105
|
+
y: e.clientY - position.y
|
|
106
|
+
};
|
|
107
|
+
const handleMove = (moveEv) => onMouseMove(moveEv, w, h);
|
|
108
|
+
const handleUp = () => {
|
|
109
|
+
isDragging.current = false;
|
|
110
|
+
document.removeEventListener("mousemove", handleMove);
|
|
111
|
+
document.removeEventListener("mouseup", handleUp);
|
|
112
|
+
};
|
|
113
|
+
document.addEventListener("mousemove", handleMove);
|
|
114
|
+
document.addEventListener("mouseup", handleUp);
|
|
115
|
+
}, [position, onMouseMove]);
|
|
116
|
+
return { position, updatePosition, startDragging, totalMovement };
|
|
117
|
+
}
|
|
118
|
+
var UnitItem = ({ unit }) => {
|
|
119
|
+
const isGuard = "state" in unit;
|
|
120
|
+
const name = unit._name || (isGuard ? "Unnamed Guard" : "Unnamed Source");
|
|
121
|
+
const hasWarning = !unit._name;
|
|
122
|
+
if (isGuard) {
|
|
123
|
+
const state = usePulse(unit);
|
|
124
|
+
return /* @__PURE__ */ jsxs("div", { style: UnitStyle(state.status, state.status === "fail"), children: [
|
|
125
|
+
/* @__PURE__ */ jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: "6px" }, children: [
|
|
126
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
127
|
+
/* @__PURE__ */ jsx("span", { style: StatusDot(state.status) }),
|
|
128
|
+
/* @__PURE__ */ jsx("strong", { style: { color: COLORS.text }, children: name })
|
|
129
|
+
] }),
|
|
130
|
+
/* @__PURE__ */ jsx("span", { style: { fontSize: "10px", color: COLORS.secondaryText }, children: "GUARD" })
|
|
131
|
+
] }),
|
|
132
|
+
state.status === "ok" && /* @__PURE__ */ jsxs("div", { style: { wordBreak: "break-all", color: COLORS.secondaryText }, children: [
|
|
133
|
+
"Value: ",
|
|
134
|
+
/* @__PURE__ */ jsx("code", { style: { color: COLORS.success }, children: JSON.stringify(state.value) })
|
|
135
|
+
] }),
|
|
136
|
+
state.status === "fail" && /* @__PURE__ */ jsxs("div", { style: { color: COLORS.error, fontSize: "11px", marginTop: "4px", background: `${COLORS.error}11`, padding: "4px", borderRadius: "4px" }, children: [
|
|
137
|
+
"Error: ",
|
|
138
|
+
state.reason
|
|
139
|
+
] }),
|
|
140
|
+
hasWarning && /* @__PURE__ */ jsx("div", { style: { fontSize: "10px", color: COLORS.pending, marginTop: "5px", opacity: 0.8 }, children: "\u26A0\uFE0F Best practice: Give guards a name for better debugging." })
|
|
141
|
+
] });
|
|
142
|
+
} else {
|
|
143
|
+
const value = usePulse(unit);
|
|
144
|
+
return /* @__PURE__ */ jsxs("div", { style: UnitStyle("ok", false), children: [
|
|
145
|
+
/* @__PURE__ */ jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: "6px" }, children: [
|
|
146
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
147
|
+
/* @__PURE__ */ jsx("span", { style: StatusDot("ok") }),
|
|
148
|
+
/* @__PURE__ */ jsx("strong", { style: { color: COLORS.text }, children: name })
|
|
149
|
+
] }),
|
|
150
|
+
/* @__PURE__ */ jsx("span", { style: { fontSize: "10px", color: COLORS.secondaryText }, children: "SOURCE" })
|
|
151
|
+
] }),
|
|
152
|
+
/* @__PURE__ */ jsxs("div", { style: { wordBreak: "break-all", color: COLORS.secondaryText }, children: [
|
|
153
|
+
"Value: ",
|
|
154
|
+
/* @__PURE__ */ jsx("code", { style: { color: "#00d2ff" }, children: JSON.stringify(value) })
|
|
155
|
+
] }),
|
|
156
|
+
hasWarning && /* @__PURE__ */ jsxs("div", { style: { fontSize: "10px", color: COLORS.pending, marginTop: "5px", opacity: 0.8 }, children: [
|
|
157
|
+
"\u26A0\uFE0F Tip: Name this source in `source(val, ",
|
|
158
|
+
"{ name: '...' }",
|
|
159
|
+
")`"
|
|
160
|
+
] })
|
|
161
|
+
] });
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
var PulseDevTools = ({ shortcut = "Ctrl+D" }) => {
|
|
165
|
+
const [units, setUnits] = useState(() => PulseRegistry.getAll());
|
|
166
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
167
|
+
const W = 350;
|
|
168
|
+
const H = 450;
|
|
169
|
+
const BTN_W = 120;
|
|
170
|
+
const BTN_H = 45;
|
|
171
|
+
const { position, updatePosition, startDragging, totalMovement } = useDraggable();
|
|
172
|
+
const handleToggle = useCallback(() => {
|
|
173
|
+
if (totalMovement.current < 5) {
|
|
174
|
+
setIsOpen((prev) => {
|
|
175
|
+
const next = !prev;
|
|
176
|
+
const isLeft = position.x < window.innerWidth / 2;
|
|
177
|
+
const isTop = position.y < window.innerHeight / 2;
|
|
178
|
+
if (next) {
|
|
179
|
+
updatePosition({
|
|
180
|
+
x: clamp(isLeft ? position.x : position.x + BTN_W - W, 0, window.innerWidth - W),
|
|
181
|
+
y: clamp(isTop ? position.y : position.y + BTN_H - H, 0, window.innerHeight - H)
|
|
182
|
+
});
|
|
183
|
+
} else {
|
|
184
|
+
updatePosition({
|
|
185
|
+
x: clamp(isLeft ? position.x : position.x + W - BTN_W, 0, window.innerWidth - BTN_W),
|
|
186
|
+
y: clamp(isTop ? position.y : position.y + H - BTN_H, 0, window.innerHeight - BTN_H)
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
return next;
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
}, [totalMovement, updatePosition, position.x, position.y]);
|
|
193
|
+
useEffect(() => {
|
|
194
|
+
const style = document.createElement("style");
|
|
195
|
+
style.innerHTML = SCROLLBAR_CSS;
|
|
196
|
+
document.head.appendChild(style);
|
|
197
|
+
return () => {
|
|
198
|
+
document.head.removeChild(style);
|
|
199
|
+
};
|
|
200
|
+
}, []);
|
|
201
|
+
useEffect(() => {
|
|
202
|
+
const unsubscribe = PulseRegistry.onRegister((newUnit) => {
|
|
203
|
+
setUnits((prev) => {
|
|
204
|
+
const name = newUnit._name;
|
|
205
|
+
if (name) {
|
|
206
|
+
const index = prev.findIndex((u) => u._name === name);
|
|
207
|
+
if (index !== -1) {
|
|
208
|
+
const next = [...prev];
|
|
209
|
+
next[index] = newUnit;
|
|
210
|
+
return next;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
return [...prev, newUnit];
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
return () => {
|
|
217
|
+
unsubscribe();
|
|
218
|
+
};
|
|
219
|
+
}, []);
|
|
220
|
+
useEffect(() => {
|
|
221
|
+
const handleKey = (e) => {
|
|
222
|
+
const parts = shortcut.toLowerCase().split("+");
|
|
223
|
+
const needsCtrl = parts.includes("ctrl");
|
|
224
|
+
const key = parts[parts.length - 1];
|
|
225
|
+
if (needsCtrl && e.ctrlKey && e.key.toLowerCase() === key) {
|
|
226
|
+
e.preventDefault();
|
|
227
|
+
handleToggle();
|
|
228
|
+
}
|
|
229
|
+
};
|
|
230
|
+
window.addEventListener("keydown", handleKey);
|
|
231
|
+
return () => window.removeEventListener("keydown", handleKey);
|
|
232
|
+
}, [shortcut, handleToggle]);
|
|
233
|
+
if (!isOpen) {
|
|
234
|
+
return /* @__PURE__ */ jsxs(
|
|
235
|
+
"button",
|
|
236
|
+
{
|
|
237
|
+
onMouseDown: (e) => startDragging(e, BTN_W, BTN_H),
|
|
238
|
+
onClick: handleToggle,
|
|
239
|
+
title: `Toggle Pulse DevTools (${shortcut})`,
|
|
240
|
+
style: {
|
|
241
|
+
position: "fixed",
|
|
242
|
+
left: `${position.x}px`,
|
|
243
|
+
top: `${position.y}px`,
|
|
244
|
+
width: `${BTN_W}px`,
|
|
245
|
+
height: `${BTN_H}px`,
|
|
246
|
+
padding: 0,
|
|
247
|
+
borderRadius: "30px",
|
|
248
|
+
border: `1px solid ${COLORS.border}`,
|
|
249
|
+
background: COLORS.bg,
|
|
250
|
+
backdropFilter: "blur(15px)",
|
|
251
|
+
color: "white",
|
|
252
|
+
fontWeight: 600,
|
|
253
|
+
cursor: "grab",
|
|
254
|
+
boxShadow: "0 8px 32px rgba(0,0,0,0.4)",
|
|
255
|
+
zIndex: 9999,
|
|
256
|
+
fontSize: "13px",
|
|
257
|
+
display: "flex",
|
|
258
|
+
alignItems: "center",
|
|
259
|
+
justifyContent: "center",
|
|
260
|
+
userSelect: "none"
|
|
261
|
+
},
|
|
262
|
+
children: [
|
|
263
|
+
/* @__PURE__ */ jsx("div", { style: { width: "10px", height: "10px", borderRadius: "50%", background: COLORS.accent, marginRight: "10px", boxShadow: `0 0 10px #00d2ff` } }),
|
|
264
|
+
"Pulse (",
|
|
265
|
+
units.length,
|
|
266
|
+
")"
|
|
267
|
+
]
|
|
268
|
+
}
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
return /* @__PURE__ */ jsxs(
|
|
272
|
+
"div",
|
|
273
|
+
{
|
|
274
|
+
style: {
|
|
275
|
+
position: "fixed",
|
|
276
|
+
left: `${position.x}px`,
|
|
277
|
+
top: `${position.y}px`,
|
|
278
|
+
width: `${W}px`,
|
|
279
|
+
maxHeight: `${H}px`,
|
|
280
|
+
backgroundColor: COLORS.bg,
|
|
281
|
+
backdropFilter: "blur(15px)",
|
|
282
|
+
border: `1px solid ${COLORS.border}`,
|
|
283
|
+
borderRadius: "16px",
|
|
284
|
+
boxShadow: "0 20px 40px rgba(0,0,0,0.4)",
|
|
285
|
+
display: "flex",
|
|
286
|
+
flexDirection: "column",
|
|
287
|
+
zIndex: 9999,
|
|
288
|
+
overflow: "hidden"
|
|
289
|
+
},
|
|
290
|
+
children: [
|
|
291
|
+
/* @__PURE__ */ jsxs("div", { style: HeaderStyle, onMouseDown: (e) => startDragging(e, W, H), children: [
|
|
292
|
+
/* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center" }, children: [
|
|
293
|
+
/* @__PURE__ */ jsx("div", { style: { width: "12px", height: "12px", borderRadius: "4px", background: COLORS.accent, marginRight: "10px", boxShadow: `0 0 8px #00d2ff` } }),
|
|
294
|
+
/* @__PURE__ */ jsx("span", { style: { color: "white", fontWeight: 600 }, children: "Pulse Inspector" })
|
|
295
|
+
] }),
|
|
296
|
+
/* @__PURE__ */ jsx(
|
|
297
|
+
"button",
|
|
298
|
+
{
|
|
299
|
+
onClick: handleToggle,
|
|
300
|
+
style: { background: "transparent", border: "none", color: COLORS.secondaryText, cursor: "pointer", fontSize: "20px", padding: "0 5px" },
|
|
301
|
+
children: "\xD7"
|
|
302
|
+
}
|
|
303
|
+
)
|
|
304
|
+
] }),
|
|
305
|
+
/* @__PURE__ */ jsx("div", { style: ListStyle, className: "pulse-devtools-list", children: units.length === 0 ? /* @__PURE__ */ jsxs("div", { style: { textAlign: "center", color: COLORS.secondaryText, marginTop: "40px", fontSize: "14px" }, children: [
|
|
306
|
+
"No reactive units detected.",
|
|
307
|
+
/* @__PURE__ */ jsx("br", {}),
|
|
308
|
+
/* @__PURE__ */ jsx("span", { style: { fontSize: "11px", opacity: 0.6 }, children: "Use source() or guard() to begin." })
|
|
309
|
+
] }) : units.map((u, i) => /* @__PURE__ */ jsx(UnitItem, { unit: u }, u._name || i)) }),
|
|
310
|
+
/* @__PURE__ */ jsxs("div", { style: { padding: "10px", background: "rgba(0,0,0,0.2)", display: "flex", justifyContent: "space-between", fontSize: "10px", color: COLORS.secondaryText }, children: [
|
|
311
|
+
/* @__PURE__ */ jsx("span", { children: "v0.1.0" }),
|
|
312
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
313
|
+
"Drag header to move \u2022 ",
|
|
314
|
+
shortcut,
|
|
315
|
+
" to toggle"
|
|
316
|
+
] })
|
|
317
|
+
] })
|
|
318
|
+
]
|
|
319
|
+
}
|
|
320
|
+
);
|
|
321
|
+
};
|
|
322
|
+
export {
|
|
323
|
+
PulseDevTools
|
|
324
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pulse-js/tools",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "dist/index.cjs",
|
|
6
|
+
"module": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist"
|
|
10
|
+
],
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "bun x tsup src/index.tsx --format esm,cjs --dts --clean --external react,react-dom,@pulse-js/core,@pulse-js/react",
|
|
13
|
+
"lint": "bun x tsc --noEmit"
|
|
14
|
+
},
|
|
15
|
+
"peerDependencies": {
|
|
16
|
+
"react": ">=18.0.0",
|
|
17
|
+
"react-dom": ">=18.0.0"
|
|
18
|
+
},
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"@pulse-js/core": "^0.1.1",
|
|
21
|
+
"@pulse-js/react": "^0.1.1"
|
|
22
|
+
}
|
|
23
|
+
}
|