@tailwind-styled/devtools 2.0.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/index.js ADDED
@@ -0,0 +1,953 @@
1
+ "use client";
2
+ var __defProp = Object.defineProperty;
3
+ var __defProps = Object.defineProperties;
4
+ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
5
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
8
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
9
+ var __spreadValues = (a, b) => {
10
+ for (var prop in b || (b = {}))
11
+ if (__hasOwnProp.call(b, prop))
12
+ __defNormalProp(a, prop, b[prop]);
13
+ if (__getOwnPropSymbols)
14
+ for (var prop of __getOwnPropSymbols(b)) {
15
+ if (__propIsEnum.call(b, prop))
16
+ __defNormalProp(a, prop, b[prop]);
17
+ }
18
+ return a;
19
+ };
20
+ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
21
+
22
+ // src/index.tsx
23
+ import React, { useCallback, useEffect, useRef, useState } from "react";
24
+ function parseDataTw(dataTw) {
25
+ if (!dataTw) return { name: "Unknown", classes: [] };
26
+ const colonIdx = dataTw.indexOf(":");
27
+ if (colonIdx === -1) return { name: dataTw, classes: [] };
28
+ const name = dataTw.slice(0, colonIdx);
29
+ const classes = dataTw.slice(colonIdx + 1).split(/\s+/).filter(Boolean);
30
+ return { name, classes };
31
+ }
32
+ function parseVariantAttr(v) {
33
+ if (!v) return {};
34
+ try {
35
+ return JSON.parse(v);
36
+ } catch (e) {
37
+ return {};
38
+ }
39
+ }
40
+ function findNearestTwElement(el) {
41
+ var _a;
42
+ let cur = el;
43
+ while (cur) {
44
+ if ((_a = cur.dataset) == null ? void 0 : _a.tw) return cur;
45
+ cur = cur.parentElement;
46
+ }
47
+ return null;
48
+ }
49
+ function getAtomicMap(classes) {
50
+ const registry = window.__TW_REGISTRY__;
51
+ if (!registry) return {};
52
+ const map = {};
53
+ for (const cls of classes) {
54
+ if (registry[cls]) map[cls] = registry[cls];
55
+ }
56
+ return map;
57
+ }
58
+ function getActiveStates(el) {
59
+ const states = {};
60
+ for (const attr of el.attributes) {
61
+ if (attr.name.startsWith("data-")) {
62
+ states[attr.name.replace("data-", "")] = attr.value;
63
+ }
64
+ }
65
+ return states;
66
+ }
67
+ function getStateNames(el) {
68
+ const registry = window.__TW_STATE_REGISTRY__;
69
+ if (!registry) return [];
70
+ for (const [id, entry] of registry) {
71
+ if (el.classList.contains(id)) return entry.states;
72
+ }
73
+ return [];
74
+ }
75
+ function getContainerBps(el) {
76
+ const registry = window.__TW_CONTAINER_REGISTRY__;
77
+ if (!registry) return [];
78
+ for (const [id, entry] of registry) {
79
+ if (el.classList.contains(id)) {
80
+ return entry.breakpoints.map((bp) => bp.minWidth);
81
+ }
82
+ }
83
+ return [];
84
+ }
85
+ function InspectorPanel({
86
+ inspected,
87
+ position: _position,
88
+ pinned: _pinned
89
+ }) {
90
+ if (!inspected) {
91
+ return React.createElement(
92
+ "div",
93
+ { style: S.emptyPanel },
94
+ React.createElement("span", { style: { opacity: 0.4 } }, "Hover an element to inspect")
95
+ );
96
+ }
97
+ return React.createElement(
98
+ "div",
99
+ { style: S.scrollArea },
100
+ // Variant props
101
+ Object.keys(inspected.variantProps).length > 0 && React.createElement(
102
+ "div",
103
+ { style: S.section },
104
+ React.createElement("div", { style: S.sectionTitle }, "Variants"),
105
+ Object.entries(inspected.variantProps).map(
106
+ ([k, v]) => React.createElement(
107
+ "div",
108
+ { key: k, style: S.row },
109
+ React.createElement("span", { style: S.varKey }, k),
110
+ React.createElement("span", { style: S.varValue }, `"${v}"`)
111
+ )
112
+ )
113
+ ),
114
+ // Active states
115
+ Object.keys(inspected.activeStates).length > 0 && React.createElement(
116
+ "div",
117
+ { style: S.section },
118
+ React.createElement("div", { style: S.sectionTitle }, "Active Data Attrs"),
119
+ Object.entries(inspected.activeStates).map(
120
+ ([k, v]) => React.createElement(
121
+ "div",
122
+ { key: k, style: S.row },
123
+ React.createElement("code", { style: __spreadProps(__spreadValues({}, S.varKey), { color: "#f59e0b" }) }, `data-${k}`),
124
+ React.createElement("span", { style: __spreadProps(__spreadValues({}, S.varValue), { color: "#34d399" }) }, `"${v}"`)
125
+ )
126
+ )
127
+ ),
128
+ // State names
129
+ inspected.stateNames.length > 0 && React.createElement(
130
+ "div",
131
+ { style: S.section },
132
+ React.createElement("div", { style: S.sectionTitle }, "Reactive States"),
133
+ React.createElement(
134
+ "div",
135
+ { style: S.classGrid },
136
+ inspected.stateNames.map(
137
+ (s) => React.createElement(
138
+ "code",
139
+ {
140
+ key: s,
141
+ style: __spreadProps(__spreadValues({}, S.classChip), {
142
+ background: inspected.activeStates[s] === "true" ? "#065f46" : "#18181b",
143
+ borderColor: inspected.activeStates[s] === "true" ? "#34d399" : "#27272a"
144
+ })
145
+ },
146
+ s
147
+ )
148
+ )
149
+ )
150
+ ),
151
+ // Container breakpoints
152
+ inspected.containerBps.length > 0 && React.createElement(
153
+ "div",
154
+ { style: S.section },
155
+ React.createElement("div", { style: S.sectionTitle }, "Container Breakpoints"),
156
+ React.createElement(
157
+ "div",
158
+ { style: S.classGrid },
159
+ inspected.containerBps.map(
160
+ (bp) => React.createElement(
161
+ "code",
162
+ { key: bp, style: __spreadProps(__spreadValues({}, S.classChip), { color: "#818cf8" }) },
163
+ bp
164
+ )
165
+ )
166
+ )
167
+ ),
168
+ // Tailwind classes
169
+ inspected.twClasses.length > 0 && React.createElement(
170
+ "div",
171
+ { style: S.section },
172
+ React.createElement("div", { style: S.sectionTitle }, "Classes"),
173
+ React.createElement(
174
+ "div",
175
+ { style: S.classGrid },
176
+ inspected.twClasses.map(
177
+ (cls) => React.createElement(
178
+ "code",
179
+ { key: cls, style: S.classChip, title: inspected.atomicMap[cls] },
180
+ cls
181
+ )
182
+ )
183
+ )
184
+ ),
185
+ // Copy
186
+ React.createElement(
187
+ "button",
188
+ {
189
+ style: S.copyBtn,
190
+ onClick: () => {
191
+ var _a;
192
+ (_a = navigator.clipboard) == null ? void 0 : _a.writeText(
193
+ JSON.stringify(
194
+ {
195
+ component: inspected.componentName,
196
+ variants: inspected.variantProps,
197
+ states: inspected.activeStates,
198
+ classes: inspected.twClasses
199
+ },
200
+ null,
201
+ 2
202
+ )
203
+ );
204
+ }
205
+ },
206
+ "Copy to clipboard"
207
+ )
208
+ );
209
+ }
210
+ function StatePanel() {
211
+ const [entries, setEntries] = useState([]);
212
+ useEffect(() => {
213
+ const refresh = () => {
214
+ const reg = window.__TW_STATE_REGISTRY__;
215
+ setEntries(reg ? Array.from(reg.values()) : []);
216
+ };
217
+ refresh();
218
+ const interval = setInterval(refresh, 1e3);
219
+ return () => clearInterval(interval);
220
+ }, []);
221
+ if (entries.length === 0) {
222
+ return React.createElement(
223
+ "div",
224
+ { style: S.emptyPanel },
225
+ React.createElement(
226
+ "span",
227
+ { style: { opacity: 0.4 } },
228
+ "No state-enabled components found."
229
+ ),
230
+ React.createElement("br", null),
231
+ React.createElement(
232
+ "span",
233
+ { style: { opacity: 0.3, fontSize: "11px" } },
234
+ 'Use tw.button({ state: { active: "..." } }) to register.'
235
+ )
236
+ );
237
+ }
238
+ return React.createElement(
239
+ "div",
240
+ { style: S.scrollArea },
241
+ entries.map(
242
+ (entry) => React.createElement(
243
+ "div",
244
+ { key: entry.id, style: S.section },
245
+ React.createElement(
246
+ "div",
247
+ { style: S.sectionTitle },
248
+ React.createElement("span", { style: { color: "#60a5fa" } }, entry.tag.toUpperCase()),
249
+ React.createElement(
250
+ "span",
251
+ { style: { marginLeft: "8px", color: "#52525b", fontSize: "10px" } },
252
+ entry.id
253
+ )
254
+ ),
255
+ React.createElement(
256
+ "div",
257
+ { style: S.classGrid },
258
+ entry.states.map(
259
+ (s) => React.createElement(
260
+ "code",
261
+ { key: s, style: __spreadProps(__spreadValues({}, S.classChip), { color: "#f59e0b" }) },
262
+ `data-${s}`
263
+ )
264
+ )
265
+ ),
266
+ React.createElement(
267
+ "div",
268
+ { style: __spreadProps(__spreadValues({}, S.row), { marginTop: "4px" }) },
269
+ React.createElement(
270
+ "span",
271
+ {
272
+ style: __spreadProps(__spreadValues({}, S.sectionTitle), {
273
+ marginBottom: 0,
274
+ color: entry.cssInjected ? "#34d399" : "#ef4444"
275
+ })
276
+ },
277
+ entry.cssInjected ? "\u25CF CSS injected" : "\u25CB CSS pending"
278
+ )
279
+ )
280
+ )
281
+ )
282
+ );
283
+ }
284
+ function ContainerPanel() {
285
+ const [entries, setEntries] = useState([]);
286
+ useEffect(() => {
287
+ const refresh = () => {
288
+ const reg = window.__TW_CONTAINER_REGISTRY__;
289
+ setEntries(reg ? Array.from(reg.values()) : []);
290
+ };
291
+ refresh();
292
+ const interval = setInterval(refresh, 1e3);
293
+ return () => clearInterval(interval);
294
+ }, []);
295
+ if (entries.length === 0) {
296
+ return React.createElement(
297
+ "div",
298
+ { style: S.emptyPanel },
299
+ React.createElement(
300
+ "span",
301
+ { style: { opacity: 0.4 } },
302
+ "No container query components found."
303
+ ),
304
+ React.createElement("br", null),
305
+ React.createElement(
306
+ "span",
307
+ { style: { opacity: 0.3, fontSize: "11px" } },
308
+ 'Use tw.div({ container: { md: "flex-row" } }) to register.'
309
+ )
310
+ );
311
+ }
312
+ return React.createElement(
313
+ "div",
314
+ { style: S.scrollArea },
315
+ entries.map(
316
+ (entry) => React.createElement(
317
+ "div",
318
+ { key: entry.id, style: S.section },
319
+ React.createElement(
320
+ "div",
321
+ { style: S.sectionTitle },
322
+ React.createElement("span", { style: { color: "#60a5fa" } }, entry.tag.toUpperCase()),
323
+ entry.containerName && React.createElement(
324
+ "span",
325
+ { style: { marginLeft: "6px", color: "#818cf8" } },
326
+ `[${entry.containerName}]`
327
+ ),
328
+ React.createElement(
329
+ "span",
330
+ { style: { marginLeft: "8px", color: "#52525b", fontSize: "10px" } },
331
+ entry.id
332
+ )
333
+ ),
334
+ React.createElement(
335
+ "div",
336
+ null,
337
+ entry.breakpoints.map(
338
+ (bp, i) => React.createElement(
339
+ "div",
340
+ { key: i, style: __spreadProps(__spreadValues({}, S.row), { marginBottom: "2px" }) },
341
+ React.createElement(
342
+ "code",
343
+ { style: { color: "#818cf8", fontSize: "11px" } },
344
+ `\u2265 ${bp.minWidth}`
345
+ ),
346
+ React.createElement(
347
+ "span",
348
+ { style: { color: "#6b7280", fontSize: "11px" } },
349
+ bp.classes
350
+ )
351
+ )
352
+ )
353
+ )
354
+ )
355
+ )
356
+ );
357
+ }
358
+ function TokensPanel() {
359
+ const [tokens, setTokens_] = useState({});
360
+ useEffect(() => {
361
+ const engine = window.__TW_TOKEN_ENGINE__;
362
+ if (!engine) return;
363
+ setTokens_(engine.getTokens());
364
+ const unsub = engine.subscribe((t) => setTokens_(__spreadValues({}, t)));
365
+ return unsub;
366
+ }, []);
367
+ const entries = Object.entries(tokens);
368
+ if (entries.length === 0) {
369
+ return React.createElement(
370
+ "div",
371
+ { style: S.emptyPanel },
372
+ React.createElement("span", { style: { opacity: 0.4 } }, "No live tokens registered."),
373
+ React.createElement("br", null),
374
+ React.createElement(
375
+ "span",
376
+ { style: { opacity: 0.3, fontSize: "11px" } },
377
+ 'Use liveToken({ primary: "#3b82f6" }) to register tokens.'
378
+ )
379
+ );
380
+ }
381
+ return React.createElement(
382
+ "div",
383
+ { style: S.scrollArea },
384
+ React.createElement(
385
+ "div",
386
+ { style: __spreadProps(__spreadValues({}, S.sectionTitle), { padding: "8px 12px 4px", color: "#52525b" }) },
387
+ "Click color to edit \xB7 Changes apply instantly"
388
+ ),
389
+ entries.map(([name, value]) => {
390
+ const isColor = value.startsWith("#") || value.startsWith("rgb") || value.startsWith("hsl");
391
+ return React.createElement(
392
+ "div",
393
+ { key: name, style: __spreadProps(__spreadValues({}, S.row), { padding: "6px 12px", borderBottom: "1px solid #18181b" }) },
394
+ React.createElement(
395
+ "div",
396
+ { style: { display: "flex", alignItems: "center", gap: "8px" } },
397
+ isColor && React.createElement("div", {
398
+ style: {
399
+ width: "16px",
400
+ height: "16px",
401
+ borderRadius: "3px",
402
+ background: value,
403
+ border: "1px solid #27272a",
404
+ flexShrink: 0
405
+ }
406
+ }),
407
+ React.createElement("span", { style: { color: "#a1a1aa", fontSize: "12px" } }, name)
408
+ ),
409
+ isColor ? React.createElement("input", {
410
+ type: "color",
411
+ defaultValue: value.startsWith("#") ? value : "#000000",
412
+ style: {
413
+ width: "52px",
414
+ height: "22px",
415
+ border: "none",
416
+ background: "none",
417
+ cursor: "pointer"
418
+ },
419
+ onChange: (e) => {
420
+ const engine = window.__TW_TOKEN_ENGINE__;
421
+ if (engine) engine.setToken(name, e.target.value);
422
+ }
423
+ }) : React.createElement("input", {
424
+ type: "text",
425
+ defaultValue: value,
426
+ style: {
427
+ background: "#18181b",
428
+ border: "1px solid #27272a",
429
+ borderRadius: "3px",
430
+ color: "#e4e4e7",
431
+ fontSize: "11px",
432
+ padding: "2px 6px",
433
+ width: "100px",
434
+ fontFamily: "monospace"
435
+ },
436
+ onBlur: (e) => {
437
+ const engine = window.__TW_TOKEN_ENGINE__;
438
+ if (engine) engine.setToken(name, e.target.value);
439
+ }
440
+ })
441
+ );
442
+ })
443
+ );
444
+ }
445
+ function AnalyzerPanel() {
446
+ const [scanning, setScanning] = useState(false);
447
+ const [results, setResults] = useState(null);
448
+ const runScan = useCallback(() => {
449
+ setScanning(true);
450
+ setTimeout(() => {
451
+ var _a, _b, _c, _d;
452
+ const twEls = document.querySelectorAll("[data-tw]");
453
+ const classMap = /* @__PURE__ */ new Map();
454
+ for (const el of twEls) {
455
+ const { name, classes } = (() => {
456
+ var _a2;
457
+ const dTw = (_a2 = el.dataset.tw) != null ? _a2 : null;
458
+ if (!dTw) return { name: "?", classes: [] };
459
+ const ci = dTw.indexOf(":");
460
+ return {
461
+ name: ci >= 0 ? dTw.slice(0, ci) : dTw,
462
+ classes: ci >= 0 ? dTw.slice(ci + 1).split(/\s+/).filter(Boolean) : []
463
+ };
464
+ })();
465
+ const key = classes.sort().join(" ");
466
+ if (!classMap.has(key)) classMap.set(key, []);
467
+ classMap.get(key).push(name);
468
+ }
469
+ const duplicates = Array.from(classMap.entries()).filter(([, names]) => names.length > 1).map(([pattern, names]) => ({ pattern, count: names.length, names })).sort((a, b) => b.count - a.count).slice(0, 10);
470
+ const stateReg = window.__TW_STATE_REGISTRY__;
471
+ const containerReg = window.__TW_CONTAINER_REGISTRY__;
472
+ const tokenEngine = window.__TW_TOKEN_ENGINE__;
473
+ setResults({
474
+ duplicates,
475
+ stateCount: (_a = stateReg == null ? void 0 : stateReg.size) != null ? _a : 0,
476
+ containerCount: (_b = containerReg == null ? void 0 : containerReg.size) != null ? _b : 0,
477
+ tokenCount: Object.keys((_d = (_c = tokenEngine == null ? void 0 : tokenEngine.getTokens) == null ? void 0 : _c.call(tokenEngine)) != null ? _d : {}).length
478
+ });
479
+ setScanning(false);
480
+ }, 100);
481
+ }, []);
482
+ return React.createElement(
483
+ "div",
484
+ { style: S.scrollArea },
485
+ React.createElement(
486
+ "div",
487
+ { style: { padding: "10px 12px" } },
488
+ React.createElement(
489
+ "button",
490
+ {
491
+ style: __spreadProps(__spreadValues({}, S.copyBtn), { borderTop: "none", color: "#60a5fa", fontWeight: "600" }),
492
+ onClick: runScan,
493
+ disabled: scanning
494
+ },
495
+ scanning ? "Scanning DOM..." : "\u25B6 Run DOM Scan"
496
+ ),
497
+ results && React.createElement(
498
+ "div",
499
+ null,
500
+ // Summary
501
+ React.createElement(
502
+ "div",
503
+ { style: S.section },
504
+ React.createElement("div", { style: S.sectionTitle }, "Summary"),
505
+ React.createElement(
506
+ "div",
507
+ { style: S.row },
508
+ React.createElement("span", { style: S.varKey }, "State components"),
509
+ React.createElement("span", { style: S.varValue }, String(results.stateCount))
510
+ ),
511
+ React.createElement(
512
+ "div",
513
+ { style: S.row },
514
+ React.createElement("span", { style: S.varKey }, "Container components"),
515
+ React.createElement("span", { style: S.varValue }, String(results.containerCount))
516
+ ),
517
+ React.createElement(
518
+ "div",
519
+ { style: S.row },
520
+ React.createElement("span", { style: S.varKey }, "Live tokens"),
521
+ React.createElement("span", { style: S.varValue }, String(results.tokenCount))
522
+ )
523
+ ),
524
+ // Duplicates
525
+ results.duplicates.length > 0 ? React.createElement(
526
+ "div",
527
+ { style: S.section },
528
+ React.createElement("div", { style: S.sectionTitle }, "Duplicate Class Sets"),
529
+ results.duplicates.map(
530
+ (d, i) => React.createElement(
531
+ "div",
532
+ { key: i, style: { marginBottom: "8px" } },
533
+ React.createElement(
534
+ "div",
535
+ { style: { color: "#f59e0b", fontSize: "11px", marginBottom: "2px" } },
536
+ d.names.join(", ")
537
+ ),
538
+ React.createElement(
539
+ "code",
540
+ {
541
+ style: {
542
+ color: "#52525b",
543
+ fontSize: "10px",
544
+ wordBreak: "break-all"
545
+ }
546
+ },
547
+ d.pattern.split(" ").slice(0, 8).join(" ") + (d.pattern.split(" ").length > 8 ? "..." : "")
548
+ )
549
+ )
550
+ )
551
+ ) : results && React.createElement(
552
+ "div",
553
+ { style: S.section },
554
+ React.createElement(
555
+ "span",
556
+ { style: { color: "#34d399", fontSize: "12px" } },
557
+ "\u2713 No duplicate class sets in current DOM"
558
+ )
559
+ )
560
+ )
561
+ )
562
+ );
563
+ }
564
+ function TwDevTools() {
565
+ if (process.env.NODE_ENV === "production") return null;
566
+ const [state, setState] = useState({
567
+ open: false,
568
+ panel: "inspector",
569
+ pinned: false,
570
+ inspected: null,
571
+ position: { x: 0, y: 0 }
572
+ });
573
+ const overlayRef = useRef(null);
574
+ const isInspecting = state.open && state.panel === "inspector";
575
+ useEffect(() => {
576
+ const onKey = (e) => {
577
+ if (e.ctrlKey && e.shiftKey && e.key === "D") {
578
+ e.preventDefault();
579
+ setState((s) => __spreadProps(__spreadValues({}, s), { open: !s.open, inspected: null }));
580
+ }
581
+ if (e.key === "Escape")
582
+ setState((s) => __spreadProps(__spreadValues({}, s), { open: false, pinned: false, inspected: null }));
583
+ if (e.key === "1") setState((s) => s.open ? __spreadProps(__spreadValues({}, s), { panel: "inspector" }) : s);
584
+ if (e.key === "2") setState((s) => s.open ? __spreadProps(__spreadValues({}, s), { panel: "state" }) : s);
585
+ if (e.key === "3") setState((s) => s.open ? __spreadProps(__spreadValues({}, s), { panel: "container" }) : s);
586
+ if (e.key === "4") setState((s) => s.open ? __spreadProps(__spreadValues({}, s), { panel: "tokens" }) : s);
587
+ if (e.key === "5") setState((s) => s.open ? __spreadProps(__spreadValues({}, s), { panel: "analyzer" }) : s);
588
+ };
589
+ window.addEventListener("keydown", onKey);
590
+ return () => window.removeEventListener("keydown", onKey);
591
+ }, []);
592
+ const onMouseMove = useCallback(
593
+ (e) => {
594
+ var _a;
595
+ if (!isInspecting || state.pinned) return;
596
+ const twEl = findNearestTwElement(e.target);
597
+ if (!twEl) {
598
+ setState((s) => __spreadProps(__spreadValues({}, s), { inspected: null, position: { x: e.clientX, y: e.clientY } }));
599
+ return;
600
+ }
601
+ const { name, classes } = parseDataTw((_a = twEl.dataset.tw) != null ? _a : null);
602
+ setState((s) => {
603
+ var _a2;
604
+ return __spreadProps(__spreadValues({}, s), {
605
+ position: { x: e.clientX, y: e.clientY },
606
+ inspected: {
607
+ componentName: name,
608
+ element: twEl,
609
+ rect: twEl.getBoundingClientRect(),
610
+ twClasses: classes,
611
+ variantProps: parseVariantAttr((_a2 = twEl.dataset.twVariants) != null ? _a2 : null),
612
+ atomicMap: getAtomicMap(classes),
613
+ rawClassName: twEl.className,
614
+ stateNames: getStateNames(twEl),
615
+ activeStates: getActiveStates(twEl),
616
+ containerBps: getContainerBps(twEl)
617
+ }
618
+ });
619
+ });
620
+ },
621
+ [isInspecting, state.pinned]
622
+ );
623
+ const onClick = useCallback(
624
+ (e) => {
625
+ var _a;
626
+ if (!isInspecting) return;
627
+ if ((_a = overlayRef.current) == null ? void 0 : _a.contains(e.target)) return;
628
+ setState((s) => __spreadProps(__spreadValues({}, s), { pinned: !s.pinned && !!s.inspected }));
629
+ },
630
+ [isInspecting]
631
+ );
632
+ useEffect(() => {
633
+ if (!isInspecting) return;
634
+ window.addEventListener("mousemove", onMouseMove);
635
+ window.addEventListener("click", onClick);
636
+ return () => {
637
+ window.removeEventListener("mousemove", onMouseMove);
638
+ window.removeEventListener("click", onClick);
639
+ };
640
+ }, [isInspecting, onMouseMove, onClick]);
641
+ if (!state.open) {
642
+ return React.createElement(
643
+ "button",
644
+ {
645
+ onClick: () => setState((s) => __spreadProps(__spreadValues({}, s), { open: true })),
646
+ style: S.toggleBtn,
647
+ title: "tailwind-styled-v4 DevTools (Ctrl+Shift+D)"
648
+ },
649
+ "\u{1F3A8}"
650
+ );
651
+ }
652
+ const PANELS = [
653
+ { id: "inspector", label: "Inspector", icon: "\u{1F50D}" },
654
+ { id: "state", label: "State", icon: "\u26A1" },
655
+ { id: "container", label: "Container", icon: "\u{1F4E6}" },
656
+ { id: "tokens", label: "Tokens", icon: "\u{1F3A8}" },
657
+ { id: "analyzer", label: "Analyzer", icon: "\u{1F4CA}" }
658
+ ];
659
+ return React.createElement(
660
+ "div",
661
+ { style: S.root },
662
+ // ── Element highlight (inspector only) ──────────────────────────────
663
+ isInspecting && state.inspected && React.createElement("div", {
664
+ style: __spreadProps(__spreadValues({}, S.highlight), {
665
+ top: state.inspected.rect.top + window.scrollY,
666
+ left: state.inspected.rect.left + window.scrollX,
667
+ width: state.inspected.rect.width,
668
+ height: state.inspected.rect.height
669
+ })
670
+ }),
671
+ // ── Component name label ────────────────────────────────────────────
672
+ isInspecting && state.inspected && React.createElement(
673
+ "div",
674
+ {
675
+ style: {
676
+ position: "absolute",
677
+ top: state.inspected.rect.top + window.scrollY - 22,
678
+ left: state.inspected.rect.left + window.scrollX,
679
+ background: "#1e3a5f",
680
+ color: "#93c5fd",
681
+ fontSize: "11px",
682
+ padding: "2px 6px",
683
+ borderRadius: "3px 3px 0 0",
684
+ pointerEvents: "none",
685
+ zIndex: 2147483646,
686
+ fontFamily: "monospace"
687
+ }
688
+ },
689
+ state.inspected.componentName
690
+ ),
691
+ // ── Main DevTools panel ─────────────────────────────────────────────
692
+ React.createElement(
693
+ "div",
694
+ {
695
+ ref: overlayRef,
696
+ style: state.panel === "inspector" && state.inspected ? __spreadProps(__spreadValues({}, S.panel), {
697
+ top: Math.min(state.position.y + 16, window.innerHeight - 460),
698
+ left: Math.min(state.position.x + 16, window.innerWidth - 320)
699
+ }) : __spreadProps(__spreadValues({}, S.panel), {
700
+ top: "auto",
701
+ bottom: "40px",
702
+ right: "12px",
703
+ left: "auto"
704
+ })
705
+ },
706
+ // Header
707
+ React.createElement(
708
+ "div",
709
+ { style: S.header },
710
+ React.createElement(
711
+ "span",
712
+ { style: S.componentName },
713
+ state.inspected && state.panel === "inspector" ? state.inspected.componentName : "tailwind-styled-v4"
714
+ ),
715
+ React.createElement(
716
+ "div",
717
+ { style: S.headerActions },
718
+ state.pinned && React.createElement("span", { style: S.pinBadge }, "\u{1F4CC}"),
719
+ React.createElement(
720
+ "button",
721
+ {
722
+ style: S.closeBtn,
723
+ onClick: () => setState((s) => __spreadProps(__spreadValues({}, s), { open: false, pinned: false, inspected: null }))
724
+ },
725
+ "\u2715"
726
+ )
727
+ )
728
+ ),
729
+ // Tab bar
730
+ React.createElement(
731
+ "div",
732
+ { style: S.tabBar },
733
+ PANELS.map(
734
+ (p) => React.createElement(
735
+ "button",
736
+ {
737
+ key: p.id,
738
+ style: __spreadProps(__spreadValues({}, S.tab), {
739
+ background: state.panel === p.id ? "#18181b" : "none",
740
+ color: state.panel === p.id ? "#e4e4e7" : "#52525b",
741
+ borderBottom: state.panel === p.id ? "2px solid #3b82f6" : "2px solid transparent"
742
+ }),
743
+ onClick: () => setState((s) => __spreadProps(__spreadValues({}, s), { panel: p.id })),
744
+ title: `${p.label} (${PANELS.findIndex((x) => x.id === p.id) + 1})`
745
+ },
746
+ `${p.icon} ${p.label}`
747
+ )
748
+ )
749
+ ),
750
+ // Panel content
751
+ state.panel === "inspector" && React.createElement(InspectorPanel, {
752
+ inspected: state.inspected,
753
+ position: state.position,
754
+ pinned: state.pinned
755
+ }),
756
+ state.panel === "state" && React.createElement(StatePanel, null),
757
+ state.panel === "container" && React.createElement(ContainerPanel, null),
758
+ state.panel === "tokens" && React.createElement(TokensPanel, null),
759
+ state.panel === "analyzer" && React.createElement(AnalyzerPanel, null)
760
+ ),
761
+ // ── Status bar ──────────────────────────────────────────────────────
762
+ React.createElement(
763
+ "div",
764
+ { style: S.statusBar },
765
+ React.createElement("span", null, "\u{1F3A8} tailwind-styled-v4 DevTools"),
766
+ React.createElement(
767
+ "span",
768
+ { style: { opacity: 0.6, fontSize: "10px" } },
769
+ state.pinned ? "Click to unpin" : isInspecting ? "Hover to inspect \xB7 Click to pin \xB7 1-5 switch panel \xB7 Esc close" : "Ctrl+Shift+D close \xB7 1-5 switch panel"
770
+ )
771
+ )
772
+ );
773
+ }
774
+ var S = {
775
+ root: {
776
+ position: "fixed",
777
+ inset: 0,
778
+ zIndex: 2147483647,
779
+ pointerEvents: "none",
780
+ fontFamily: "ui-monospace, 'JetBrains Mono', monospace",
781
+ fontSize: "12px"
782
+ },
783
+ highlight: {
784
+ position: "absolute",
785
+ borderRadius: "3px",
786
+ outline: "2px solid #3b82f6",
787
+ outlineOffset: "1px",
788
+ background: "rgba(59,130,246,0.08)",
789
+ pointerEvents: "none",
790
+ transition: "all 0.1s ease",
791
+ zIndex: 2147483646
792
+ },
793
+ panel: {
794
+ position: "fixed",
795
+ width: 320,
796
+ maxHeight: 480,
797
+ background: "#0f0f0f",
798
+ border: "1px solid #27272a",
799
+ borderRadius: "10px",
800
+ boxShadow: "0 8px 32px rgba(0,0,0,0.8)",
801
+ pointerEvents: "all",
802
+ zIndex: 2147483647,
803
+ userSelect: "text",
804
+ display: "flex",
805
+ flexDirection: "column"
806
+ },
807
+ header: {
808
+ display: "flex",
809
+ alignItems: "center",
810
+ justifyContent: "space-between",
811
+ padding: "8px 12px 6px",
812
+ borderBottom: "1px solid #1f1f1f",
813
+ flexShrink: 0
814
+ },
815
+ tabBar: {
816
+ display: "flex",
817
+ borderBottom: "1px solid #18181b",
818
+ flexShrink: 0,
819
+ overflowX: "auto"
820
+ },
821
+ tab: {
822
+ background: "none",
823
+ border: "none",
824
+ cursor: "pointer",
825
+ fontSize: "10px",
826
+ padding: "5px 8px",
827
+ whiteSpace: "nowrap",
828
+ fontFamily: "inherit",
829
+ transition: "color 0.1s",
830
+ pointerEvents: "all"
831
+ },
832
+ scrollArea: {
833
+ overflowY: "auto",
834
+ flex: 1
835
+ },
836
+ emptyPanel: {
837
+ padding: "24px 12px",
838
+ color: "#71717a",
839
+ fontSize: "12px",
840
+ textAlign: "center",
841
+ lineHeight: 1.6
842
+ },
843
+ componentName: {
844
+ color: "#60a5fa",
845
+ fontWeight: "bold",
846
+ fontSize: "13px"
847
+ },
848
+ headerActions: {
849
+ display: "flex",
850
+ alignItems: "center",
851
+ gap: "6px"
852
+ },
853
+ pinBadge: { color: "#fbbf24", fontSize: "10px" },
854
+ closeBtn: {
855
+ background: "none",
856
+ border: "none",
857
+ color: "#71717a",
858
+ cursor: "pointer",
859
+ fontSize: "14px",
860
+ lineHeight: 1,
861
+ padding: "2px 4px",
862
+ pointerEvents: "all"
863
+ },
864
+ section: {
865
+ padding: "8px 12px",
866
+ borderBottom: "1px solid #18181b"
867
+ },
868
+ sectionTitle: {
869
+ color: "#52525b",
870
+ fontSize: "10px",
871
+ textTransform: "uppercase",
872
+ letterSpacing: "0.08em",
873
+ marginBottom: "6px"
874
+ },
875
+ row: {
876
+ display: "flex",
877
+ justifyContent: "space-between",
878
+ alignItems: "center",
879
+ marginBottom: "3px"
880
+ },
881
+ varKey: { color: "#a1a1aa" },
882
+ varValue: { color: "#34d399", fontWeight: "bold" },
883
+ classGrid: {
884
+ display: "flex",
885
+ flexWrap: "wrap",
886
+ gap: "4px"
887
+ },
888
+ classChip: {
889
+ background: "#18181b",
890
+ border: "1px solid #27272a",
891
+ borderRadius: "4px",
892
+ padding: "2px 6px",
893
+ color: "#e4e4e7",
894
+ cursor: "default",
895
+ fontSize: "11px"
896
+ },
897
+ copyBtn: {
898
+ display: "block",
899
+ width: "100%",
900
+ background: "none",
901
+ border: "none",
902
+ borderTop: "1px solid #18181b",
903
+ color: "#52525b",
904
+ cursor: "pointer",
905
+ padding: "8px 12px",
906
+ textAlign: "left",
907
+ fontSize: "11px",
908
+ pointerEvents: "all",
909
+ fontFamily: "inherit"
910
+ },
911
+ statusBar: {
912
+ position: "fixed",
913
+ bottom: 0,
914
+ left: 0,
915
+ right: 0,
916
+ background: "#1e3a5f",
917
+ color: "#93c5fd",
918
+ fontSize: "11px",
919
+ padding: "4px 12px",
920
+ display: "flex",
921
+ justifyContent: "space-between",
922
+ alignItems: "center",
923
+ pointerEvents: "all",
924
+ zIndex: 2147483647
925
+ },
926
+ toggleBtn: {
927
+ position: "fixed",
928
+ bottom: 12,
929
+ right: 12,
930
+ width: 36,
931
+ height: 36,
932
+ borderRadius: "50%",
933
+ background: "#1e3a5f",
934
+ border: "1px solid #3b82f6",
935
+ color: "white",
936
+ cursor: "pointer",
937
+ fontSize: "16px",
938
+ display: "flex",
939
+ alignItems: "center",
940
+ justifyContent: "center",
941
+ zIndex: 2147483647,
942
+ boxShadow: "0 2px 8px rgba(0,0,0,0.4)",
943
+ pointerEvents: "all"
944
+ }
945
+ };
946
+ function DevToolsProvider() {
947
+ if (process.env.NODE_ENV === "production") return null;
948
+ return React.createElement(TwDevTools);
949
+ }
950
+ export {
951
+ DevToolsProvider,
952
+ TwDevTools
953
+ };