@pulse-js/tools 0.1.1 → 0.1.3

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/index.js CHANGED
@@ -1,10 +1,7 @@
1
- // src/index.tsx
2
- import { useState, useEffect, useRef, useCallback } from "react";
1
+ // src/pulse-inspector.ts
3
2
  import { PulseRegistry } from "@pulse-js/core";
4
- import { usePulse } from "@pulse-js/react";
5
- import { jsx, jsxs } from "react/jsx-runtime";
6
3
  var COLORS = {
7
- bg: "rgba(13, 13, 18, 0.85)",
4
+ bg: "rgba(13, 13, 18, 0.95)",
8
5
  border: "rgba(255, 255, 255, 0.1)",
9
6
  accent: "linear-gradient(135deg, #00d2ff 0%, #3a7bd5 100%)",
10
7
  error: "#ff4b2b",
@@ -14,311 +11,338 @@ var COLORS = {
14
11
  secondaryText: "#a0a0a0",
15
12
  cardBg: "rgba(255, 255, 255, 0.05)"
16
13
  };
17
- var SCROLLBAR_CSS = `
18
- .pulse-devtools-list::-webkit-scrollbar {
19
- width: 6px;
20
- }
21
- .pulse-devtools-list::-webkit-scrollbar-track {
22
- background: transparent;
14
+ var STORAGE_KEY = "pulse-devtools-pos";
15
+ var PulseInspector = class extends HTMLElement {
16
+ shadow;
17
+ units = [];
18
+ isOpen = false;
19
+ position = { x: window.innerWidth - 140, y: window.innerHeight - 65 };
20
+ isDragging = false;
21
+ offset = { x: 0, y: 0 };
22
+ totalMovement = 0;
23
+ lastMousePos = { x: 0, y: 0 };
24
+ unsubscribeRegistry;
25
+ unitSubscriptions = /* @__PURE__ */ new Map();
26
+ constructor() {
27
+ super();
28
+ this.shadow = this.attachShadow({ mode: "open" });
29
+ this.loadPosition();
23
30
  }
24
- .pulse-devtools-list::-webkit-scrollbar-thumb {
25
- background: rgba(255, 255, 255, 0.1);
26
- border-radius: 10px;
31
+ connectedCallback() {
32
+ this.render();
33
+ this.setupListeners();
34
+ this.refreshUnits();
35
+ this.unsubscribeRegistry = PulseRegistry.onRegister(() => {
36
+ this.refreshUnits();
37
+ });
38
+ window.addEventListener("keydown", this.handleKeyDown);
27
39
  }
28
- .pulse-devtools-list::-webkit-scrollbar-thumb:hover {
29
- background: rgba(255, 255, 255, 0.2);
40
+ disconnectedCallback() {
41
+ if (this.unsubscribeRegistry) this.unsubscribeRegistry();
42
+ this.unitSubscriptions.forEach((unsub) => unsub());
43
+ window.removeEventListener("keydown", this.handleKeyDown);
30
44
  }
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(() => {
45
+ loadPosition() {
72
46
  try {
73
47
  const saved = localStorage.getItem(STORAGE_KEY);
74
- if (saved) return JSON.parse(saved);
48
+ if (saved) this.position = JSON.parse(saved);
75
49
  } catch (e) {
76
50
  }
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);
51
+ }
52
+ savePosition() {
85
53
  try {
86
- localStorage.setItem(STORAGE_KEY, JSON.stringify(newPos));
54
+ localStorage.setItem(STORAGE_KEY, JSON.stringify(this.position));
87
55
  } catch (e) {
88
56
  }
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
57
  }
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];
58
+ refreshUnits() {
59
+ this.units = PulseRegistry.getAll();
60
+ this.updateUnitSubscriptions();
61
+ this.render();
62
+ }
63
+ updateUnitSubscriptions() {
64
+ this.unitSubscriptions.forEach((unsub) => unsub());
65
+ this.unitSubscriptions.clear();
66
+ this.units.forEach((unit) => {
67
+ const unsub = unit.subscribe(() => {
68
+ this.render();
214
69
  });
70
+ this.unitSubscriptions.set(unit, unsub);
215
71
  });
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();
72
+ }
73
+ setupListeners() {
74
+ this.shadow.addEventListener("mousedown", (e) => this.startDragging(e));
75
+ }
76
+ handleKeyDown = (e) => {
77
+ if (e.ctrlKey && e.key.toLowerCase() === "d") {
78
+ e.preventDefault();
79
+ this.toggle();
80
+ }
81
+ };
82
+ toggle() {
83
+ if (this.totalMovement < 5) {
84
+ this.isOpen = !this.isOpen;
85
+ if (this.isOpen) {
86
+ this.position.x = Math.max(0, Math.min(window.innerWidth - 350, this.position.x));
87
+ this.position.y = Math.max(0, Math.min(window.innerHeight - 450, this.position.y));
88
+ } else {
89
+ this.position.x = Math.max(0, Math.min(window.innerWidth - 120, this.position.x));
90
+ this.position.y = Math.max(0, Math.min(window.innerHeight - 45, this.position.y));
228
91
  }
92
+ this.savePosition();
93
+ this.render();
94
+ }
95
+ }
96
+ startDragging(e) {
97
+ const target = e.target;
98
+ const isHeader = target.closest(".header") || target.classList.contains("toggle-btn");
99
+ if (!isHeader) return;
100
+ this.isDragging = true;
101
+ this.totalMovement = 0;
102
+ this.lastMousePos = { x: e.clientX, y: e.clientY };
103
+ this.offset = {
104
+ x: e.clientX - this.position.x,
105
+ y: e.clientY - this.position.y
229
106
  };
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
- ]
107
+ const onMouseMove = (moveEv) => {
108
+ if (!this.isDragging) return;
109
+ this.totalMovement += Math.abs(moveEv.clientX - this.lastMousePos.x) + Math.abs(moveEv.clientY - this.lastMousePos.y);
110
+ this.lastMousePos = { x: moveEv.clientX, y: moveEv.clientY };
111
+ const w = this.isOpen ? 350 : 120;
112
+ const h = this.isOpen ? 450 : 45;
113
+ this.position = {
114
+ x: Math.max(0, Math.min(window.innerWidth - w, moveEv.clientX - this.offset.x)),
115
+ y: Math.max(0, Math.min(window.innerHeight - h, moveEv.clientY - this.offset.y))
116
+ };
117
+ const container = this.shadow.querySelector(".container");
118
+ if (container) {
119
+ container.style.left = `${this.position.x}px`;
120
+ container.style.top = `${this.position.y}px`;
268
121
  }
269
- );
122
+ };
123
+ const onMouseUp = () => {
124
+ this.isDragging = false;
125
+ this.savePosition();
126
+ document.removeEventListener("mousemove", onMouseMove);
127
+ document.removeEventListener("mouseup", onMouseUp);
128
+ };
129
+ document.addEventListener("mousemove", onMouseMove);
130
+ document.addEventListener("mouseup", onMouseUp);
270
131
  }
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
- ]
132
+ render() {
133
+ const w = this.isOpen ? 350 : 120;
134
+ const h = this.isOpen ? 450 : 45;
135
+ this.shadow.innerHTML = `
136
+ <style>
137
+ :host {
138
+ all: initial;
139
+ }
140
+ .container {
141
+ position: fixed;
142
+ left: ${this.position.x}px;
143
+ top: ${this.position.y}px;
144
+ width: ${w}px;
145
+ z-index: 999999;
146
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
147
+ color: white;
148
+ user-select: none;
149
+ }
150
+ .glass {
151
+ background: ${COLORS.bg};
152
+ backdrop-filter: blur(20px);
153
+ -webkit-backdrop-filter: blur(20px);
154
+ border: 1px solid ${COLORS.border};
155
+ border-radius: ${this.isOpen ? "16px" : "30px"};
156
+ box-shadow: 0 10px 40px rgba(0,0,0,0.5);
157
+ overflow: hidden;
158
+ transition: border-radius 0.2s ease;
159
+ display: flex;
160
+ flex-direction: column;
161
+ }
162
+ .toggle-btn {
163
+ width: 120px;
164
+ height: 45px;
165
+ display: flex;
166
+ align-items: center;
167
+ justify-content: center;
168
+ cursor: grab;
169
+ font-weight: 600;
170
+ font-size: 13px;
171
+ }
172
+ .dot {
173
+ width: 8px;
174
+ height: 8px;
175
+ border-radius: 50%;
176
+ background: ${COLORS.accent};
177
+ margin-right: 10px;
178
+ box-shadow: 0 0 10px #00d2ff;
179
+ }
180
+ .header {
181
+ padding: 12px 16px;
182
+ background: rgba(0,0,0,0.3);
183
+ border-bottom: 1px solid ${COLORS.border};
184
+ display: flex;
185
+ justify-content: space-between;
186
+ align-items: center;
187
+ cursor: grab;
188
+ }
189
+ .list {
190
+ flex: 1;
191
+ overflow-y: auto;
192
+ max-height: 380px;
193
+ padding: 12px;
194
+ }
195
+ .list::-webkit-scrollbar { width: 5px; }
196
+ .list::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.1); border-radius: 10px; }
197
+
198
+ .unit-card {
199
+ padding: 10px;
200
+ margin-bottom: 10px;
201
+ border-radius: 10px;
202
+ background: ${COLORS.cardBg};
203
+ border: 1px solid ${COLORS.border};
204
+ font-size: 12px;
205
+ }
206
+ .unit-header {
207
+ display: flex;
208
+ justify-content: space-between;
209
+ margin-bottom: 5px;
210
+ }
211
+ .unit-type {
212
+ font-size: 9px;
213
+ opacity: 0.5;
214
+ text-transform: uppercase;
215
+ }
216
+ .status-dot {
217
+ width: 6px;
218
+ height: 6px;
219
+ border-radius: 50%;
220
+ display: inline-block;
221
+ margin-right: 6px;
222
+ }
223
+ .status-ok { background: ${COLORS.success}; box-shadow: 0 0 5px ${COLORS.success}; }
224
+ .status-fail { background: ${COLORS.error}; box-shadow: 0 0 5px ${COLORS.error}; }
225
+ .status-pending { background: ${COLORS.pending}; box-shadow: 0 0 5px ${COLORS.pending}; }
226
+
227
+ .value {
228
+ color: #00d2ff;
229
+ font-family: monospace;
230
+ word-break: break-all;
231
+ }
232
+ .reason {
233
+ color: ${COLORS.error};
234
+ margin-top: 4px;
235
+ font-size: 11px;
236
+ background: rgba(255,75,43,0.1);
237
+ padding: 4px;
238
+ border-radius: 4px;
239
+ }
240
+ .footer {
241
+ padding: 8px 12px;
242
+ font-size: 10px;
243
+ opacity: 0.5;
244
+ text-align: right;
245
+ background: rgba(0,0,0,0.1);
246
+ }
247
+ .explain-toggle {
248
+ margin-top: 8px;
249
+ font-size: 10px;
250
+ color: ${COLORS.secondaryText};
251
+ cursor: pointer;
252
+ text-decoration: underline;
253
+ }
254
+ .explanation {
255
+ margin-top: 8px;
256
+ padding-top: 8px;
257
+ border-top: 1px dashed ${COLORS.border};
258
+ }
259
+ .dep-item {
260
+ display: flex;
261
+ justify-content: space-between;
262
+ padding: 2px 0;
263
+ opacity: 0.8;
264
+ }
265
+ </style>
266
+ <div class="container">
267
+ <div class="glass">
268
+ ${!this.isOpen ? `
269
+ <div class="toggle-btn" id="toggle">
270
+ <div class="dot"></div>
271
+ Pulse (${this.units.length})
272
+ </div>
273
+ ` : `
274
+ <div class="header">
275
+ <div style="display:flex;align-items:center">
276
+ <div class="dot" style="width:10px;height:10px"></div>
277
+ <strong style="font-size:14px">Pulse Inspector</strong>
278
+ </div>
279
+ <div id="close" style="cursor:pointer;font-size:18px;opacity:0.6">\xD7</div>
280
+ </div>
281
+ <div class="list">
282
+ ${this.units.length === 0 ? '<div style="text-align:center;padding:20px;opacity:0.5">No units detected</div>' : ""}
283
+ ${this.units.map((u) => this.renderUnit(u)).join("")}
284
+ </div>
285
+ <div class="footer">v0.2.0 \u2022 Framework-Agnostic</div>
286
+ `}
287
+ </div>
288
+ </div>
289
+ `;
290
+ this.shadow.getElementById("toggle")?.addEventListener("click", () => this.toggle());
291
+ this.shadow.getElementById("close")?.addEventListener("click", () => this.toggle());
292
+ }
293
+ renderUnit(unit) {
294
+ const isGuard = "state" in unit;
295
+ const name = unit._name || "unnamed";
296
+ if (isGuard) {
297
+ const g = unit;
298
+ const explanation = g.explain();
299
+ const statusClass = `status-${explanation.status}`;
300
+ return `
301
+ <div class="unit-card" style="border-color: ${explanation.status === "fail" ? COLORS.error + "44" : COLORS.border}">
302
+ <div class="unit-header">
303
+ <span>
304
+ <span class="status-dot ${statusClass}"></span>
305
+ <strong>${name}</strong>
306
+ </span>
307
+ <span class="unit-type">Guard</span>
308
+ </div>
309
+ ${explanation.status === "ok" ? `<div class="value">Value: ${JSON.stringify(explanation.value)}</div>` : ""}
310
+ ${explanation.status === "fail" ? `<div class="reason">${explanation.reason}</div>` : ""}
311
+ ${explanation.status === "pending" ? `<div style="opacity:0.5">Evaluating...</div>` : ""}
312
+
313
+ ${explanation.dependencies.length > 0 ? `
314
+ <div class="explanation">
315
+ <div style="font-size:9px;opacity:0.5;margin-bottom:4px">DEPENDENCIES</div>
316
+ ${explanation.dependencies.map((dep) => `
317
+ <div class="dep-item">
318
+ <span>${dep.name} <small opacity="0.5">(${dep.type})</small></span>
319
+ ${dep.status ? `<span class="status-dot status-${dep.status}" style="width:4px;height:4px"></span>` : ""}
320
+ </div>
321
+ `).join("")}
322
+ </div>
323
+ ` : ""}
324
+ </div>
325
+ `;
326
+ } else {
327
+ const value = unit();
328
+ return `
329
+ <div class="unit-card">
330
+ <div class="unit-header">
331
+ <span>
332
+ <span class="status-dot status-ok"></span>
333
+ <strong>${name}</strong>
334
+ </span>
335
+ <span class="unit-type">Source</span>
336
+ </div>
337
+ <div class="value">Value: ${JSON.stringify(value)}</div>
338
+ </div>
339
+ `;
319
340
  }
320
- );
341
+ }
321
342
  };
343
+ if (!customElements.get("pulse-inspector")) {
344
+ customElements.define("pulse-inspector", PulseInspector);
345
+ }
322
346
  export {
323
- PulseDevTools
347
+ PulseInspector
324
348
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pulse-js/tools",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "type": "module",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.js",
@@ -9,15 +9,10 @@
9
9
  "dist"
10
10
  ],
11
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",
12
+ "build": "bun x tsup src/index.ts --format esm,cjs --dts --clean --external @pulse-js/core",
13
13
  "lint": "bun x tsc --noEmit"
14
14
  },
15
- "peerDependencies": {
16
- "react": ">=18.0.0",
17
- "react-dom": ">=18.0.0"
18
- },
19
15
  "dependencies": {
20
- "@pulse-js/core": "^0.1.1",
21
- "@pulse-js/react": "^0.1.1"
16
+ "@pulse-js/core": "^0.1.5"
22
17
  }
23
18
  }