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