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