@lytjs/devtools 6.4.0 → 6.6.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.mjs CHANGED
@@ -32,7 +32,10 @@ function buildComponentTreeRecursive(component, parentId) {
32
32
  if (component.children && Array.isArray(component.children)) {
33
33
  const children = [];
34
34
  for (const child of component.children) {
35
- const childNode = buildComponentTreeRecursive(child, node.id);
35
+ const childNode = buildComponentTreeRecursive(
36
+ child,
37
+ node.id
38
+ );
36
39
  if (childNode) {
37
40
  children.push(childNode);
38
41
  }
@@ -130,16 +133,14 @@ function setStoreState(storeId, path, value) {
130
133
  const store = storeRegistry.get(storeId);
131
134
  if (!store) return false;
132
135
  const keys = path.split(".");
133
- let current = store.$state || store;
136
+ let current = store;
134
137
  for (let i = 0; i < keys.length - 1; i++) {
135
138
  const key = keys[i];
136
- if (!isObject(current[key])) {
137
- return false;
138
- }
139
+ if (!isObject(current)) return false;
139
140
  current = current[key];
140
141
  }
141
142
  const lastKey = keys[keys.length - 1];
142
- if (lastKey) {
143
+ if (lastKey && isObject(current)) {
143
144
  current[lastKey] = value;
144
145
  }
145
146
  return true;
@@ -182,7 +183,7 @@ function subscribeStore(storeId) {
182
183
  unsubscribeStore(storeId);
183
184
  }
184
185
  if (isFunction(store.$subscribe)) {
185
- const unsubscribe = store.$subscribe((_mutation, state) => {
186
+ const unsubscribe = store.$subscribe?.((_mutation, state) => {
186
187
  const currentState = isObject(state) ? deepClone(state) : {};
187
188
  globalChangeCallbacks.forEach((cb) => cb(storeId, currentState));
188
189
  });
@@ -249,7 +250,7 @@ function watchRouteChanges() {
249
250
  if (afterEachHandler !== null) {
250
251
  unwatchRouteChanges();
251
252
  }
252
- routerInstance.afterEach((to) => {
253
+ routerInstance.afterEach?.((to) => {
253
254
  const routeInfo = {
254
255
  path: to.path || "/",
255
256
  name: to.name || null,
@@ -283,7 +284,8 @@ function navigateToName(name, params) {
283
284
  if (!routerInstance) {
284
285
  return Promise.reject(new Error("Router not registered"));
285
286
  }
286
- return routerInstance.push?.({ name, params }) || Promise.resolve();
287
+ const path = `/${name}${params ? "?" + new URLSearchParams(params).toString() : ""}`;
288
+ return routerInstance.push?.(path) || Promise.resolve();
287
289
  }
288
290
  function goBack() {
289
291
  if (!routerInstance) {
@@ -329,6 +331,14 @@ function clearRouteHistory() {
329
331
  }
330
332
 
331
333
  // src/signalsInspector.ts
334
+ var DEFAULT_LAYOUT_OPTIONS = {
335
+ nodeWidth: 120,
336
+ nodeHeight: 40,
337
+ horizontalSpacing: 60,
338
+ verticalSpacing: 80,
339
+ centerX: 300,
340
+ centerY: 200
341
+ };
332
342
  var signalRegistry = /* @__PURE__ */ new Map();
333
343
  var snapshotManager = {
334
344
  snapshots: [],
@@ -484,6 +494,9 @@ function createSnapshot(label) {
484
494
  function getSnapshots() {
485
495
  return snapshotManager.getAll();
486
496
  }
497
+ function getSnapshot(index) {
498
+ return snapshotManager.get(index);
499
+ }
487
500
  function getTimeTravelState() {
488
501
  const length = snapshotManager.getLength();
489
502
  return {
@@ -637,6 +650,352 @@ function serializePerformanceStats() {
637
650
  });
638
651
  return result;
639
652
  }
653
+ function calculateNodeLevels() {
654
+ const levels = /* @__PURE__ */ new Map();
655
+ const visited = /* @__PURE__ */ new Set();
656
+ function visitNode(nodeId, level) {
657
+ if (visited.has(nodeId)) {
658
+ const existingLevel = levels.get(nodeId);
659
+ if (existingLevel !== void 0 && level > existingLevel) {
660
+ levels.set(nodeId, level);
661
+ }
662
+ return;
663
+ }
664
+ visited.add(nodeId);
665
+ levels.set(nodeId, level);
666
+ const node = signalRegistry.get(nodeId);
667
+ if (node) {
668
+ node.dependents.forEach((depId) => {
669
+ visitNode(depId, level + 1);
670
+ });
671
+ }
672
+ }
673
+ signalRegistry.forEach((node, id) => {
674
+ if (node.dependencies.size === 0) {
675
+ visitNode(id, 0);
676
+ }
677
+ });
678
+ signalRegistry.forEach((_, id) => {
679
+ if (!visited.has(id)) {
680
+ visitNode(id, 0);
681
+ }
682
+ });
683
+ return levels;
684
+ }
685
+ function calculateNodeDegrees() {
686
+ const degrees = /* @__PURE__ */ new Map();
687
+ signalRegistry.forEach((node, id) => {
688
+ degrees.set(id, {
689
+ inDegree: node.dependencies.size,
690
+ outDegree: node.dependents.size
691
+ });
692
+ });
693
+ return degrees;
694
+ }
695
+ function getVisualLayoutGraph(options2) {
696
+ const opts = { ...DEFAULT_LAYOUT_OPTIONS, ...options2 };
697
+ if (signalRegistry.size === 0) {
698
+ return { nodes: [], edges: [], width: 0, height: 0 };
699
+ }
700
+ const levels = calculateNodeLevels();
701
+ const degrees = calculateNodeDegrees();
702
+ const levelNodes = /* @__PURE__ */ new Map();
703
+ levels.forEach((level, nodeId) => {
704
+ const nodes2 = levelNodes.get(level) || [];
705
+ nodes2.push(nodeId);
706
+ levelNodes.set(level, nodes2);
707
+ });
708
+ const maxLevel = Math.max(...Array.from(levels.values()), 0);
709
+ const maxNodesInLevel = Math.max(...Array.from(levelNodes.values()).map((n) => n.length), 1);
710
+ const graphWidth = (maxNodesInLevel + 1) * (opts.nodeWidth + opts.horizontalSpacing);
711
+ const graphHeight = (maxLevel + 2) * (opts.nodeHeight + opts.verticalSpacing);
712
+ const nodes = [];
713
+ const edges = [];
714
+ levelNodes.forEach((nodeIds, level) => {
715
+ const levelWidth = nodeIds.length * (opts.nodeWidth + opts.horizontalSpacing) - opts.horizontalSpacing;
716
+ const startX = (graphWidth - levelWidth) / 2;
717
+ nodeIds.forEach((nodeId, index) => {
718
+ const node = signalRegistry.get(nodeId);
719
+ if (!node) return;
720
+ const degree = degrees.get(nodeId) || { inDegree: 0, outDegree: 0 };
721
+ const x = startX + index * (opts.nodeWidth + opts.horizontalSpacing);
722
+ const y = opts.centerY - graphHeight / 2 + level * (opts.nodeHeight + opts.verticalSpacing) + opts.verticalSpacing / 2;
723
+ nodes.push({
724
+ id: nodeId,
725
+ name: node.name,
726
+ type: node.type,
727
+ x,
728
+ y,
729
+ level,
730
+ width: opts.nodeWidth,
731
+ height: opts.nodeHeight,
732
+ inDegree: degree.inDegree,
733
+ outDegree: degree.outDegree
734
+ });
735
+ node.dependencies.forEach((depId) => {
736
+ const depNode = nodes.find((n) => n.id === depId);
737
+ if (depNode) {
738
+ edges.push({
739
+ source: depId,
740
+ target: nodeId,
741
+ sourceX: depNode.x + opts.nodeWidth,
742
+ sourceY: depNode.y + opts.nodeHeight / 2,
743
+ targetX: x,
744
+ targetY: y + opts.nodeHeight / 2,
745
+ type: "dependency"
746
+ });
747
+ }
748
+ });
749
+ });
750
+ });
751
+ return { nodes, edges, width: graphWidth, height: graphHeight };
752
+ }
753
+ function getSubgraph(centerId, depth = 2) {
754
+ const includedNodes = /* @__PURE__ */ new Set([centerId]);
755
+ const queue = [{ id: centerId, currentDepth: 0 }];
756
+ while (queue.length > 0) {
757
+ const { id, currentDepth: currentDepth2 } = queue.shift();
758
+ if (currentDepth2 >= depth) continue;
759
+ const node = signalRegistry.get(id);
760
+ if (node) {
761
+ node.dependencies.forEach((depId) => {
762
+ if (!includedNodes.has(depId)) {
763
+ includedNodes.add(depId);
764
+ queue.push({ id: depId, currentDepth: currentDepth2 + 1 });
765
+ }
766
+ });
767
+ node.dependents.forEach((depId) => {
768
+ if (!includedNodes.has(depId)) {
769
+ includedNodes.add(depId);
770
+ queue.push({ id: depId, currentDepth: currentDepth2 + 1 });
771
+ }
772
+ });
773
+ }
774
+ }
775
+ const tempRegistry = new Map(signalRegistry);
776
+ signalRegistry.forEach((_, id) => {
777
+ if (!includedNodes.has(id)) {
778
+ signalRegistry.delete(id);
779
+ }
780
+ });
781
+ const subgraph = getVisualLayoutGraph();
782
+ signalRegistry.clear();
783
+ tempRegistry.forEach((value, key) => {
784
+ signalRegistry.set(key, value);
785
+ });
786
+ return subgraph;
787
+ }
788
+ function searchSignals(query) {
789
+ const lowerQuery = query.toLowerCase();
790
+ return getSignalNodes().filter(
791
+ (node) => node.name.toLowerCase().includes(lowerQuery) || node.id.toLowerCase().includes(lowerQuery) || node.type.toLowerCase().includes(lowerQuery)
792
+ );
793
+ }
794
+ function filterSignals(options2) {
795
+ return getSignalNodes().filter((node) => {
796
+ if (options2.types && !options2.types.includes(node.type)) {
797
+ return false;
798
+ }
799
+ if (options2.minUpdateCount !== void 0 && node.updateCount < options2.minUpdateCount) {
800
+ return false;
801
+ }
802
+ if (options2.hasDependencies && node.dependencies.length === 0) {
803
+ return false;
804
+ }
805
+ if (options2.hasDependents && node.dependents.length === 0) {
806
+ return false;
807
+ }
808
+ return true;
809
+ });
810
+ }
811
+ function compareSnapshots(snapshot1, snapshot2) {
812
+ const diff = {
813
+ added: [],
814
+ removed: [],
815
+ changed: []
816
+ };
817
+ const ids1 = new Set(Object.keys(snapshot1.signals));
818
+ const ids2 = new Set(Object.keys(snapshot2.signals));
819
+ ids2.forEach((id) => {
820
+ if (!ids1.has(id)) {
821
+ diff.added.push({
822
+ id,
823
+ value: snapshot2.signals[id].value
824
+ });
825
+ }
826
+ });
827
+ ids1.forEach((id) => {
828
+ if (!ids2.has(id)) {
829
+ diff.removed.push({
830
+ id,
831
+ value: snapshot1.signals[id].value
832
+ });
833
+ }
834
+ });
835
+ ids1.forEach((id) => {
836
+ if (ids2.has(id)) {
837
+ const oldValue = snapshot1.signals[id].value;
838
+ const newValue = snapshot2.signals[id].value;
839
+ if (!Object.is(oldValue, newValue)) {
840
+ diff.changed.push({ id, oldValue, newValue });
841
+ }
842
+ }
843
+ });
844
+ return diff;
845
+ }
846
+ function serializeSnapshotDiff(diff) {
847
+ let result = "";
848
+ if (diff.added.length > 0) {
849
+ result += `\u2795 \u65B0\u589E\u4FE1\u53F7 (${diff.added.length}):
850
+ `;
851
+ diff.added.forEach(({ id, value }) => {
852
+ result += ` ${id}: ${JSON.stringify(value)}
853
+ `;
854
+ });
855
+ result += "\n";
856
+ }
857
+ if (diff.removed.length > 0) {
858
+ result += `\u2796 \u79FB\u9664\u4FE1\u53F7 (${diff.removed.length}):
859
+ `;
860
+ diff.removed.forEach(({ id, value }) => {
861
+ result += ` ${id}: ${JSON.stringify(value)}
862
+ `;
863
+ });
864
+ result += "\n";
865
+ }
866
+ if (diff.changed.length > 0) {
867
+ result += `\u{1F504} \u53D8\u66F4\u4FE1\u53F7 (${diff.changed.length}):
868
+ `;
869
+ diff.changed.forEach(({ id, oldValue, newValue }) => {
870
+ result += ` ${id}:
871
+ `;
872
+ result += ` \u65E7\u503C: ${JSON.stringify(oldValue)}
873
+ `;
874
+ result += ` \u65B0\u503C: ${JSON.stringify(newValue)}
875
+ `;
876
+ });
877
+ result += "\n";
878
+ }
879
+ if (result === "") {
880
+ result = "\u2728 \u5FEB\u7167\u5B8C\u5168\u76F8\u540C";
881
+ }
882
+ return result;
883
+ }
884
+ function getDiffBetweenSnapshots(index1, index2) {
885
+ const snapshot1 = getSnapshot(index1);
886
+ const snapshot2 = getSnapshot(index2);
887
+ if (!snapshot1 || !snapshot2) {
888
+ return null;
889
+ }
890
+ return compareSnapshots(snapshot1, snapshot2);
891
+ }
892
+ function getTimeTravelNavigator(index) {
893
+ const snapshots = getSnapshots();
894
+ const currentIndex = index !== void 0 ? index : Math.max(0, snapshots.length - 1);
895
+ return {
896
+ currentIndex,
897
+ total: snapshots.length,
898
+ canGoBack: currentIndex > 0,
899
+ canGoForward: currentIndex < snapshots.length - 1,
900
+ currentSnapshot: snapshots[currentIndex] ?? null,
901
+ previousSnapshot: currentIndex > 0 ? snapshots[currentIndex - 1] ?? null : null,
902
+ nextSnapshot: snapshots[currentIndex + 1] ?? null,
903
+ diff: currentIndex > 0 ? compareSnapshots(snapshots[currentIndex - 1], snapshots[currentIndex]) : null
904
+ };
905
+ }
906
+ function timeTravelBack() {
907
+ const navigator = getTimeTravelNavigator();
908
+ if (!navigator.canGoBack) return null;
909
+ return restoreSnapshot(navigator.currentIndex - 1) ?? null;
910
+ }
911
+ function timeTravelForward() {
912
+ const navigator = getTimeTravelNavigator();
913
+ if (!navigator.canGoForward) return null;
914
+ return restoreSnapshot(navigator.currentIndex + 1) ?? null;
915
+ }
916
+
917
+ // src/vdomInspector.ts
918
+ var registry = {
919
+ roots: /* @__PURE__ */ new Map(),
920
+ nodes: /* @__PURE__ */ new Map()};
921
+ function getVDOMRoots() {
922
+ return Array.from(registry.roots.values());
923
+ }
924
+ function getVDOMTree() {
925
+ return getVDOMRoots();
926
+ }
927
+ function getVDOMStats() {
928
+ let maxDepth = 0;
929
+ let componentCount = 0;
930
+ let elementCount = 0;
931
+ let textCount = 0;
932
+ for (const node of registry.nodes.values()) {
933
+ if (node.depth > maxDepth) maxDepth = node.depth;
934
+ if (node.isComponent) componentCount++;
935
+ else if (node.type === "element") elementCount++;
936
+ else if (node.type === "text") textCount++;
937
+ }
938
+ return {
939
+ totalNodes: registry.nodes.size,
940
+ rootCount: registry.roots.size,
941
+ maxDepth,
942
+ componentCount,
943
+ elementCount,
944
+ textCount
945
+ };
946
+ }
947
+ function serializeVDOMNode(node, indent = 0) {
948
+ const spaces = " ".repeat(indent);
949
+ let result = "";
950
+ if (node.type === "text") {
951
+ result += `${spaces}\u{1F4DD} "${node.text || ""}"
952
+ `;
953
+ } else if (node.isComponent) {
954
+ result += `${spaces}\u{1F9E9} <${node.tagName || "Component"}`;
955
+ if (node.key !== void 0) result += ` key="${node.key}"`;
956
+ result += ">\n";
957
+ } else if (node.type === "element") {
958
+ result += `${spaces}\u{1F3F7}\uFE0F <${node.tagName || "unknown"}`;
959
+ if (node.key !== void 0) result += ` key="${node.key}"`;
960
+ if (node.props) {
961
+ const propStr = Object.entries(node.props).slice(0, 3).map(([k, v]) => `${k}="${v}"`).join(" ");
962
+ if (propStr) result += ` ${propStr}`;
963
+ if (Object.keys(node.props).length > 3) {
964
+ result += " ...";
965
+ }
966
+ }
967
+ result += ">\n";
968
+ }
969
+ for (const child of node.children) {
970
+ result += serializeVDOMNode(child, indent + 1);
971
+ }
972
+ if ((node.isComponent || node.type === "element") && node.children.length === 0) {
973
+ result += `${spaces} (empty)
974
+ `;
975
+ }
976
+ return result;
977
+ }
978
+ function serializeVDOMTree() {
979
+ const roots = getVDOMRoots();
980
+ if (roots.length === 0) {
981
+ return "No VDOM roots registered.";
982
+ }
983
+ let result = `\u{1F4E6} VDOM Tree (${roots.length} root(s))
984
+ `;
985
+ result += "\u2500".repeat(40) + "\n";
986
+ for (const root of roots) {
987
+ result += serializeVDOMNode(root);
988
+ result += "\n";
989
+ }
990
+ const stats = getVDOMStats();
991
+ result += "\u2500".repeat(40) + "\n";
992
+ result += `\u{1F4CA} Stats: ${stats.totalNodes} nodes, ${stats.componentCount} components, `;
993
+ result += `${stats.elementCount} elements, ${stats.textCount} text nodes
994
+ `;
995
+ result += `\u{1F4CF} Max depth: ${stats.maxDepth}
996
+ `;
997
+ return result;
998
+ }
640
999
 
641
1000
  // src/performance.ts
642
1001
  var DEFAULT_OPTIONS = {
@@ -881,7 +1240,7 @@ function outputAlert(alert) {
881
1240
  error: "color: #ff4d4f;",
882
1241
  critical: "color: #722ed1; background: #ff4d4f; padding: 2px 5px;"
883
1242
  };
884
- console.log(
1243
+ console.warn(
885
1244
  `%c[LytJS Alert] ${alert.ruleName}%c ${alert.message}`,
886
1245
  levelStyles[alert.level],
887
1246
  "color: inherit;"
@@ -968,6 +1327,151 @@ function startTimer(name, type = "custom") {
968
1327
  recordMetric({ name, type, duration });
969
1328
  };
970
1329
  }
1330
+ var timelineEventStack = [];
1331
+ var timelineEvents = [];
1332
+ var maxTimelineEvents = 1e3;
1333
+ var currentDepth = 0;
1334
+ function beginTimelineEvent(name, category = "custom", metadata) {
1335
+ const id = `timeline_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
1336
+ const event = {
1337
+ id,
1338
+ name,
1339
+ category,
1340
+ startTime: performance.now(),
1341
+ duration: 0,
1342
+ depth: currentDepth,
1343
+ metadata
1344
+ };
1345
+ timelineEventStack.push({
1346
+ event,
1347
+ startTime: performance.now()
1348
+ });
1349
+ currentDepth++;
1350
+ return id;
1351
+ }
1352
+ function endTimelineEvent(id) {
1353
+ const stackIndex = timelineEventStack.findIndex((item) => item.event.id === id);
1354
+ if (stackIndex === -1) {
1355
+ console.warn(`[DevTools] Timeline event ${id} not found in stack`);
1356
+ return null;
1357
+ }
1358
+ const { event } = timelineEventStack[stackIndex];
1359
+ const endTime = performance.now();
1360
+ event.duration = endTime - event.startTime;
1361
+ for (let i = stackIndex + 1; i < timelineEventStack.length; i++) {
1362
+ const nestedEvent = timelineEventStack[i];
1363
+ event.duration = Math.max(
1364
+ event.duration,
1365
+ nestedEvent.startTime - event.startTime + nestedEvent.event.duration
1366
+ );
1367
+ }
1368
+ timelineEventStack.splice(stackIndex);
1369
+ currentDepth = Math.max(0, currentDepth - 1);
1370
+ timelineEvents.push(event);
1371
+ if (timelineEvents.length > maxTimelineEvents) {
1372
+ timelineEvents.shift();
1373
+ }
1374
+ return event;
1375
+ }
1376
+ function getTimelineEvents() {
1377
+ return [...timelineEvents];
1378
+ }
1379
+ function getTimelineEventsInRange(startTime, endTime) {
1380
+ return timelineEvents.filter(
1381
+ (event) => event.startTime >= startTime && event.startTime <= endTime
1382
+ );
1383
+ }
1384
+ function getSlowOperations(limit = 10, threshold) {
1385
+ const sorted = [...timelineEvents].sort((a, b) => b.duration - a.duration);
1386
+ const result = threshold !== void 0 ? sorted.filter((e) => e.duration >= threshold) : sorted;
1387
+ return result.slice(0, limit);
1388
+ }
1389
+ function getFlameGraphData() {
1390
+ const root = {
1391
+ name: "root",
1392
+ value: 0,
1393
+ children: [],
1394
+ category: "custom"
1395
+ };
1396
+ const nodeMap = /* @__PURE__ */ new Map();
1397
+ nodeMap.set("root", root);
1398
+ timelineEvents.forEach((event) => {
1399
+ const node = {
1400
+ name: event.name,
1401
+ value: event.duration,
1402
+ category: event.category,
1403
+ children: []
1404
+ };
1405
+ const parentNode = event.depth === 0 ? root : findParentNode(root, event.depth);
1406
+ if (parentNode) {
1407
+ if (!parentNode.children) {
1408
+ parentNode.children = [];
1409
+ }
1410
+ parentNode.children.push(node);
1411
+ }
1412
+ nodeMap.set(event.id, node);
1413
+ });
1414
+ aggregateFlameGraphValues(root);
1415
+ return root;
1416
+ }
1417
+ function findParentNode(node, depth) {
1418
+ if (!node.children || node.children.length === 0) {
1419
+ return depth === 1 ? node : null;
1420
+ }
1421
+ let targetDepth = depth - 1;
1422
+ let currentNode = node;
1423
+ while (targetDepth > 0 && currentNode) {
1424
+ const children = currentNode.children || [];
1425
+ if (children.length === 0) {
1426
+ return currentNode;
1427
+ }
1428
+ const lastChild = children[children.length - 1];
1429
+ currentNode = lastChild;
1430
+ targetDepth--;
1431
+ }
1432
+ return currentNode;
1433
+ }
1434
+ function aggregateFlameGraphValues(node) {
1435
+ if (!node.children || node.children.length === 0) {
1436
+ return node.value;
1437
+ }
1438
+ let totalValue = node.value;
1439
+ node.children.forEach((child) => {
1440
+ totalValue += aggregateFlameGraphValues(child);
1441
+ });
1442
+ node.value = totalValue;
1443
+ return totalValue;
1444
+ }
1445
+ function clearTimelineEvents() {
1446
+ timelineEvents.length = 0;
1447
+ timelineEventStack.length = 0;
1448
+ currentDepth = 0;
1449
+ }
1450
+ function exportTimelineAsJSON() {
1451
+ return JSON.stringify(
1452
+ {
1453
+ events: timelineEvents,
1454
+ exportedAt: Date.now()
1455
+ },
1456
+ null,
1457
+ 2
1458
+ );
1459
+ }
1460
+ function serializeTimelineEvents() {
1461
+ if (timelineEvents.length === 0) {
1462
+ return "\u6682\u65E0\u65F6\u5E8F\u4E8B\u4EF6\u6570\u636E";
1463
+ }
1464
+ let result = `\u{1F4CA} \u65F6\u5E8F\u4E8B\u4EF6 (${timelineEvents.length} \u4E2A)
1465
+
1466
+ `;
1467
+ timelineEvents.slice(-20).forEach((event) => {
1468
+ const indent = " ".repeat(event.depth);
1469
+ const duration = event.duration.toFixed(2);
1470
+ result += `${indent}\u251C\u2500 ${event.name} [${event.category}] ${duration}ms
1471
+ `;
1472
+ });
1473
+ return result;
1474
+ }
971
1475
 
972
1476
  // src/benchmark.ts
973
1477
  var LARGE_SCALE_SCENARIOS = [
@@ -1208,7 +1712,7 @@ var DevTools = class {
1208
1712
  this.toggle();
1209
1713
  }
1210
1714
  });
1211
- console.log("[LytJS DevTools] Initialized. Press Ctrl+Shift+D to toggle.");
1715
+ console.warn("[LytJS DevTools] Initialized. Press Ctrl+Shift+D to toggle.");
1212
1716
  }
1213
1717
  /**
1214
1718
  * 创建 DevTools 面板
@@ -1296,6 +1800,15 @@ var DevTools = class {
1296
1800
  cursor: pointer;
1297
1801
  border-bottom: 2px solid transparent;
1298
1802
  ">Signals</button>
1803
+ <button class="lytjs-devtools-tab" data-tab="vdom" style="
1804
+ flex: 1;
1805
+ padding: 10px;
1806
+ background: transparent;
1807
+ border: none;
1808
+ color: #d4d4d4;
1809
+ cursor: pointer;
1810
+ border-bottom: 2px solid transparent;
1811
+ ">VDOM</button>
1299
1812
  <button class="lytjs-devtools-tab" data-tab="performance" style="
1300
1813
  flex: 1;
1301
1814
  padding: 10px;
@@ -1352,6 +1865,9 @@ var DevTools = class {
1352
1865
  case "signals":
1353
1866
  content.innerHTML = this.renderSignalsTab();
1354
1867
  break;
1868
+ case "vdom":
1869
+ content.innerHTML = this.renderVDOMTab();
1870
+ break;
1355
1871
  case "performance":
1356
1872
  content.innerHTML = this.renderPerformanceTab();
1357
1873
  break;
@@ -1386,7 +1902,7 @@ var DevTools = class {
1386
1902
  */
1387
1903
  renderSignalsTab() {
1388
1904
  const nodes = getSignalNodes();
1389
- let html = `
1905
+ const html = `
1390
1906
  <div style="margin-bottom: 15px;">
1391
1907
  <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px;">
1392
1908
  <strong>\u{1F4CA} Signals (${nodes.length})</strong>
@@ -1491,6 +2007,97 @@ var DevTools = class {
1491
2007
  html += "</div>";
1492
2008
  return html;
1493
2009
  }
2010
+ /**
2011
+ * 渲染 VDOM 标签页
2012
+ */
2013
+ renderVDOMTab() {
2014
+ const roots = getVDOMTree();
2015
+ const stats = getVDOMStats();
2016
+ if (roots.length === 0) {
2017
+ return `
2018
+ <div style="color: #666; text-align: center; padding: 40px;">
2019
+ <div style="font-size: 48px; margin-bottom: 15px;">\u{1F333}</div>
2020
+ <div>No VDOM roots registered.</div>
2021
+ <div style="font-size: 11px; margin-top: 10px; color: #555;">
2022
+ Use registerVDOMRoot() to register your VDOM roots.
2023
+ </div>
2024
+ </div>
2025
+ `;
2026
+ }
2027
+ const html = `
2028
+ <div style="margin-bottom: 15px;">
2029
+ <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 15px;">
2030
+ <strong>\u{1F333} VDOM Tree</strong>
2031
+ <button id="lytjs-devtools-clear-vdom" style="
2032
+ background: #ff4d4f;
2033
+ border: none;
2034
+ color: white;
2035
+ padding: 5px 10px;
2036
+ border-radius: 4px;
2037
+ cursor: pointer;
2038
+ font-size: 11px;
2039
+ ">\u6E05\u7A7A</button>
2040
+ </div>
2041
+
2042
+ <div style="background: #252526; border-radius: 4px; padding: 15px; margin-bottom: 15px;">
2043
+ <div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; text-align: center;">
2044
+ <div>
2045
+ <div style="color: #888; font-size: 11px;">\u603B\u8282\u70B9\u6570</div>
2046
+ <div style="font-size: 20px; font-weight: bold; color: #4fc08d;">${stats.totalNodes}</div>
2047
+ </div>
2048
+ <div>
2049
+ <div style="color: #888; font-size: 11px;">\u7EC4\u4EF6\u6570</div>
2050
+ <div style="font-size: 20px; font-weight: bold; color: #1890ff;">${stats.componentCount}</div>
2051
+ </div>
2052
+ <div>
2053
+ <div style="color: #888; font-size: 11px;">\u5143\u7D20\u6570</div>
2054
+ <div style="font-size: 20px; font-weight: bold; color: #faad14;">${stats.elementCount}</div>
2055
+ </div>
2056
+ </div>
2057
+ <div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; text-align: center; margin-top: 10px;">
2058
+ <div>
2059
+ <div style="color: #888; font-size: 11px;">\u6587\u672C\u8282\u70B9</div>
2060
+ <div style="font-size: 16px; font-weight: bold; color: #888;">${stats.textCount}</div>
2061
+ </div>
2062
+ <div>
2063
+ <div style="color: #888; font-size: 11px;">\u6839\u8282\u70B9</div>
2064
+ <div style="font-size: 16px; font-weight: bold; color: #888;">${stats.rootCount}</div>
2065
+ </div>
2066
+ <div>
2067
+ <div style="color: #888; font-size: 11px;">\u6700\u5927\u6DF1\u5EA6</div>
2068
+ <div style="font-size: 16px; font-weight: bold; color: #888;">${stats.maxDepth}</div>
2069
+ </div>
2070
+ </div>
2071
+ </div>
2072
+
2073
+ <div style="margin-bottom: 10px;">
2074
+ <input id="lytjs-devtools-vdom-search" type="text" placeholder="\u641C\u7D22\u6807\u7B7E\u540D..." style="
2075
+ width: 100%;
2076
+ padding: 8px;
2077
+ background: #252526;
2078
+ border: 1px solid #3d3d3d;
2079
+ border-radius: 4px;
2080
+ color: #d4d4d4;
2081
+ font-size: 12px;
2082
+ " />
2083
+ </div>
2084
+
2085
+ <div id="lytjs-devtools-vdom-tree" style="
2086
+ background: #252526;
2087
+ border-radius: 4px;
2088
+ padding: 15px;
2089
+ max-height: 400px;
2090
+ overflow: auto;
2091
+ font-family: 'Consolas', 'Monaco', monospace;
2092
+ font-size: 11px;
2093
+ line-height: 1.6;
2094
+ ">
2095
+ <pre style="margin: 0; white-space: pre-wrap; word-break: break-all;">${serializeVDOMTree()}</pre>
2096
+ </div>
2097
+ </div>
2098
+ `;
2099
+ return html;
2100
+ }
1494
2101
  /**
1495
2102
  * 渲染 Performance 标签页
1496
2103
  */
@@ -1663,6 +2270,6 @@ function uninstallDevTools() {
1663
2270
  }
1664
2271
  }
1665
2272
 
1666
- export { LARGE_SCALE_SCENARIOS, acknowledgeAlert, acknowledgeAllAlerts, addObserver, clearAlerts, clearBenchmarkResults, clearMetrics, clearPerformanceRecords, clearRouteHistory, clearSignalRegistry, clearSnapshots, clearStoreRegistry, compareBenchmarkResults, createLargeScaleBenchmark, createRegressionDetector, createSnapshot, installDevTools as default, dispatchStoreAction, getAlertRules, getAlerts, getBenchmarkResults, getComponentTree, getCurrentRoute, getDependencyGraph, getDevTools, getLatestBenchmarkResult, getMemoryUsage, getMetrics, getPerformanceRecords, getPerformanceReport, getPerformanceStats, getRegisteredStoreIds, getRouteHistory, getRoutes, getSignalNode, getSignalNodes, getSnapshots, getStats, getStoreState, getStoreStates, getTimeTravelState, goBack, initPerformanceMonitor, installDevTools, isRouterRegistered, navigateTo, navigateToName, onStoreChange, recordDependency, recordMetric, recordSignalUpdate, registerAlertRule, registerRootComponent, registerRouter, registerSignal, registerStore, removeObserver, resetPerformanceMonitor, restoreSnapshot, runAsyncBenchmark, runBenchmark, serializeAllBenchmarkResults, serializeBenchmarkResult, serializeComponentTree, serializeDependencyGraph, serializeMemoryUsage, serializePerformanceReport, serializePerformanceStats, serializeRouteInfo, serializeSignalNode, serializeStoreStates, setAlertRuleEnabled, setStoreState, startTimer, subscribeStore, uninstallDevTools, unregisterAlertRule, unregisterRootComponent, unregisterRouter, unregisterSignal, unregisterStore, unsubscribeStore, unwatchRouteChanges, watchRouteChanges };
2273
+ export { LARGE_SCALE_SCENARIOS, acknowledgeAlert, acknowledgeAllAlerts, addObserver, beginTimelineEvent, clearAlerts, clearBenchmarkResults, clearMetrics, clearPerformanceRecords, clearRouteHistory, clearSignalRegistry, clearSnapshots, clearStoreRegistry, clearTimelineEvents, compareBenchmarkResults, compareSnapshots, createLargeScaleBenchmark, createRegressionDetector, createSnapshot, installDevTools as default, dispatchStoreAction, endTimelineEvent, exportTimelineAsJSON, filterSignals, getAlertRules, getAlerts, getBenchmarkResults, getComponentTree, getCurrentRoute, getDependencyGraph, getDevTools, getDiffBetweenSnapshots, getFlameGraphData, getLatestBenchmarkResult, getMemoryUsage, getMetrics, getPerformanceRecords, getPerformanceReport, getPerformanceStats, getRegisteredStoreIds, getRouteHistory, getRoutes, getSignalNode, getSignalNodes, getSlowOperations, getSnapshots, getStats, getStoreState, getStoreStates, getSubgraph, getTimeTravelNavigator, getTimeTravelState, getTimelineEvents, getTimelineEventsInRange, getVisualLayoutGraph, goBack, initPerformanceMonitor, installDevTools, isRouterRegistered, navigateTo, navigateToName, onStoreChange, recordDependency, recordMetric, recordSignalUpdate, registerAlertRule, registerRootComponent, registerRouter, registerSignal, registerStore, removeObserver, resetPerformanceMonitor, restoreSnapshot, runAsyncBenchmark, runBenchmark, searchSignals, serializeAllBenchmarkResults, serializeBenchmarkResult, serializeComponentTree, serializeDependencyGraph, serializeMemoryUsage, serializePerformanceReport, serializePerformanceStats, serializeRouteInfo, serializeSignalNode, serializeSnapshotDiff, serializeStoreStates, serializeTimelineEvents, setAlertRuleEnabled, setStoreState, startTimer, subscribeStore, timeTravelBack, timeTravelForward, uninstallDevTools, unregisterAlertRule, unregisterRootComponent, unregisterRouter, unregisterSignal, unregisterStore, unsubscribeStore, unwatchRouteChanges, watchRouteChanges };
1667
2274
  //# sourceMappingURL=index.mjs.map
1668
2275
  //# sourceMappingURL=index.mjs.map