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