state-surgeon 1.1.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
- import React2, { useState, useMemo, useCallback, useEffect } from 'react';
2
+ import React4, { useState, useMemo, useCallback, useEffect } from 'react';
3
3
 
4
4
  // src/dashboard/components/MutationInspector.tsx
5
5
  function MutationInspector({
@@ -1676,7 +1676,1674 @@ function TimelineScrubber({
1676
1676
  ` })
1677
1677
  ] });
1678
1678
  }
1679
- var DashboardContext = React2.createContext(null);
1679
+
1680
+ // src/core/analyzer.ts
1681
+ var StateAnalyzer = class {
1682
+ constructor() {
1683
+ this.invariants = [];
1684
+ this.updateTimestamps = /* @__PURE__ */ new Map();
1685
+ }
1686
+ /**
1687
+ * Register custom invariant rules
1688
+ */
1689
+ addInvariant(invariant) {
1690
+ this.invariants.push(invariant);
1691
+ }
1692
+ /**
1693
+ * Analyze a single mutation for issues
1694
+ */
1695
+ analyzeMutation(mutation, index, timeline) {
1696
+ const issues = [];
1697
+ if (mutation.diff) {
1698
+ for (const diff of mutation.diff) {
1699
+ if (diff.operation === "REMOVE") {
1700
+ issues.push({
1701
+ id: `issue_${mutation.id}_${diff.path}_loss`,
1702
+ category: "state-loss",
1703
+ severity: "critical",
1704
+ title: `State field removed: ${diff.path}`,
1705
+ description: `The field "${diff.path}" was removed from state. This may indicate an overwrite instead of a merge.`,
1706
+ mutationId: mutation.id,
1707
+ mutationIndex: index,
1708
+ path: diff.path,
1709
+ previousValue: diff.oldValue,
1710
+ currentValue: void 0,
1711
+ suggestion: "Use spread operator to preserve existing fields: setState(prev => ({ ...prev, newField }))",
1712
+ timestamp: mutation.timestamp
1713
+ });
1714
+ }
1715
+ if (diff.operation !== "REMOVE") {
1716
+ const invalidIssue = this.checkInvalidValue(diff, mutation, index);
1717
+ if (invalidIssue) {
1718
+ issues.push(invalidIssue);
1719
+ }
1720
+ }
1721
+ if (diff.operation === "UPDATE") {
1722
+ const typeIssue = this.checkTypeChange(diff, mutation, index);
1723
+ if (typeIssue) {
1724
+ issues.push(typeIssue);
1725
+ }
1726
+ }
1727
+ }
1728
+ }
1729
+ if (this.isNoOpUpdate(mutation)) {
1730
+ issues.push({
1731
+ id: `issue_${mutation.id}_noop`,
1732
+ category: "no-op-update",
1733
+ severity: "info",
1734
+ title: "Redundant state update",
1735
+ description: "This mutation did not change any values. Consider memoizing or adding conditions.",
1736
+ mutationId: mutation.id,
1737
+ mutationIndex: index,
1738
+ suggestion: "Add a condition before updating: if (newValue !== currentValue) setState(newValue)",
1739
+ timestamp: mutation.timestamp
1740
+ });
1741
+ }
1742
+ const excessiveIssue = this.checkExcessiveUpdates(mutation, index);
1743
+ if (excessiveIssue) {
1744
+ issues.push(excessiveIssue);
1745
+ }
1746
+ for (const invariant of this.invariants) {
1747
+ const violation = this.checkInvariant(invariant, mutation, index);
1748
+ if (violation) {
1749
+ issues.push(violation);
1750
+ }
1751
+ }
1752
+ return issues;
1753
+ }
1754
+ /**
1755
+ * Check for invalid values (NaN, unexpected undefined/null)
1756
+ */
1757
+ checkInvalidValue(diff, mutation, index) {
1758
+ const value = diff.newValue;
1759
+ if (typeof value === "number" && isNaN(value)) {
1760
+ return {
1761
+ id: `issue_${mutation.id}_${diff.path}_nan`,
1762
+ category: "invalid-value",
1763
+ severity: "critical",
1764
+ title: `NaN value at: ${diff.path}`,
1765
+ description: `The value at "${diff.path}" became NaN. This usually indicates a calculation with undefined/null.`,
1766
+ mutationId: mutation.id,
1767
+ mutationIndex: index,
1768
+ path: diff.path,
1769
+ previousValue: diff.oldValue,
1770
+ currentValue: value,
1771
+ suggestion: "Check for undefined/null values before calculation. Use default values: (value ?? 0)",
1772
+ timestamp: mutation.timestamp
1773
+ };
1774
+ }
1775
+ if (value === void 0 && diff.oldValue !== void 0 && diff.operation === "UPDATE") {
1776
+ return {
1777
+ id: `issue_${mutation.id}_${diff.path}_undefined`,
1778
+ category: "invalid-value",
1779
+ severity: "critical",
1780
+ title: `Value became undefined: ${diff.path}`,
1781
+ description: `The field "${diff.path}" changed from a defined value to undefined.`,
1782
+ mutationId: mutation.id,
1783
+ mutationIndex: index,
1784
+ path: diff.path,
1785
+ previousValue: diff.oldValue,
1786
+ currentValue: void 0,
1787
+ suggestion: "Ensure the value is always defined or explicitly handle undefined cases.",
1788
+ timestamp: mutation.timestamp
1789
+ };
1790
+ }
1791
+ return null;
1792
+ }
1793
+ /**
1794
+ * Check for unexpected type changes
1795
+ */
1796
+ checkTypeChange(diff, mutation, index) {
1797
+ if (diff.oldValue === void 0 || diff.oldValue === null) return null;
1798
+ if (diff.newValue === void 0 || diff.newValue === null) return null;
1799
+ const oldType = typeof diff.oldValue;
1800
+ const newType = typeof diff.newValue;
1801
+ if (oldType !== newType) {
1802
+ return {
1803
+ id: `issue_${mutation.id}_${diff.path}_type`,
1804
+ category: "type-change",
1805
+ severity: "warning",
1806
+ title: `Type changed: ${diff.path}`,
1807
+ description: `The type of "${diff.path}" changed from ${oldType} to ${newType}.`,
1808
+ mutationId: mutation.id,
1809
+ mutationIndex: index,
1810
+ path: diff.path,
1811
+ previousValue: diff.oldValue,
1812
+ currentValue: diff.newValue,
1813
+ suggestion: "Ensure consistent types. Use TypeScript or runtime validation.",
1814
+ timestamp: mutation.timestamp
1815
+ };
1816
+ }
1817
+ return null;
1818
+ }
1819
+ /**
1820
+ * Check if mutation is a no-op (no actual changes)
1821
+ */
1822
+ isNoOpUpdate(mutation) {
1823
+ if (!mutation.diff || mutation.diff.length === 0) {
1824
+ return true;
1825
+ }
1826
+ return mutation.diff.every((d) => {
1827
+ if (d.operation !== "UPDATE") return false;
1828
+ return JSON.stringify(d.oldValue) === JSON.stringify(d.newValue);
1829
+ });
1830
+ }
1831
+ /**
1832
+ * Check for excessive updates in short time period
1833
+ */
1834
+ checkExcessiveUpdates(mutation, index) {
1835
+ const key = mutation.component || mutation.source;
1836
+ const timestamps = this.updateTimestamps.get(key) || [];
1837
+ timestamps.push(mutation.timestamp);
1838
+ const cutoff = mutation.timestamp - 100;
1839
+ const recentTimestamps = timestamps.filter((t) => t >= cutoff);
1840
+ this.updateTimestamps.set(key, recentTimestamps);
1841
+ if (recentTimestamps.length > 5) {
1842
+ return {
1843
+ id: `issue_${mutation.id}_excessive`,
1844
+ category: "excessive-updates",
1845
+ severity: "warning",
1846
+ title: `Excessive updates from: ${key}`,
1847
+ description: `${recentTimestamps.length} mutations in 100ms from "${key}". This may cause performance issues.`,
1848
+ mutationId: mutation.id,
1849
+ mutationIndex: index,
1850
+ suggestion: "Use debouncing, batching, or memoization to reduce update frequency.",
1851
+ timestamp: mutation.timestamp
1852
+ };
1853
+ }
1854
+ return null;
1855
+ }
1856
+ /**
1857
+ * Check custom invariant rule
1858
+ */
1859
+ checkInvariant(invariant, mutation, index) {
1860
+ if (!mutation.nextState) return null;
1861
+ const value = getValueAtPath(mutation.nextState, invariant.path);
1862
+ try {
1863
+ const isValid = invariant.rule(value);
1864
+ if (!isValid) {
1865
+ return {
1866
+ id: `issue_${mutation.id}_invariant_${invariant.name}`,
1867
+ category: "broken-invariant",
1868
+ severity: "critical",
1869
+ title: `Invariant violated: ${invariant.name}`,
1870
+ description: invariant.message,
1871
+ mutationId: mutation.id,
1872
+ mutationIndex: index,
1873
+ path: invariant.path,
1874
+ currentValue: value,
1875
+ suggestion: `Ensure the invariant "${invariant.name}" is always maintained.`,
1876
+ timestamp: mutation.timestamp
1877
+ };
1878
+ }
1879
+ } catch (e) {
1880
+ return null;
1881
+ }
1882
+ return null;
1883
+ }
1884
+ /**
1885
+ * Analyze entire timeline for issues
1886
+ */
1887
+ analyzeTimeline(timeline) {
1888
+ const allIssues = [];
1889
+ this.updateTimestamps.clear();
1890
+ for (let i = 0; i < timeline.length; i++) {
1891
+ const issues = this.analyzeMutation(timeline[i], i, timeline);
1892
+ allIssues.push(...issues);
1893
+ }
1894
+ return allIssues;
1895
+ }
1896
+ /**
1897
+ * Build dependency graph showing which components touch which state paths
1898
+ */
1899
+ buildDependencyGraph(timeline) {
1900
+ const nodes = /* @__PURE__ */ new Map();
1901
+ for (const mutation of timeline) {
1902
+ if (!mutation.diff) continue;
1903
+ const component = mutation.component || mutation.source || "unknown";
1904
+ for (const diff of mutation.diff) {
1905
+ const existing = nodes.get(diff.path);
1906
+ if (existing) {
1907
+ existing.components.add(component);
1908
+ existing.mutationCount++;
1909
+ existing.lastMutationId = mutation.id;
1910
+ } else {
1911
+ nodes.set(diff.path, {
1912
+ path: diff.path,
1913
+ components: /* @__PURE__ */ new Set([component]),
1914
+ mutationCount: 1,
1915
+ lastMutationId: mutation.id
1916
+ });
1917
+ }
1918
+ }
1919
+ }
1920
+ const couplings = [];
1921
+ for (const [path, node] of nodes) {
1922
+ if (node.components.size > 1) {
1923
+ couplings.push({
1924
+ path,
1925
+ components: Array.from(node.components),
1926
+ severity: node.components.size > 2 ? "critical" : "warning"
1927
+ });
1928
+ }
1929
+ }
1930
+ return { nodes, couplings };
1931
+ }
1932
+ /**
1933
+ * Find causal chain - which mutations caused downstream effects
1934
+ */
1935
+ findCausalChain(mutationId, timeline) {
1936
+ const rootIndex = timeline.findIndex((m) => m.id === mutationId);
1937
+ if (rootIndex === -1) return null;
1938
+ const root = timeline[rootIndex];
1939
+ const effects = [];
1940
+ if (!root.diff) return { rootMutation: root, effects };
1941
+ const changedPaths = new Set(root.diff.map((d) => d.path));
1942
+ for (let i = rootIndex + 1; i < timeline.length; i++) {
1943
+ const mutation = timeline[i];
1944
+ if (!mutation.diff) continue;
1945
+ for (const diff of mutation.diff) {
1946
+ for (const changedPath of changedPaths) {
1947
+ if (diff.path.startsWith(changedPath) || changedPath.startsWith(diff.path)) {
1948
+ effects.push({
1949
+ mutation,
1950
+ causedBy: root.id,
1951
+ reason: `Uses path "${diff.path}" which was affected by "${changedPath}"`
1952
+ });
1953
+ break;
1954
+ }
1955
+ }
1956
+ }
1957
+ }
1958
+ return { rootMutation: root, effects };
1959
+ }
1960
+ /**
1961
+ * Find the first mutation that corrupted state
1962
+ * Uses binary search for efficiency
1963
+ */
1964
+ findCorruptionPoint(timeline, validator) {
1965
+ let left = 0;
1966
+ let right = timeline.length - 1;
1967
+ let result = null;
1968
+ while (left <= right) {
1969
+ const mid = Math.floor((left + right) / 2);
1970
+ const mutation = timeline[mid];
1971
+ if (mutation.nextState && !validator(mutation.nextState)) {
1972
+ result = { mutation, index: mid };
1973
+ right = mid - 1;
1974
+ } else {
1975
+ left = mid + 1;
1976
+ }
1977
+ }
1978
+ return result;
1979
+ }
1980
+ };
1981
+ function getValueAtPath(obj, path) {
1982
+ if (!path) return obj;
1983
+ const parts = path.split(".");
1984
+ let current = obj;
1985
+ for (const part of parts) {
1986
+ if (current === null || current === void 0) return void 0;
1987
+ if (typeof current !== "object") return void 0;
1988
+ current = current[part];
1989
+ }
1990
+ return current;
1991
+ }
1992
+ function DependencyGraph({
1993
+ graph,
1994
+ onPathSelect,
1995
+ selectedPath,
1996
+ className = ""
1997
+ }) {
1998
+ const [viewMode, setViewMode] = useState("couplings");
1999
+ const [hoveredPath, setHoveredPath] = useState(null);
2000
+ const sortedNodes = useMemo(() => {
2001
+ const nodes = Array.from(graph.nodes.values());
2002
+ return nodes.sort((a, b) => b.mutationCount - a.mutationCount);
2003
+ }, [graph.nodes]);
2004
+ const allComponents = useMemo(() => {
2005
+ const components = /* @__PURE__ */ new Set();
2006
+ for (const node of graph.nodes.values()) {
2007
+ for (const c of node.components) {
2008
+ components.add(c);
2009
+ }
2010
+ }
2011
+ return Array.from(components);
2012
+ }, [graph.nodes]);
2013
+ const componentColors = useMemo(() => {
2014
+ const colors = {};
2015
+ const palette = [
2016
+ "#61dafb",
2017
+ "#764abc",
2018
+ "#f59e0b",
2019
+ "#22c55e",
2020
+ "#ef4444",
2021
+ "#a78bfa",
2022
+ "#f472b6",
2023
+ "#06b6d4",
2024
+ "#84cc16",
2025
+ "#f97316"
2026
+ ];
2027
+ allComponents.forEach((comp, i) => {
2028
+ colors[comp] = palette[i % palette.length];
2029
+ });
2030
+ return colors;
2031
+ }, [allComponents]);
2032
+ const handlePathClick = useCallback((path) => {
2033
+ onPathSelect?.(path);
2034
+ }, [onPathSelect]);
2035
+ const displayNodes = viewMode === "couplings" ? sortedNodes.filter((n) => n.components.size > 1) : sortedNodes;
2036
+ return /* @__PURE__ */ jsxs("div", { className: `dependency-graph ${className}`, children: [
2037
+ /* @__PURE__ */ jsxs("div", { className: "dg-header", children: [
2038
+ /* @__PURE__ */ jsx("span", { className: "dg-title", children: "\u{1F517} State Dependencies" }),
2039
+ /* @__PURE__ */ jsxs("div", { className: "dg-controls", children: [
2040
+ /* @__PURE__ */ jsxs(
2041
+ "button",
2042
+ {
2043
+ className: `dg-view-btn ${viewMode === "couplings" ? "active" : ""}`,
2044
+ onClick: () => setViewMode("couplings"),
2045
+ children: [
2046
+ "\u26A0\uFE0F Couplings Only (",
2047
+ graph.couplings.length,
2048
+ ")"
2049
+ ]
2050
+ }
2051
+ ),
2052
+ /* @__PURE__ */ jsxs(
2053
+ "button",
2054
+ {
2055
+ className: `dg-view-btn ${viewMode === "all" ? "active" : ""}`,
2056
+ onClick: () => setViewMode("all"),
2057
+ children: [
2058
+ "\u{1F4CA} All Paths (",
2059
+ graph.nodes.size,
2060
+ ")"
2061
+ ]
2062
+ }
2063
+ )
2064
+ ] })
2065
+ ] }),
2066
+ /* @__PURE__ */ jsx("div", { className: "dg-legend", children: allComponents.map((comp) => /* @__PURE__ */ jsxs(
2067
+ "span",
2068
+ {
2069
+ className: "dg-legend-item",
2070
+ style: { "--comp-color": componentColors[comp] },
2071
+ children: [
2072
+ /* @__PURE__ */ jsx("span", { className: "dg-legend-dot" }),
2073
+ comp
2074
+ ]
2075
+ },
2076
+ comp
2077
+ )) }),
2078
+ graph.couplings.length > 0 && viewMode === "couplings" && /* @__PURE__ */ jsxs("div", { className: "dg-alert", children: [
2079
+ /* @__PURE__ */ jsx("span", { className: "dg-alert-icon", children: "\u26A0\uFE0F" }),
2080
+ /* @__PURE__ */ jsxs("div", { className: "dg-alert-content", children: [
2081
+ /* @__PURE__ */ jsx("strong", { children: "Hidden State Coupling Detected" }),
2082
+ /* @__PURE__ */ jsxs("p", { children: [
2083
+ graph.couplings.length,
2084
+ " state path",
2085
+ graph.couplings.length > 1 ? "s are" : " is",
2086
+ " accessed by multiple components. This can cause unexpected state conflicts."
2087
+ ] })
2088
+ ] })
2089
+ ] }),
2090
+ displayNodes.length === 0 && /* @__PURE__ */ jsxs("div", { className: "dg-empty", children: [
2091
+ /* @__PURE__ */ jsx("span", { className: "dg-empty-icon", children: "\u2705" }),
2092
+ /* @__PURE__ */ jsx("p", { children: "No state coupling detected" })
2093
+ ] }),
2094
+ /* @__PURE__ */ jsx("div", { className: "dg-paths", children: displayNodes.map((node) => {
2095
+ const isCoupled = node.components.size > 1;
2096
+ const coupling = graph.couplings.find((c) => c.path === node.path);
2097
+ return /* @__PURE__ */ jsxs(
2098
+ "div",
2099
+ {
2100
+ className: `dg-path ${isCoupled ? "dg-path--coupled" : ""} ${selectedPath === node.path ? "selected" : ""} ${hoveredPath === node.path ? "hovered" : ""}`,
2101
+ onClick: () => handlePathClick(node.path),
2102
+ onMouseEnter: () => setHoveredPath(node.path),
2103
+ onMouseLeave: () => setHoveredPath(null),
2104
+ children: [
2105
+ /* @__PURE__ */ jsxs("div", { className: "dg-path-header", children: [
2106
+ /* @__PURE__ */ jsx("code", { className: "dg-path-name", children: node.path }),
2107
+ /* @__PURE__ */ jsxs("span", { className: "dg-path-count", children: [
2108
+ node.mutationCount,
2109
+ " mutation",
2110
+ node.mutationCount > 1 ? "s" : ""
2111
+ ] })
2112
+ ] }),
2113
+ /* @__PURE__ */ jsx("div", { className: "dg-path-components", children: Array.from(node.components).map((comp) => /* @__PURE__ */ jsx(
2114
+ "span",
2115
+ {
2116
+ className: "dg-component-tag",
2117
+ style: {
2118
+ backgroundColor: componentColors[comp] + "30",
2119
+ borderColor: componentColors[comp],
2120
+ color: componentColors[comp]
2121
+ },
2122
+ children: comp
2123
+ },
2124
+ comp
2125
+ )) }),
2126
+ isCoupled && coupling && /* @__PURE__ */ jsxs("div", { className: `dg-coupling-warning dg-coupling-${coupling.severity}`, children: [
2127
+ /* @__PURE__ */ jsx("span", { className: "dg-warning-icon", children: coupling.severity === "critical" ? "\u{1F534}" : "\u{1F7E1}" }),
2128
+ /* @__PURE__ */ jsxs("span", { className: "dg-warning-text", children: [
2129
+ coupling.components.length,
2130
+ " components access this path"
2131
+ ] })
2132
+ ] })
2133
+ ]
2134
+ },
2135
+ node.path
2136
+ );
2137
+ }) }),
2138
+ displayNodes.length > 0 && displayNodes.length <= 20 && /* @__PURE__ */ jsxs("div", { className: "dg-visual", children: [
2139
+ /* @__PURE__ */ jsx("div", { className: "dg-visual-title", children: "Dependency Visualization" }),
2140
+ /* @__PURE__ */ jsxs("svg", { className: "dg-svg", viewBox: "0 0 400 300", children: [
2141
+ allComponents.map((comp, i) => {
2142
+ const x = 50 + i * (350 / Math.max(allComponents.length, 1));
2143
+ return /* @__PURE__ */ jsxs("g", { children: [
2144
+ /* @__PURE__ */ jsx(
2145
+ "circle",
2146
+ {
2147
+ cx: x,
2148
+ cy: 40,
2149
+ r: 20,
2150
+ fill: componentColors[comp],
2151
+ opacity: 0.8
2152
+ }
2153
+ ),
2154
+ /* @__PURE__ */ jsx(
2155
+ "text",
2156
+ {
2157
+ x,
2158
+ y: 75,
2159
+ textAnchor: "middle",
2160
+ fill: "#888",
2161
+ fontSize: "10",
2162
+ children: comp.length > 10 ? comp.slice(0, 10) + "..." : comp
2163
+ }
2164
+ )
2165
+ ] }, comp);
2166
+ }),
2167
+ displayNodes.slice(0, 8).map((node, i) => {
2168
+ const x = 50 + i * (350 / Math.max(displayNodes.length, 1));
2169
+ const isCoupled = node.components.size > 1;
2170
+ return /* @__PURE__ */ jsxs("g", { children: [
2171
+ Array.from(node.components).map((comp) => {
2172
+ const compIndex = allComponents.indexOf(comp);
2173
+ const compX = 50 + compIndex * (350 / Math.max(allComponents.length, 1));
2174
+ return /* @__PURE__ */ jsx(
2175
+ "line",
2176
+ {
2177
+ x1: compX,
2178
+ y1: 60,
2179
+ x2: x,
2180
+ y2: 180,
2181
+ stroke: componentColors[comp],
2182
+ strokeWidth: isCoupled ? 2 : 1,
2183
+ opacity: isCoupled ? 0.8 : 0.3
2184
+ },
2185
+ comp
2186
+ );
2187
+ }),
2188
+ /* @__PURE__ */ jsx(
2189
+ "rect",
2190
+ {
2191
+ x: x - 25,
2192
+ y: 180,
2193
+ width: 50,
2194
+ height: 24,
2195
+ rx: 4,
2196
+ fill: isCoupled ? "#ef444430" : "#16213e",
2197
+ stroke: isCoupled ? "#ef4444" : "#333"
2198
+ }
2199
+ ),
2200
+ /* @__PURE__ */ jsx(
2201
+ "text",
2202
+ {
2203
+ x,
2204
+ y: 196,
2205
+ textAnchor: "middle",
2206
+ fill: isCoupled ? "#fca5a5" : "#888",
2207
+ fontSize: "8",
2208
+ children: node.path.length > 8 ? node.path.slice(-8) : node.path
2209
+ }
2210
+ )
2211
+ ] }, node.path);
2212
+ }),
2213
+ displayNodes.length > 8 && /* @__PURE__ */ jsxs("text", { x: 200, y: 250, textAnchor: "middle", fill: "#666", fontSize: "12", children: [
2214
+ "+",
2215
+ displayNodes.length - 8,
2216
+ " more paths..."
2217
+ ] })
2218
+ ] })
2219
+ ] }),
2220
+ /* @__PURE__ */ jsx("style", { children: styles })
2221
+ ] });
2222
+ }
2223
+ var styles = `
2224
+ .dependency-graph {
2225
+ background: #1a1a2e;
2226
+ color: #e0e0e0;
2227
+ font-family: system-ui, sans-serif;
2228
+ height: 100%;
2229
+ display: flex;
2230
+ flex-direction: column;
2231
+ }
2232
+
2233
+ .dg-header {
2234
+ display: flex;
2235
+ justify-content: space-between;
2236
+ align-items: center;
2237
+ padding: 0.75rem 1rem;
2238
+ background: #16213e;
2239
+ border-bottom: 1px solid #333;
2240
+ flex-wrap: wrap;
2241
+ gap: 0.5rem;
2242
+ }
2243
+
2244
+ .dg-title {
2245
+ font-weight: 600;
2246
+ color: #00d9ff;
2247
+ }
2248
+
2249
+ .dg-controls {
2250
+ display: flex;
2251
+ gap: 0.5rem;
2252
+ }
2253
+
2254
+ .dg-view-btn {
2255
+ padding: 0.375rem 0.75rem;
2256
+ border: 1px solid #333;
2257
+ border-radius: 6px;
2258
+ background: transparent;
2259
+ color: #888;
2260
+ font-size: 0.8rem;
2261
+ cursor: pointer;
2262
+ transition: all 0.2s;
2263
+ }
2264
+
2265
+ .dg-view-btn:hover {
2266
+ background: #16213e;
2267
+ color: #ccc;
2268
+ }
2269
+
2270
+ .dg-view-btn.active {
2271
+ background: #00d9ff20;
2272
+ border-color: #00d9ff;
2273
+ color: #00d9ff;
2274
+ }
2275
+
2276
+ .dg-legend {
2277
+ display: flex;
2278
+ flex-wrap: wrap;
2279
+ gap: 0.75rem;
2280
+ padding: 0.75rem 1rem;
2281
+ background: #0f0f23;
2282
+ border-bottom: 1px solid #333;
2283
+ }
2284
+
2285
+ .dg-legend-item {
2286
+ display: flex;
2287
+ align-items: center;
2288
+ gap: 0.375rem;
2289
+ font-size: 0.8rem;
2290
+ color: #888;
2291
+ }
2292
+
2293
+ .dg-legend-dot {
2294
+ width: 10px;
2295
+ height: 10px;
2296
+ border-radius: 50%;
2297
+ background: var(--comp-color);
2298
+ }
2299
+
2300
+ .dg-alert {
2301
+ display: flex;
2302
+ gap: 0.75rem;
2303
+ padding: 1rem;
2304
+ margin: 0.75rem;
2305
+ background: #ef444420;
2306
+ border: 1px solid #ef4444;
2307
+ border-radius: 8px;
2308
+ }
2309
+
2310
+ .dg-alert-icon {
2311
+ font-size: 1.25rem;
2312
+ }
2313
+
2314
+ .dg-alert-content strong {
2315
+ color: #fca5a5;
2316
+ display: block;
2317
+ margin-bottom: 0.25rem;
2318
+ }
2319
+
2320
+ .dg-alert-content p {
2321
+ margin: 0;
2322
+ font-size: 0.875rem;
2323
+ color: #ccc;
2324
+ }
2325
+
2326
+ .dg-empty {
2327
+ flex: 1;
2328
+ display: flex;
2329
+ flex-direction: column;
2330
+ align-items: center;
2331
+ justify-content: center;
2332
+ color: #888;
2333
+ }
2334
+
2335
+ .dg-empty-icon {
2336
+ font-size: 2.5rem;
2337
+ margin-bottom: 0.5rem;
2338
+ }
2339
+
2340
+ .dg-paths {
2341
+ flex: 1;
2342
+ overflow: auto;
2343
+ padding: 0.75rem;
2344
+ }
2345
+
2346
+ .dg-path {
2347
+ background: #16213e;
2348
+ border-radius: 8px;
2349
+ padding: 0.875rem 1rem;
2350
+ margin-bottom: 0.625rem;
2351
+ cursor: pointer;
2352
+ border: 1px solid transparent;
2353
+ transition: all 0.2s;
2354
+ }
2355
+
2356
+ .dg-path:hover, .dg-path.hovered {
2357
+ border-color: #333;
2358
+ background: #1a2744;
2359
+ }
2360
+
2361
+ .dg-path.selected {
2362
+ border-color: #00d9ff;
2363
+ box-shadow: 0 0 0 2px #00d9ff30;
2364
+ }
2365
+
2366
+ .dg-path--coupled {
2367
+ border-left: 3px solid #ef4444;
2368
+ }
2369
+
2370
+ .dg-path-header {
2371
+ display: flex;
2372
+ justify-content: space-between;
2373
+ align-items: center;
2374
+ margin-bottom: 0.5rem;
2375
+ }
2376
+
2377
+ .dg-path-name {
2378
+ font-family: 'Fira Code', monospace;
2379
+ color: #a78bfa;
2380
+ font-size: 0.875rem;
2381
+ }
2382
+
2383
+ .dg-path-count {
2384
+ font-size: 0.75rem;
2385
+ color: #666;
2386
+ }
2387
+
2388
+ .dg-path-components {
2389
+ display: flex;
2390
+ flex-wrap: wrap;
2391
+ gap: 0.375rem;
2392
+ }
2393
+
2394
+ .dg-component-tag {
2395
+ padding: 0.25rem 0.5rem;
2396
+ border-radius: 4px;
2397
+ font-size: 0.75rem;
2398
+ border: 1px solid;
2399
+ }
2400
+
2401
+ .dg-coupling-warning {
2402
+ display: flex;
2403
+ align-items: center;
2404
+ gap: 0.375rem;
2405
+ margin-top: 0.625rem;
2406
+ padding: 0.375rem 0.5rem;
2407
+ border-radius: 4px;
2408
+ font-size: 0.75rem;
2409
+ }
2410
+
2411
+ .dg-coupling-critical {
2412
+ background: #ef444420;
2413
+ color: #fca5a5;
2414
+ }
2415
+
2416
+ .dg-coupling-warning {
2417
+ background: #fbbf2420;
2418
+ color: #fcd34d;
2419
+ }
2420
+
2421
+ .dg-visual {
2422
+ padding: 1rem;
2423
+ border-top: 1px solid #333;
2424
+ }
2425
+
2426
+ .dg-visual-title {
2427
+ font-size: 0.8rem;
2428
+ color: #888;
2429
+ margin-bottom: 0.5rem;
2430
+ }
2431
+
2432
+ .dg-svg {
2433
+ width: 100%;
2434
+ max-height: 200px;
2435
+ background: #0f0f23;
2436
+ border-radius: 8px;
2437
+ }
2438
+ `;
2439
+ var severityIcons = {
2440
+ critical: "\u{1F534}",
2441
+ warning: "\u{1F7E1}",
2442
+ info: "\u{1F7E2}"
2443
+ };
2444
+ var categoryIcons = {
2445
+ "state-loss": "\u{1F4E4}",
2446
+ "invalid-value": "\u26A0\uFE0F",
2447
+ "type-change": "\u{1F504}",
2448
+ "no-op-update": "\u267B\uFE0F",
2449
+ "excessive-updates": "\u26A1",
2450
+ "broken-invariant": "\u{1F6AB}",
2451
+ "hidden-coupling": "\u{1F517}",
2452
+ "temporal-anomaly": "\u23F0",
2453
+ "state-corruption": "\u{1F4A5}"
2454
+ };
2455
+ var categoryLabels = {
2456
+ "state-loss": "State Loss",
2457
+ "invalid-value": "Invalid Value",
2458
+ "type-change": "Type Change",
2459
+ "no-op-update": "Redundant Update",
2460
+ "excessive-updates": "Excessive Updates",
2461
+ "broken-invariant": "Broken Invariant",
2462
+ "hidden-coupling": "Hidden Coupling",
2463
+ "temporal-anomaly": "Temporal Anomaly",
2464
+ "state-corruption": "State Corruption"
2465
+ };
2466
+ function IssuesPanel({
2467
+ issues,
2468
+ onJumpToMutation,
2469
+ onSelectIssue,
2470
+ selectedIssueId,
2471
+ className = ""
2472
+ }) {
2473
+ const [severityFilter, setSeverityFilter] = useState("all");
2474
+ const [categoryFilter, setCategoryFilter] = useState("all");
2475
+ const [expandedIssue, setExpandedIssue] = useState(null);
2476
+ const filteredIssues = useMemo(() => {
2477
+ return issues.filter((issue) => {
2478
+ if (severityFilter !== "all" && issue.severity !== severityFilter) return false;
2479
+ if (categoryFilter !== "all" && issue.category !== categoryFilter) return false;
2480
+ return true;
2481
+ });
2482
+ }, [issues, severityFilter, categoryFilter]);
2483
+ const issueSummary = useMemo(() => {
2484
+ const summary = { critical: 0, warning: 0, info: 0 };
2485
+ for (const issue of issues) {
2486
+ summary[issue.severity]++;
2487
+ }
2488
+ return summary;
2489
+ }, [issues]);
2490
+ const availableCategories = useMemo(() => {
2491
+ return Array.from(new Set(issues.map((i) => i.category)));
2492
+ }, [issues]);
2493
+ const handleIssueClick = (issue) => {
2494
+ if (expandedIssue === issue.id) {
2495
+ setExpandedIssue(null);
2496
+ } else {
2497
+ setExpandedIssue(issue.id);
2498
+ onSelectIssue?.(issue);
2499
+ }
2500
+ };
2501
+ if (issues.length === 0) {
2502
+ return /* @__PURE__ */ jsxs("div", { className: `issues-panel issues-panel--empty ${className}`, children: [
2503
+ /* @__PURE__ */ jsxs("div", { className: "ip-empty", children: [
2504
+ /* @__PURE__ */ jsx("span", { className: "ip-empty-icon", children: "\u2705" }),
2505
+ /* @__PURE__ */ jsx("h3", { children: "No Issues Detected" }),
2506
+ /* @__PURE__ */ jsx("p", { children: "Your state timeline looks clean!" })
2507
+ ] }),
2508
+ /* @__PURE__ */ jsx("style", { children: styles2 })
2509
+ ] });
2510
+ }
2511
+ return /* @__PURE__ */ jsxs("div", { className: `issues-panel ${className}`, children: [
2512
+ /* @__PURE__ */ jsxs("div", { className: "ip-summary", children: [
2513
+ /* @__PURE__ */ jsxs("div", { className: "ip-summary-title", children: [
2514
+ /* @__PURE__ */ jsx("span", { className: "ip-summary-icon", children: "\u{1F6A8}" }),
2515
+ /* @__PURE__ */ jsxs("span", { children: [
2516
+ issues.length,
2517
+ " Issue",
2518
+ issues.length !== 1 ? "s" : "",
2519
+ " Detected"
2520
+ ] })
2521
+ ] }),
2522
+ /* @__PURE__ */ jsxs("div", { className: "ip-summary-counts", children: [
2523
+ issueSummary.critical > 0 && /* @__PURE__ */ jsxs("span", { className: "ip-count ip-count--critical", children: [
2524
+ severityIcons.critical,
2525
+ " ",
2526
+ issueSummary.critical
2527
+ ] }),
2528
+ issueSummary.warning > 0 && /* @__PURE__ */ jsxs("span", { className: "ip-count ip-count--warning", children: [
2529
+ severityIcons.warning,
2530
+ " ",
2531
+ issueSummary.warning
2532
+ ] }),
2533
+ issueSummary.info > 0 && /* @__PURE__ */ jsxs("span", { className: "ip-count ip-count--info", children: [
2534
+ severityIcons.info,
2535
+ " ",
2536
+ issueSummary.info
2537
+ ] })
2538
+ ] })
2539
+ ] }),
2540
+ /* @__PURE__ */ jsxs("div", { className: "ip-filters", children: [
2541
+ /* @__PURE__ */ jsxs(
2542
+ "select",
2543
+ {
2544
+ value: severityFilter,
2545
+ onChange: (e) => setSeverityFilter(e.target.value),
2546
+ className: "ip-filter",
2547
+ children: [
2548
+ /* @__PURE__ */ jsx("option", { value: "all", children: "All Severities" }),
2549
+ /* @__PURE__ */ jsx("option", { value: "critical", children: "\u{1F534} Critical" }),
2550
+ /* @__PURE__ */ jsx("option", { value: "warning", children: "\u{1F7E1} Warning" }),
2551
+ /* @__PURE__ */ jsx("option", { value: "info", children: "\u{1F7E2} Info" })
2552
+ ]
2553
+ }
2554
+ ),
2555
+ /* @__PURE__ */ jsxs(
2556
+ "select",
2557
+ {
2558
+ value: categoryFilter,
2559
+ onChange: (e) => setCategoryFilter(e.target.value),
2560
+ className: "ip-filter",
2561
+ children: [
2562
+ /* @__PURE__ */ jsx("option", { value: "all", children: "All Categories" }),
2563
+ availableCategories.map((cat) => /* @__PURE__ */ jsxs("option", { value: cat, children: [
2564
+ categoryIcons[cat],
2565
+ " ",
2566
+ categoryLabels[cat]
2567
+ ] }, cat))
2568
+ ]
2569
+ }
2570
+ )
2571
+ ] }),
2572
+ /* @__PURE__ */ jsx("div", { className: "ip-list", children: filteredIssues.map((issue) => /* @__PURE__ */ jsxs(
2573
+ "div",
2574
+ {
2575
+ className: `ip-issue ip-issue--${issue.severity} ${selectedIssueId === issue.id ? "selected" : ""} ${expandedIssue === issue.id ? "expanded" : ""}`,
2576
+ onClick: () => handleIssueClick(issue),
2577
+ children: [
2578
+ /* @__PURE__ */ jsxs("div", { className: "ip-issue-header", children: [
2579
+ /* @__PURE__ */ jsx("span", { className: "ip-issue-severity", children: severityIcons[issue.severity] }),
2580
+ /* @__PURE__ */ jsx("span", { className: "ip-issue-category", children: categoryIcons[issue.category] }),
2581
+ /* @__PURE__ */ jsx("span", { className: "ip-issue-title", children: issue.title }),
2582
+ /* @__PURE__ */ jsxs("span", { className: "ip-issue-index", children: [
2583
+ "#",
2584
+ issue.mutationIndex + 1
2585
+ ] })
2586
+ ] }),
2587
+ expandedIssue === issue.id && /* @__PURE__ */ jsxs("div", { className: "ip-issue-details", children: [
2588
+ /* @__PURE__ */ jsx("p", { className: "ip-issue-description", children: issue.description }),
2589
+ issue.path && /* @__PURE__ */ jsxs("div", { className: "ip-issue-field", children: [
2590
+ /* @__PURE__ */ jsx("span", { className: "ip-field-label", children: "Path:" }),
2591
+ /* @__PURE__ */ jsx("code", { className: "ip-field-value", children: issue.path })
2592
+ ] }),
2593
+ issue.previousValue !== void 0 && /* @__PURE__ */ jsxs("div", { className: "ip-issue-field", children: [
2594
+ /* @__PURE__ */ jsx("span", { className: "ip-field-label", children: "Before:" }),
2595
+ /* @__PURE__ */ jsx("code", { className: "ip-field-value ip-value-old", children: formatValue(issue.previousValue) })
2596
+ ] }),
2597
+ issue.currentValue !== void 0 && /* @__PURE__ */ jsxs("div", { className: "ip-issue-field", children: [
2598
+ /* @__PURE__ */ jsx("span", { className: "ip-field-label", children: "After:" }),
2599
+ /* @__PURE__ */ jsx("code", { className: `ip-field-value ip-value-new ${issue.severity === "critical" ? "ip-value-bad" : ""}`, children: formatValue(issue.currentValue) })
2600
+ ] }),
2601
+ issue.suggestion && /* @__PURE__ */ jsxs("div", { className: "ip-issue-suggestion", children: [
2602
+ /* @__PURE__ */ jsx("span", { className: "ip-suggestion-icon", children: "\u{1F4A1}" }),
2603
+ /* @__PURE__ */ jsx("span", { children: issue.suggestion })
2604
+ ] }),
2605
+ /* @__PURE__ */ jsx("div", { className: "ip-issue-actions", children: /* @__PURE__ */ jsx(
2606
+ "button",
2607
+ {
2608
+ className: "ip-action-btn",
2609
+ onClick: (e) => {
2610
+ e.stopPropagation();
2611
+ onJumpToMutation(issue.mutationId, issue.mutationIndex);
2612
+ },
2613
+ children: "\u{1F3AF} Jump to Mutation"
2614
+ }
2615
+ ) })
2616
+ ] })
2617
+ ]
2618
+ },
2619
+ issue.id
2620
+ )) }),
2621
+ /* @__PURE__ */ jsx("style", { children: styles2 })
2622
+ ] });
2623
+ }
2624
+ function formatValue(value) {
2625
+ if (value === void 0) return "undefined";
2626
+ if (value === null) return "null";
2627
+ if (typeof value === "number" && isNaN(value)) return "NaN";
2628
+ if (typeof value === "object") {
2629
+ const str = JSON.stringify(value);
2630
+ return str.length > 50 ? str.slice(0, 50) + "..." : str;
2631
+ }
2632
+ return String(value);
2633
+ }
2634
+ var styles2 = `
2635
+ .issues-panel {
2636
+ background: #1a1a2e;
2637
+ color: #e0e0e0;
2638
+ font-family: system-ui, sans-serif;
2639
+ height: 100%;
2640
+ display: flex;
2641
+ flex-direction: column;
2642
+ }
2643
+
2644
+ .issues-panel--empty {
2645
+ align-items: center;
2646
+ justify-content: center;
2647
+ }
2648
+
2649
+ .ip-empty {
2650
+ text-align: center;
2651
+ padding: 3rem;
2652
+ }
2653
+
2654
+ .ip-empty-icon {
2655
+ font-size: 4rem;
2656
+ display: block;
2657
+ margin-bottom: 1rem;
2658
+ }
2659
+
2660
+ .ip-empty h3 {
2661
+ margin: 0 0 0.5rem;
2662
+ color: #22c55e;
2663
+ }
2664
+
2665
+ .ip-empty p {
2666
+ margin: 0;
2667
+ color: #888;
2668
+ }
2669
+
2670
+ .ip-summary {
2671
+ display: flex;
2672
+ justify-content: space-between;
2673
+ align-items: center;
2674
+ padding: 1rem 1.25rem;
2675
+ background: linear-gradient(135deg, #ef444420, #fbbf2410);
2676
+ border-bottom: 1px solid #333;
2677
+ }
2678
+
2679
+ .ip-summary-title {
2680
+ display: flex;
2681
+ align-items: center;
2682
+ gap: 0.5rem;
2683
+ font-weight: 600;
2684
+ color: #fca5a5;
2685
+ }
2686
+
2687
+ .ip-summary-icon {
2688
+ font-size: 1.25rem;
2689
+ }
2690
+
2691
+ .ip-summary-counts {
2692
+ display: flex;
2693
+ gap: 0.75rem;
2694
+ }
2695
+
2696
+ .ip-count {
2697
+ padding: 0.25rem 0.75rem;
2698
+ border-radius: 12px;
2699
+ font-size: 0.875rem;
2700
+ font-weight: 600;
2701
+ }
2702
+
2703
+ .ip-count--critical { background: #ef444430; color: #fca5a5; }
2704
+ .ip-count--warning { background: #fbbf2430; color: #fcd34d; }
2705
+ .ip-count--info { background: #22c55e30; color: #86efac; }
2706
+
2707
+ .ip-filters {
2708
+ display: flex;
2709
+ gap: 0.75rem;
2710
+ padding: 0.75rem 1.25rem;
2711
+ background: #16213e;
2712
+ border-bottom: 1px solid #333;
2713
+ }
2714
+
2715
+ .ip-filter {
2716
+ flex: 1;
2717
+ padding: 0.5rem 0.75rem;
2718
+ border: 1px solid #333;
2719
+ border-radius: 6px;
2720
+ background: #0f0f23;
2721
+ color: #e0e0e0;
2722
+ font-size: 0.875rem;
2723
+ cursor: pointer;
2724
+ }
2725
+
2726
+ .ip-list {
2727
+ flex: 1;
2728
+ overflow: auto;
2729
+ padding: 0.75rem;
2730
+ }
2731
+
2732
+ .ip-issue {
2733
+ background: #16213e;
2734
+ border-radius: 8px;
2735
+ margin-bottom: 0.75rem;
2736
+ border-left: 4px solid;
2737
+ cursor: pointer;
2738
+ transition: all 0.2s;
2739
+ }
2740
+
2741
+ .ip-issue:hover {
2742
+ background: #1a2744;
2743
+ }
2744
+
2745
+ .ip-issue--critical { border-left-color: #ef4444; }
2746
+ .ip-issue--warning { border-left-color: #fbbf24; }
2747
+ .ip-issue--info { border-left-color: #22c55e; }
2748
+
2749
+ .ip-issue.selected {
2750
+ box-shadow: 0 0 0 2px #00d9ff40;
2751
+ }
2752
+
2753
+ .ip-issue-header {
2754
+ display: flex;
2755
+ align-items: center;
2756
+ gap: 0.625rem;
2757
+ padding: 0.875rem 1rem;
2758
+ }
2759
+
2760
+ .ip-issue-severity {
2761
+ font-size: 0.875rem;
2762
+ }
2763
+
2764
+ .ip-issue-category {
2765
+ font-size: 1rem;
2766
+ }
2767
+
2768
+ .ip-issue-title {
2769
+ flex: 1;
2770
+ font-size: 0.875rem;
2771
+ font-weight: 500;
2772
+ }
2773
+
2774
+ .ip-issue-index {
2775
+ font-size: 0.75rem;
2776
+ color: #666;
2777
+ font-family: 'Fira Code', monospace;
2778
+ }
2779
+
2780
+ .ip-issue-details {
2781
+ padding: 0 1rem 1rem;
2782
+ border-top: 1px solid #333;
2783
+ margin-top: 0.5rem;
2784
+ padding-top: 0.75rem;
2785
+ }
2786
+
2787
+ .ip-issue-description {
2788
+ margin: 0 0 1rem;
2789
+ font-size: 0.875rem;
2790
+ color: #ccc;
2791
+ line-height: 1.5;
2792
+ }
2793
+
2794
+ .ip-issue-field {
2795
+ display: flex;
2796
+ align-items: flex-start;
2797
+ gap: 0.75rem;
2798
+ margin-bottom: 0.5rem;
2799
+ font-size: 0.875rem;
2800
+ }
2801
+
2802
+ .ip-field-label {
2803
+ color: #888;
2804
+ min-width: 50px;
2805
+ }
2806
+
2807
+ .ip-field-value {
2808
+ font-family: 'Fira Code', monospace;
2809
+ padding: 0.25rem 0.5rem;
2810
+ background: #0f0f23;
2811
+ border-radius: 4px;
2812
+ font-size: 0.8rem;
2813
+ }
2814
+
2815
+ .ip-value-old {
2816
+ color: #fca5a5;
2817
+ background: #ef444420;
2818
+ }
2819
+
2820
+ .ip-value-new {
2821
+ color: #86efac;
2822
+ background: #22c55e20;
2823
+ }
2824
+
2825
+ .ip-value-bad {
2826
+ color: #fca5a5 !important;
2827
+ background: #ef444420 !important;
2828
+ }
2829
+
2830
+ .ip-issue-suggestion {
2831
+ display: flex;
2832
+ align-items: flex-start;
2833
+ gap: 0.5rem;
2834
+ margin-top: 1rem;
2835
+ padding: 0.75rem;
2836
+ background: #0f0f23;
2837
+ border-radius: 6px;
2838
+ font-size: 0.8rem;
2839
+ color: #a78bfa;
2840
+ }
2841
+
2842
+ .ip-suggestion-icon {
2843
+ flex-shrink: 0;
2844
+ }
2845
+
2846
+ .ip-issue-actions {
2847
+ margin-top: 1rem;
2848
+ display: flex;
2849
+ gap: 0.5rem;
2850
+ }
2851
+
2852
+ .ip-action-btn {
2853
+ padding: 0.5rem 1rem;
2854
+ border: 1px solid #333;
2855
+ border-radius: 6px;
2856
+ background: #0f0f23;
2857
+ color: #e0e0e0;
2858
+ font-size: 0.8rem;
2859
+ cursor: pointer;
2860
+ transition: all 0.2s;
2861
+ }
2862
+
2863
+ .ip-action-btn:hover {
2864
+ background: #16213e;
2865
+ border-color: #00d9ff;
2866
+ color: #00d9ff;
2867
+ }
2868
+ `;
2869
+ function getValueType(value) {
2870
+ if (value === null) return "null";
2871
+ if (value === void 0) return "undefined";
2872
+ if (typeof value === "number" && isNaN(value)) return "nan";
2873
+ if (Array.isArray(value)) return "array";
2874
+ if (typeof value === "function") return "function";
2875
+ return typeof value;
2876
+ }
2877
+ var typeIcons = {
2878
+ null: "\u2298",
2879
+ undefined: "\u2753",
2880
+ boolean: "\u25C9",
2881
+ number: "#",
2882
+ string: '"',
2883
+ array: "[]",
2884
+ object: "{}",
2885
+ nan: "\u26A0\uFE0F",
2886
+ function: "\u0192"
2887
+ };
2888
+ var typeColors = {
2889
+ null: "#f472b6",
2890
+ undefined: "#ef4444",
2891
+ boolean: "#fb923c",
2892
+ number: "#22c55e",
2893
+ string: "#fbbf24",
2894
+ array: "#a78bfa",
2895
+ object: "#60a5fa",
2896
+ nan: "#ef4444",
2897
+ function: "#888"
2898
+ };
2899
+ function StateTreeViewer({
2900
+ state,
2901
+ issuePaths = /* @__PURE__ */ new Set(),
2902
+ changedPaths = /* @__PURE__ */ new Set(),
2903
+ removedPaths = /* @__PURE__ */ new Set(),
2904
+ defaultExpandDepth = 3,
2905
+ searchQuery = "",
2906
+ onPathClick,
2907
+ className = ""
2908
+ }) {
2909
+ const [expandedPaths, setExpandedPaths] = useState(/* @__PURE__ */ new Set());
2910
+ const [copiedPath, setCopiedPath] = useState(null);
2911
+ const toggleExpand = useCallback((path) => {
2912
+ setExpandedPaths((prev) => {
2913
+ const next = new Set(prev);
2914
+ if (next.has(path)) {
2915
+ next.delete(path);
2916
+ } else {
2917
+ next.add(path);
2918
+ }
2919
+ return next;
2920
+ });
2921
+ }, []);
2922
+ const copyPath = useCallback((path, e) => {
2923
+ e.stopPropagation();
2924
+ navigator.clipboard.writeText(path);
2925
+ setCopiedPath(path);
2926
+ setTimeout(() => setCopiedPath(null), 2e3);
2927
+ }, []);
2928
+ const copyValue = useCallback((value, e) => {
2929
+ e.stopPropagation();
2930
+ const text = typeof value === "object" ? JSON.stringify(value, null, 2) : String(value);
2931
+ navigator.clipboard.writeText(text);
2932
+ }, []);
2933
+ const matchingPaths = useMemo(() => {
2934
+ if (!searchQuery) return /* @__PURE__ */ new Set();
2935
+ const matches = /* @__PURE__ */ new Set();
2936
+ function traverse(obj, path) {
2937
+ if (!obj || typeof obj !== "object") return;
2938
+ for (const key of Object.keys(obj)) {
2939
+ const fullPath = path ? `${path}.${key}` : key;
2940
+ if (fullPath.toLowerCase().includes(searchQuery.toLowerCase())) {
2941
+ matches.add(fullPath);
2942
+ const parts = fullPath.split(".");
2943
+ for (let i = 1; i < parts.length; i++) {
2944
+ matches.add(parts.slice(0, i).join("."));
2945
+ }
2946
+ }
2947
+ traverse(obj[key], fullPath);
2948
+ }
2949
+ }
2950
+ traverse(state, "");
2951
+ return matches;
2952
+ }, [state, searchQuery]);
2953
+ return /* @__PURE__ */ jsxs("div", { className: `state-tree-viewer ${className}`, children: [
2954
+ /* @__PURE__ */ jsxs("div", { className: "stv-header", children: [
2955
+ /* @__PURE__ */ jsx("span", { className: "stv-title", children: "\u{1F333} State Tree" }),
2956
+ /* @__PURE__ */ jsxs("div", { className: "stv-legend", children: [
2957
+ /* @__PURE__ */ jsx("span", { className: "stv-legend-item stv-legend-changed", children: "\u25CF Changed" }),
2958
+ /* @__PURE__ */ jsx("span", { className: "stv-legend-item stv-legend-removed", children: "\u25CF Removed" }),
2959
+ /* @__PURE__ */ jsx("span", { className: "stv-legend-item stv-legend-issue", children: "\u25CF Issue" })
2960
+ ] })
2961
+ ] }),
2962
+ /* @__PURE__ */ jsx("div", { className: "stv-tree", children: /* @__PURE__ */ jsx(
2963
+ TreeNode,
2964
+ {
2965
+ value: state,
2966
+ path: "",
2967
+ depth: 0,
2968
+ issuePaths,
2969
+ changedPaths,
2970
+ removedPaths,
2971
+ expandedPaths,
2972
+ matchingPaths,
2973
+ defaultExpandDepth,
2974
+ searchQuery,
2975
+ copiedPath,
2976
+ onToggle: toggleExpand,
2977
+ onCopyPath: copyPath,
2978
+ onCopyValue: copyValue,
2979
+ onPathClick
2980
+ }
2981
+ ) }),
2982
+ /* @__PURE__ */ jsx("style", { children: styles3 })
2983
+ ] });
2984
+ }
2985
+ function TreeNode({
2986
+ value,
2987
+ path,
2988
+ depth,
2989
+ keyName,
2990
+ issuePaths,
2991
+ changedPaths,
2992
+ removedPaths,
2993
+ expandedPaths,
2994
+ matchingPaths,
2995
+ defaultExpandDepth,
2996
+ searchQuery,
2997
+ copiedPath,
2998
+ onToggle,
2999
+ onCopyPath,
3000
+ onCopyValue,
3001
+ onPathClick
3002
+ }) {
3003
+ const valueType = getValueType(value);
3004
+ const hasIssue = path ? issuePaths.has(path) : false;
3005
+ const isChanged = path ? changedPaths.has(path) : false;
3006
+ const isRemoved = path ? removedPaths.has(path) : false;
3007
+ const isMatch = path ? matchingPaths.has(path) : false;
3008
+ const isExpandable = valueType === "object" || valueType === "array";
3009
+ const isExpanded = expandedPaths.has(path) || depth < defaultExpandDepth && !expandedPaths.has(path) || isMatch;
3010
+ const entries = isExpandable && value ? Object.entries(value) : [];
3011
+ const handleClick = () => {
3012
+ if (isExpandable) {
3013
+ onToggle(path);
3014
+ } else if (onPathClick) {
3015
+ onPathClick(path, value);
3016
+ }
3017
+ };
3018
+ const highlightKey = (key) => {
3019
+ if (!searchQuery) return key;
3020
+ const idx = key.toLowerCase().indexOf(searchQuery.toLowerCase());
3021
+ if (idx === -1) return key;
3022
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
3023
+ key.slice(0, idx),
3024
+ /* @__PURE__ */ jsx("span", { className: "stv-highlight", children: key.slice(idx, idx + searchQuery.length) }),
3025
+ key.slice(idx + searchQuery.length)
3026
+ ] });
3027
+ };
3028
+ return /* @__PURE__ */ jsxs("div", { className: `stv-node ${depth === 0 ? "stv-root" : ""}`, children: [
3029
+ /* @__PURE__ */ jsxs(
3030
+ "div",
3031
+ {
3032
+ className: `stv-row ${isExpandable ? "stv-expandable" : ""} ${hasIssue ? "stv-has-issue" : ""} ${isChanged ? "stv-changed" : ""} ${isRemoved ? "stv-removed" : ""} ${isMatch ? "stv-match" : ""}`,
3033
+ onClick: handleClick,
3034
+ children: [
3035
+ isExpandable ? /* @__PURE__ */ jsx("span", { className: "stv-toggle", children: isExpanded ? "\u25BC" : "\u25B6" }) : /* @__PURE__ */ jsx("span", { className: "stv-toggle stv-toggle-spacer" }),
3036
+ keyName !== void 0 && /* @__PURE__ */ jsxs("span", { className: "stv-key", children: [
3037
+ highlightKey(keyName),
3038
+ /* @__PURE__ */ jsx("span", { className: "stv-colon", children: ":" })
3039
+ ] }),
3040
+ /* @__PURE__ */ jsx(
3041
+ "span",
3042
+ {
3043
+ className: "stv-type-icon",
3044
+ style: { color: typeColors[valueType] },
3045
+ title: valueType,
3046
+ children: typeIcons[valueType]
3047
+ }
3048
+ ),
3049
+ /* @__PURE__ */ jsx("span", { className: `stv-value stv-value-${valueType}`, children: renderValue(value, valueType, isExpanded) }),
3050
+ /* @__PURE__ */ jsxs("span", { className: "stv-badges", children: [
3051
+ hasIssue && /* @__PURE__ */ jsx("span", { className: "stv-badge stv-badge-issue", title: "Has issue", children: "\u26A0\uFE0F" }),
3052
+ isChanged && /* @__PURE__ */ jsx("span", { className: "stv-badge stv-badge-changed", title: "Changed", children: "\u25CF" }),
3053
+ isRemoved && /* @__PURE__ */ jsx("span", { className: "stv-badge stv-badge-removed", title: "Removed", children: "\u2715" }),
3054
+ valueType === "nan" && /* @__PURE__ */ jsx("span", { className: "stv-badge stv-badge-nan", children: "NaN!" }),
3055
+ valueType === "undefined" && /* @__PURE__ */ jsx("span", { className: "stv-badge stv-badge-undefined", children: "undef" })
3056
+ ] }),
3057
+ /* @__PURE__ */ jsxs("span", { className: "stv-actions", children: [
3058
+ /* @__PURE__ */ jsx(
3059
+ "button",
3060
+ {
3061
+ className: "stv-action",
3062
+ onClick: (e) => onCopyPath(path, e),
3063
+ title: "Copy path",
3064
+ children: copiedPath === path ? "\u2713" : "\u{1F4CB}"
3065
+ }
3066
+ ),
3067
+ /* @__PURE__ */ jsx(
3068
+ "button",
3069
+ {
3070
+ className: "stv-action",
3071
+ onClick: (e) => onCopyValue(value, e),
3072
+ title: "Copy value",
3073
+ children: "\u{1F4C4}"
3074
+ }
3075
+ )
3076
+ ] })
3077
+ ]
3078
+ }
3079
+ ),
3080
+ isExpandable && isExpanded && entries.length > 0 && /* @__PURE__ */ jsx("div", { className: "stv-children", children: entries.map(([childKey, childValue]) => {
3081
+ const childPath = path ? `${path}.${childKey}` : childKey;
3082
+ return /* @__PURE__ */ jsx(
3083
+ TreeNode,
3084
+ {
3085
+ value: childValue,
3086
+ path: childPath,
3087
+ depth: depth + 1,
3088
+ keyName: childKey,
3089
+ issuePaths,
3090
+ changedPaths,
3091
+ removedPaths,
3092
+ expandedPaths,
3093
+ matchingPaths,
3094
+ defaultExpandDepth,
3095
+ searchQuery,
3096
+ copiedPath,
3097
+ onToggle,
3098
+ onCopyPath,
3099
+ onCopyValue,
3100
+ onPathClick
3101
+ },
3102
+ childPath
3103
+ );
3104
+ }) }),
3105
+ isExpandable && isExpanded && entries.length === 0 && /* @__PURE__ */ jsx("div", { className: "stv-children", children: /* @__PURE__ */ jsx("span", { className: "stv-empty", children: "(empty)" }) })
3106
+ ] });
3107
+ }
3108
+ function renderValue(value, valueType, isExpanded) {
3109
+ switch (valueType) {
3110
+ case "null":
3111
+ return "null";
3112
+ case "undefined":
3113
+ return "undefined";
3114
+ case "nan":
3115
+ return "NaN";
3116
+ case "boolean":
3117
+ return String(value);
3118
+ case "number":
3119
+ return String(value);
3120
+ case "string":
3121
+ const str = value;
3122
+ const display = str.length > 50 ? str.slice(0, 50) + "..." : str;
3123
+ return `"${display}"`;
3124
+ case "array":
3125
+ if (!isExpanded) {
3126
+ const arr = value;
3127
+ return `Array(${arr.length})`;
3128
+ }
3129
+ return "";
3130
+ case "object":
3131
+ if (!isExpanded) {
3132
+ const obj = value;
3133
+ const keys = Object.keys(obj);
3134
+ return `{${keys.length} keys}`;
3135
+ }
3136
+ return "";
3137
+ case "function":
3138
+ return "function()";
3139
+ default:
3140
+ return String(value);
3141
+ }
3142
+ }
3143
+ var styles3 = `
3144
+ .state-tree-viewer {
3145
+ background: #1a1a2e;
3146
+ color: #e0e0e0;
3147
+ font-family: system-ui, sans-serif;
3148
+ height: 100%;
3149
+ display: flex;
3150
+ flex-direction: column;
3151
+ }
3152
+
3153
+ .stv-header {
3154
+ display: flex;
3155
+ justify-content: space-between;
3156
+ align-items: center;
3157
+ padding: 0.75rem 1rem;
3158
+ background: #16213e;
3159
+ border-bottom: 1px solid #333;
3160
+ }
3161
+
3162
+ .stv-title {
3163
+ font-weight: 600;
3164
+ color: #00d9ff;
3165
+ }
3166
+
3167
+ .stv-legend {
3168
+ display: flex;
3169
+ gap: 1rem;
3170
+ font-size: 0.75rem;
3171
+ }
3172
+
3173
+ .stv-legend-item {
3174
+ opacity: 0.7;
3175
+ }
3176
+
3177
+ .stv-legend-changed { color: #fbbf24; }
3178
+ .stv-legend-removed { color: #ef4444; }
3179
+ .stv-legend-issue { color: #f472b6; }
3180
+
3181
+ .stv-tree {
3182
+ flex: 1;
3183
+ overflow: auto;
3184
+ padding: 0.75rem;
3185
+ font-family: 'Fira Code', 'Consolas', monospace;
3186
+ font-size: 0.875rem;
3187
+ }
3188
+
3189
+ .stv-node {
3190
+ line-height: 1.6;
3191
+ }
3192
+
3193
+ .stv-row {
3194
+ display: flex;
3195
+ align-items: center;
3196
+ padding: 0.125rem 0.25rem;
3197
+ border-radius: 4px;
3198
+ cursor: default;
3199
+ }
3200
+
3201
+ .stv-row:hover {
3202
+ background: #16213e;
3203
+ }
3204
+
3205
+ .stv-expandable {
3206
+ cursor: pointer;
3207
+ }
3208
+
3209
+ .stv-toggle {
3210
+ width: 1rem;
3211
+ font-size: 0.7rem;
3212
+ color: #666;
3213
+ }
3214
+
3215
+ .stv-toggle-spacer {
3216
+ visibility: hidden;
3217
+ }
3218
+
3219
+ .stv-key {
3220
+ color: #a78bfa;
3221
+ margin-right: 0.375rem;
3222
+ }
3223
+
3224
+ .stv-colon {
3225
+ color: #666;
3226
+ }
3227
+
3228
+ .stv-type-icon {
3229
+ font-size: 0.75rem;
3230
+ width: 1.25rem;
3231
+ text-align: center;
3232
+ opacity: 0.7;
3233
+ }
3234
+
3235
+ .stv-value {
3236
+ flex: 1;
3237
+ }
3238
+
3239
+ .stv-value-null { color: #f472b6; font-style: italic; }
3240
+ .stv-value-undefined { color: #ef4444; font-style: italic; }
3241
+ .stv-value-nan { color: #ef4444; font-weight: bold; }
3242
+ .stv-value-boolean { color: #fb923c; }
3243
+ .stv-value-number { color: #22c55e; }
3244
+ .stv-value-string { color: #fbbf24; }
3245
+ .stv-value-array { color: #a78bfa; }
3246
+ .stv-value-object { color: #60a5fa; }
3247
+ .stv-value-function { color: #888; font-style: italic; }
3248
+
3249
+ .stv-badges {
3250
+ display: flex;
3251
+ gap: 0.25rem;
3252
+ margin-left: 0.5rem;
3253
+ }
3254
+
3255
+ .stv-badge {
3256
+ font-size: 0.7rem;
3257
+ padding: 0 0.25rem;
3258
+ border-radius: 3px;
3259
+ }
3260
+
3261
+ .stv-badge-issue {
3262
+ background: #f472b640;
3263
+ }
3264
+
3265
+ .stv-badge-changed {
3266
+ color: #fbbf24;
3267
+ }
3268
+
3269
+ .stv-badge-removed {
3270
+ color: #ef4444;
3271
+ background: #ef444430;
3272
+ padding: 0 0.375rem;
3273
+ }
3274
+
3275
+ .stv-badge-nan, .stv-badge-undefined {
3276
+ background: #ef444440;
3277
+ color: #fca5a5;
3278
+ padding: 0.125rem 0.375rem;
3279
+ font-weight: 600;
3280
+ }
3281
+
3282
+ .stv-actions {
3283
+ display: none;
3284
+ gap: 0.25rem;
3285
+ margin-left: 0.5rem;
3286
+ }
3287
+
3288
+ .stv-row:hover .stv-actions {
3289
+ display: flex;
3290
+ }
3291
+
3292
+ .stv-action {
3293
+ padding: 0.125rem 0.375rem;
3294
+ border: none;
3295
+ background: transparent;
3296
+ color: #666;
3297
+ cursor: pointer;
3298
+ font-size: 0.75rem;
3299
+ border-radius: 3px;
3300
+ }
3301
+
3302
+ .stv-action:hover {
3303
+ background: #333;
3304
+ color: #fff;
3305
+ }
3306
+
3307
+ .stv-children {
3308
+ margin-left: 1.25rem;
3309
+ border-left: 1px solid #333;
3310
+ padding-left: 0.5rem;
3311
+ }
3312
+
3313
+ .stv-empty {
3314
+ color: #666;
3315
+ font-style: italic;
3316
+ font-size: 0.8rem;
3317
+ padding: 0.25rem 0;
3318
+ }
3319
+
3320
+ .stv-has-issue {
3321
+ background: #f472b620 !important;
3322
+ }
3323
+
3324
+ .stv-changed {
3325
+ background: #fbbf2420 !important;
3326
+ }
3327
+
3328
+ .stv-removed {
3329
+ background: #ef444420 !important;
3330
+ text-decoration: line-through;
3331
+ opacity: 0.7;
3332
+ }
3333
+
3334
+ .stv-match .stv-key {
3335
+ background: #fbbf2440;
3336
+ padding: 0 0.25rem;
3337
+ border-radius: 2px;
3338
+ }
3339
+
3340
+ .stv-highlight {
3341
+ background: #fbbf2480;
3342
+ padding: 0 0.125rem;
3343
+ border-radius: 2px;
3344
+ }
3345
+ `;
3346
+ var DashboardContext = React4.createContext(null);
1680
3347
  function DashboardProvider({
1681
3348
  serverUrl = "http://localhost:8080",
1682
3349
  children
@@ -1737,7 +3404,7 @@ function DashboardProvider({
1737
3404
  return /* @__PURE__ */ jsx(DashboardContext.Provider, { value, children });
1738
3405
  }
1739
3406
  function useDashboard() {
1740
- const context = React2.useContext(DashboardContext);
3407
+ const context = React4.useContext(DashboardContext);
1741
3408
  if (!context) {
1742
3409
  throw new Error("useDashboard must be used within a DashboardProvider");
1743
3410
  }
@@ -1789,8 +3456,37 @@ function DashboardContent() {
1789
3456
  loadSessions
1790
3457
  } = useDashboard();
1791
3458
  const playback = usePlayback(timeline);
1792
- const [activePanel, setActivePanel] = useState("diff");
3459
+ const [mainTab, setMainTab] = useState("issues");
3460
+ const [detailTab, setDetailTab] = useState("tree");
1793
3461
  const [autoRefresh, setAutoRefresh] = useState(true);
3462
+ const [searchQuery, setSearchQuery] = useState("");
3463
+ const analyzer2 = useMemo(() => new StateAnalyzer(), []);
3464
+ const issues = useMemo(() => {
3465
+ return analyzer2.analyzeTimeline(timeline);
3466
+ }, [analyzer2, timeline]);
3467
+ const dependencyGraph = useMemo(() => {
3468
+ return analyzer2.buildDependencyGraph(timeline);
3469
+ }, [analyzer2, timeline]);
3470
+ const issuePaths = useMemo(() => {
3471
+ return new Set(issues.filter((i) => i.path).map((i) => i.path));
3472
+ }, [issues]);
3473
+ const changedPaths = useMemo(() => {
3474
+ if (!selectedMutation?.diff) return /* @__PURE__ */ new Set();
3475
+ return new Set(selectedMutation.diff.map((d) => d.path));
3476
+ }, [selectedMutation]);
3477
+ const removedPaths = useMemo(() => {
3478
+ if (!selectedMutation?.diff) return /* @__PURE__ */ new Set();
3479
+ return new Set(
3480
+ selectedMutation.diff.filter((d) => d.operation === "REMOVE").map((d) => d.path)
3481
+ );
3482
+ }, [selectedMutation]);
3483
+ const issueCounts = useMemo(() => {
3484
+ const counts = { critical: 0, warning: 0, info: 0 };
3485
+ for (const issue of issues) {
3486
+ counts[issue.severity]++;
3487
+ }
3488
+ return counts;
3489
+ }, [issues]);
1794
3490
  useEffect(() => {
1795
3491
  if (playback.currentMutation) {
1796
3492
  selectMutation(playback.currentMutation);
@@ -1815,25 +3511,58 @@ function DashboardContent() {
1815
3511
  e.preventDefault();
1816
3512
  playback.isPlaying ? playback.pause() : playback.play();
1817
3513
  break;
3514
+ case "1":
3515
+ setMainTab("issues");
3516
+ break;
3517
+ case "2":
3518
+ setMainTab("timeline");
3519
+ break;
3520
+ case "3":
3521
+ setMainTab("graph");
3522
+ break;
1818
3523
  }
1819
3524
  };
1820
3525
  window.addEventListener("keydown", handleKeyDown);
1821
3526
  return () => window.removeEventListener("keydown", handleKeyDown);
1822
3527
  }, [playback]);
1823
- return /* @__PURE__ */ jsxs("div", { className: "surgeon-dashboard", children: [
1824
- /* @__PURE__ */ jsxs("header", { className: "sd-header", children: [
1825
- /* @__PURE__ */ jsx("div", { className: "sd-header-left", children: /* @__PURE__ */ jsxs("h1", { className: "sd-logo", children: [
1826
- /* @__PURE__ */ jsx("span", { className: "sd-logo-icon", children: "\u{1F52C}" }),
1827
- /* @__PURE__ */ jsx("span", { className: "sd-logo-text", children: "State Surgeon" }),
1828
- /* @__PURE__ */ jsx("span", { className: "sd-version", children: "v1.1.0" })
1829
- ] }) }),
1830
- /* @__PURE__ */ jsx("div", { className: "sd-header-center", children: /* @__PURE__ */ jsxs(
3528
+ const handleJumpToMutation = (mutationId, index) => {
3529
+ playback.goTo(index);
3530
+ const mutation = timeline[index];
3531
+ if (mutation) {
3532
+ selectMutation(mutation);
3533
+ }
3534
+ setMainTab("timeline");
3535
+ };
3536
+ return /* @__PURE__ */ jsxs("div", { className: "surgeon-dashboard-v2", children: [
3537
+ /* @__PURE__ */ jsxs("header", { className: "sd2-header", children: [
3538
+ /* @__PURE__ */ jsxs("div", { className: "sd2-header-left", children: [
3539
+ /* @__PURE__ */ jsxs("h1", { className: "sd2-logo", children: [
3540
+ /* @__PURE__ */ jsx("span", { className: "sd2-logo-icon", children: "\u{1F52C}" }),
3541
+ /* @__PURE__ */ jsx("span", { className: "sd2-logo-text", children: "State Surgeon" }),
3542
+ /* @__PURE__ */ jsx("span", { className: "sd2-version", children: "v2.0" })
3543
+ ] }),
3544
+ issues.length > 0 && /* @__PURE__ */ jsxs("div", { className: "sd2-issue-badges", children: [
3545
+ issueCounts.critical > 0 && /* @__PURE__ */ jsxs("span", { className: "sd2-badge sd2-badge-critical", children: [
3546
+ "\u{1F534} ",
3547
+ issueCounts.critical
3548
+ ] }),
3549
+ issueCounts.warning > 0 && /* @__PURE__ */ jsxs("span", { className: "sd2-badge sd2-badge-warning", children: [
3550
+ "\u{1F7E1} ",
3551
+ issueCounts.warning
3552
+ ] }),
3553
+ issueCounts.info > 0 && /* @__PURE__ */ jsxs("span", { className: "sd2-badge sd2-badge-info", children: [
3554
+ "\u{1F7E2} ",
3555
+ issueCounts.info
3556
+ ] })
3557
+ ] })
3558
+ ] }),
3559
+ /* @__PURE__ */ jsx("div", { className: "sd2-header-center", children: /* @__PURE__ */ jsxs(
1831
3560
  "select",
1832
3561
  {
1833
3562
  value: currentSession?.id || "",
1834
3563
  onChange: (e) => selectSession(e.target.value),
1835
3564
  disabled: isLoading,
1836
- className: "sd-session-select",
3565
+ className: "sd2-session-select",
1837
3566
  children: [
1838
3567
  /* @__PURE__ */ jsx("option", { value: "", children: "Select a session..." }),
1839
3568
  sessions.map((session) => /* @__PURE__ */ jsxs("option", { value: session.id, children: [
@@ -1847,8 +3576,18 @@ function DashboardContent() {
1847
3576
  ]
1848
3577
  }
1849
3578
  ) }),
1850
- /* @__PURE__ */ jsxs("div", { className: "sd-header-right", children: [
1851
- /* @__PURE__ */ jsxs("label", { className: "sd-auto-refresh", children: [
3579
+ /* @__PURE__ */ jsxs("div", { className: "sd2-header-right", children: [
3580
+ /* @__PURE__ */ jsx(
3581
+ "input",
3582
+ {
3583
+ type: "text",
3584
+ placeholder: "\u{1F50D} Search state paths...",
3585
+ value: searchQuery,
3586
+ onChange: (e) => setSearchQuery(e.target.value),
3587
+ className: "sd2-search"
3588
+ }
3589
+ ),
3590
+ /* @__PURE__ */ jsxs("label", { className: "sd2-auto-refresh", children: [
1852
3591
  /* @__PURE__ */ jsx(
1853
3592
  "input",
1854
3593
  {
@@ -1857,109 +3596,177 @@ function DashboardContent() {
1857
3596
  onChange: (e) => setAutoRefresh(e.target.checked)
1858
3597
  }
1859
3598
  ),
1860
- "Auto-refresh"
3599
+ "Auto"
1861
3600
  ] }),
1862
- /* @__PURE__ */ jsx("button", { onClick: loadSessions, disabled: isLoading, className: "sd-refresh-btn", children: isLoading ? "\u23F3" : "\u{1F504}" })
3601
+ /* @__PURE__ */ jsx("button", { onClick: loadSessions, disabled: isLoading, className: "sd2-refresh-btn", children: isLoading ? "\u23F3" : "\u{1F504}" })
1863
3602
  ] })
1864
3603
  ] }),
1865
- error && /* @__PURE__ */ jsxs("div", { className: "sd-error", children: [
1866
- /* @__PURE__ */ jsx("span", { className: "sd-error-icon", children: "\u274C" }),
1867
- /* @__PURE__ */ jsx("p", { children: error })
3604
+ error && /* @__PURE__ */ jsxs("div", { className: "sd2-error", children: [
3605
+ /* @__PURE__ */ jsx("span", { children: "\u274C" }),
3606
+ " ",
3607
+ error
1868
3608
  ] }),
1869
- !currentSession && !isLoading && /* @__PURE__ */ jsx("div", { className: "sd-welcome", children: /* @__PURE__ */ jsxs("div", { className: "sd-welcome-content", children: [
1870
- /* @__PURE__ */ jsx("div", { className: "sd-welcome-icon", children: "\u{1F52C}" }),
1871
- /* @__PURE__ */ jsx("h2", { children: "Welcome to State Surgeon" }),
1872
- /* @__PURE__ */ jsx("p", { children: "A forensic debugging platform for JavaScript applications" }),
1873
- sessions.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "sd-welcome-steps", children: [
1874
- /* @__PURE__ */ jsx("h3", { children: "Getting Started" }),
1875
- /* @__PURE__ */ jsxs("ol", { children: [
1876
- /* @__PURE__ */ jsxs("li", { children: [
1877
- /* @__PURE__ */ jsx("strong", { children: "Install the client in your app:" }),
1878
- /* @__PURE__ */ jsx("pre", { children: `import { StateSurgeonClient, instrumentReact } from 'state-surgeon/instrument';
1879
-
1880
- const client = new StateSurgeonClient({ appId: 'my-app' });
1881
- instrumentReact(React);` })
1882
- ] }),
1883
- /* @__PURE__ */ jsxs("li", { children: [
1884
- /* @__PURE__ */ jsx("strong", { children: "Interact with your application" }),
1885
- " to generate state mutations"
1886
- ] }),
1887
- /* @__PURE__ */ jsxs("li", { children: [
1888
- /* @__PURE__ */ jsx("strong", { children: "Select a session above" }),
1889
- " to start debugging"
1890
- ] })
3609
+ !currentSession && !isLoading && /* @__PURE__ */ jsx("div", { className: "sd2-welcome", children: /* @__PURE__ */ jsxs("div", { className: "sd2-welcome-content", children: [
3610
+ /* @__PURE__ */ jsx("div", { className: "sd2-welcome-icon", children: "\u{1F52C}" }),
3611
+ /* @__PURE__ */ jsx("h2", { children: "State Surgeon v2.0" }),
3612
+ /* @__PURE__ */ jsx("p", { children: "Forensic debugging platform for JavaScript applications" }),
3613
+ /* @__PURE__ */ jsxs("div", { className: "sd2-features", children: [
3614
+ /* @__PURE__ */ jsxs("div", { className: "sd2-feature", children: [
3615
+ /* @__PURE__ */ jsx("span", { className: "sd2-feature-icon", children: "\u{1F6A8}" }),
3616
+ /* @__PURE__ */ jsx("strong", { children: "Issue Detection" }),
3617
+ /* @__PURE__ */ jsx("span", { children: "Find state loss, invalid values, broken invariants" })
3618
+ ] }),
3619
+ /* @__PURE__ */ jsxs("div", { className: "sd2-feature", children: [
3620
+ /* @__PURE__ */ jsx("span", { className: "sd2-feature-icon", children: "\u{1F333}" }),
3621
+ /* @__PURE__ */ jsx("strong", { children: "State Tree" }),
3622
+ /* @__PURE__ */ jsx("span", { children: "Visual tree with inline anomaly badges" })
3623
+ ] }),
3624
+ /* @__PURE__ */ jsxs("div", { className: "sd2-feature", children: [
3625
+ /* @__PURE__ */ jsx("span", { className: "sd2-feature-icon", children: "\u{1F517}" }),
3626
+ /* @__PURE__ */ jsx("strong", { children: "Dependency Graph" }),
3627
+ /* @__PURE__ */ jsx("span", { children: "Detect hidden coupling between components" })
3628
+ ] }),
3629
+ /* @__PURE__ */ jsxs("div", { className: "sd2-feature", children: [
3630
+ /* @__PURE__ */ jsx("span", { className: "sd2-feature-icon", children: "\u23F1\uFE0F" }),
3631
+ /* @__PURE__ */ jsx("strong", { children: "Timeline Analysis" }),
3632
+ /* @__PURE__ */ jsx("span", { children: "See exactly what your code did" })
1891
3633
  ] })
1892
- ] }) : /* @__PURE__ */ jsxs("div", { className: "sd-welcome-sessions", children: [
3634
+ ] }),
3635
+ sessions.length > 0 && /* @__PURE__ */ jsxs("div", { className: "sd2-session-list", children: [
1893
3636
  /* @__PURE__ */ jsxs("h3", { children: [
1894
- "Available Sessions (",
3637
+ "Sessions (",
1895
3638
  sessions.length,
1896
3639
  ")"
1897
3640
  ] }),
1898
- /* @__PURE__ */ jsx("div", { className: "sd-session-list", children: sessions.map((session) => /* @__PURE__ */ jsxs(
3641
+ sessions.map((session) => /* @__PURE__ */ jsxs(
1899
3642
  "button",
1900
3643
  {
1901
- className: "sd-session-card",
3644
+ className: "sd2-session-card",
1902
3645
  onClick: () => selectSession(session.id),
1903
3646
  children: [
1904
- /* @__PURE__ */ jsx("span", { className: "sd-session-app", children: session.appId }),
1905
- /* @__PURE__ */ jsxs("span", { className: "sd-session-id", children: [
1906
- session.id.slice(0, 16),
1907
- "..."
1908
- ] }),
1909
- /* @__PURE__ */ jsxs("span", { className: "sd-session-count", children: [
3647
+ /* @__PURE__ */ jsx("span", { className: "sd2-session-app", children: session.appId }),
3648
+ /* @__PURE__ */ jsxs("span", { className: "sd2-session-count", children: [
1910
3649
  session.mutationCount,
1911
3650
  " mutations"
1912
3651
  ] })
1913
3652
  ]
1914
3653
  },
1915
3654
  session.id
1916
- )) })
3655
+ ))
1917
3656
  ] })
1918
3657
  ] }) }),
1919
- isLoading && !currentSession && /* @__PURE__ */ jsxs("div", { className: "sd-loading", children: [
1920
- /* @__PURE__ */ jsx("div", { className: "sd-spinner" }),
3658
+ isLoading && !currentSession && /* @__PURE__ */ jsxs("div", { className: "sd2-loading", children: [
3659
+ /* @__PURE__ */ jsx("div", { className: "sd2-spinner" }),
1921
3660
  /* @__PURE__ */ jsx("p", { children: "Loading sessions..." })
1922
3661
  ] }),
1923
- currentSession && /* @__PURE__ */ jsxs("div", { className: "sd-main", children: [
1924
- /* @__PURE__ */ jsx("section", { className: "sd-timeline", children: /* @__PURE__ */ jsx(
1925
- TimelineScrubber,
1926
- {
1927
- mutations: timeline,
1928
- selectedIndex: playback.currentIndex,
1929
- onSelect: (index, mutation) => {
1930
- playback.goTo(index);
1931
- selectMutation(mutation);
1932
- },
1933
- isPlaying: playback.isPlaying,
1934
- onPlay: playback.play,
1935
- onPause: playback.pause,
1936
- onStepForward: playback.stepForward,
1937
- onStepBackward: playback.stepBackward,
1938
- playbackSpeed: playback.playbackSpeed,
1939
- onSpeedChange: playback.setSpeed
1940
- }
1941
- ) }),
1942
- /* @__PURE__ */ jsxs("div", { className: "sd-panels", children: [
1943
- /* @__PURE__ */ jsxs("div", { className: "sd-panel-tabs", children: [
3662
+ currentSession && /* @__PURE__ */ jsxs("div", { className: "sd2-main", children: [
3663
+ /* @__PURE__ */ jsxs("div", { className: "sd2-left-panel", children: [
3664
+ /* @__PURE__ */ jsxs("div", { className: "sd2-tabs", children: [
3665
+ /* @__PURE__ */ jsxs(
3666
+ "button",
3667
+ {
3668
+ className: `sd2-tab ${mainTab === "issues" ? "active" : ""}`,
3669
+ onClick: () => setMainTab("issues"),
3670
+ children: [
3671
+ "\u{1F6A8} Issues ",
3672
+ issues.length > 0 && `(${issues.length})`
3673
+ ]
3674
+ }
3675
+ ),
3676
+ /* @__PURE__ */ jsx(
3677
+ "button",
3678
+ {
3679
+ className: `sd2-tab ${mainTab === "timeline" ? "active" : ""}`,
3680
+ onClick: () => setMainTab("timeline"),
3681
+ children: "\u{1F4CA} Timeline"
3682
+ }
3683
+ ),
3684
+ /* @__PURE__ */ jsxs(
3685
+ "button",
3686
+ {
3687
+ className: `sd2-tab ${mainTab === "graph" ? "active" : ""}`,
3688
+ onClick: () => setMainTab("graph"),
3689
+ children: [
3690
+ "\u{1F517} Graph ",
3691
+ dependencyGraph.couplings.length > 0 && `(${dependencyGraph.couplings.length})`
3692
+ ]
3693
+ }
3694
+ )
3695
+ ] }),
3696
+ /* @__PURE__ */ jsxs("div", { className: "sd2-tab-content", children: [
3697
+ mainTab === "issues" && /* @__PURE__ */ jsx(
3698
+ IssuesPanel,
3699
+ {
3700
+ issues,
3701
+ onJumpToMutation: handleJumpToMutation
3702
+ }
3703
+ ),
3704
+ mainTab === "timeline" && /* @__PURE__ */ jsx("div", { className: "sd2-timeline-panel", children: /* @__PURE__ */ jsx(
3705
+ TimelineScrubber,
3706
+ {
3707
+ mutations: timeline,
3708
+ selectedIndex: playback.currentIndex,
3709
+ onSelect: (index, mutation) => {
3710
+ playback.goTo(index);
3711
+ selectMutation(mutation);
3712
+ },
3713
+ isPlaying: playback.isPlaying,
3714
+ onPlay: playback.play,
3715
+ onPause: playback.pause,
3716
+ onStepForward: playback.stepForward,
3717
+ onStepBackward: playback.stepBackward,
3718
+ playbackSpeed: playback.playbackSpeed,
3719
+ onSpeedChange: playback.setSpeed
3720
+ }
3721
+ ) }),
3722
+ mainTab === "graph" && /* @__PURE__ */ jsx(
3723
+ DependencyGraph,
3724
+ {
3725
+ graph: dependencyGraph,
3726
+ onPathSelect: (path) => setSearchQuery(path)
3727
+ }
3728
+ )
3729
+ ] })
3730
+ ] }),
3731
+ /* @__PURE__ */ jsxs("div", { className: "sd2-right-panel", children: [
3732
+ /* @__PURE__ */ jsxs("div", { className: "sd2-tabs", children: [
1944
3733
  /* @__PURE__ */ jsx(
1945
3734
  "button",
1946
3735
  {
1947
- className: `sd-panel-tab ${activePanel === "diff" ? "active" : ""}`,
1948
- onClick: () => setActivePanel("diff"),
1949
- children: "\u{1F4CA} State Changes"
3736
+ className: `sd2-tab ${detailTab === "tree" ? "active" : ""}`,
3737
+ onClick: () => setDetailTab("tree"),
3738
+ children: "\u{1F333} Tree"
1950
3739
  }
1951
3740
  ),
1952
3741
  /* @__PURE__ */ jsx(
1953
3742
  "button",
1954
3743
  {
1955
- className: `sd-panel-tab ${activePanel === "inspector" ? "active" : ""}`,
1956
- onClick: () => setActivePanel("inspector"),
1957
- children: "\u{1F50D} Mutation Details"
3744
+ className: `sd2-tab ${detailTab === "diff" ? "active" : ""}`,
3745
+ onClick: () => setDetailTab("diff"),
3746
+ children: "\u{1F4CA} Diff"
3747
+ }
3748
+ ),
3749
+ /* @__PURE__ */ jsx(
3750
+ "button",
3751
+ {
3752
+ className: `sd2-tab ${detailTab === "inspector" ? "active" : ""}`,
3753
+ onClick: () => setDetailTab("inspector"),
3754
+ children: "\u{1F50D} Details"
1958
3755
  }
1959
3756
  )
1960
3757
  ] }),
1961
- /* @__PURE__ */ jsxs("div", { className: "sd-panel-content", children: [
1962
- activePanel === "diff" && selectedMutation && /* @__PURE__ */ jsx(
3758
+ /* @__PURE__ */ jsxs("div", { className: "sd2-tab-content", children: [
3759
+ detailTab === "tree" && selectedMutation && /* @__PURE__ */ jsx(
3760
+ StateTreeViewer,
3761
+ {
3762
+ state: selectedMutation.nextState,
3763
+ issuePaths,
3764
+ changedPaths,
3765
+ removedPaths,
3766
+ searchQuery
3767
+ }
3768
+ ),
3769
+ detailTab === "diff" && selectedMutation && /* @__PURE__ */ jsx(
1963
3770
  StateDiffViewer,
1964
3771
  {
1965
3772
  previousState: selectedMutation.previousState,
@@ -1967,344 +3774,339 @@ instrumentReact(React);` })
1967
3774
  diff: selectedMutation.diff
1968
3775
  }
1969
3776
  ),
1970
- activePanel === "inspector" && /* @__PURE__ */ jsx(MutationInspector, { mutation: selectedMutation }),
1971
- !selectedMutation && /* @__PURE__ */ jsx("div", { className: "sd-panel-empty", children: /* @__PURE__ */ jsx("p", { children: "Select a mutation from the timeline to view details" }) })
3777
+ detailTab === "inspector" && /* @__PURE__ */ jsx(MutationInspector, { mutation: selectedMutation }),
3778
+ !selectedMutation && /* @__PURE__ */ jsx("div", { className: "sd2-panel-empty", children: /* @__PURE__ */ jsx("p", { children: "Select a mutation to view state details" }) })
1972
3779
  ] })
1973
3780
  ] })
1974
3781
  ] }),
1975
- /* @__PURE__ */ jsxs("footer", { className: "sd-footer", children: [
1976
- /* @__PURE__ */ jsx("span", { children: "State Surgeon v1.1.0" }),
1977
- /* @__PURE__ */ jsx("span", { className: "sd-footer-sep", children: "\u2022" }),
1978
- /* @__PURE__ */ jsx("span", { children: "Press \u2190 \u2192 to navigate, Space to play/pause" })
3782
+ /* @__PURE__ */ jsxs("footer", { className: "sd2-footer", children: [
3783
+ /* @__PURE__ */ jsx("span", { children: "State Surgeon v2.0" }),
3784
+ /* @__PURE__ */ jsx("span", { className: "sd2-sep", children: "\u2022" }),
3785
+ /* @__PURE__ */ jsx("span", { children: "\u2190 \u2192 navigate \u2022 Space play/pause \u2022 1-3 switch tabs" })
1979
3786
  ] }),
1980
- /* @__PURE__ */ jsx("style", { children: `
1981
- .surgeon-dashboard {
1982
- min-height: 100vh;
1983
- display: flex;
1984
- flex-direction: column;
1985
- background: linear-gradient(135deg, #0f0f23 0%, #1a1a2e 100%);
1986
- color: #e0e0e0;
1987
- font-family: system-ui, -apple-system, sans-serif;
1988
- }
1989
-
1990
- .sd-header {
1991
- display: flex;
1992
- justify-content: space-between;
1993
- align-items: center;
1994
- padding: 1rem 2rem;
1995
- background: rgba(22, 33, 62, 0.8);
1996
- backdrop-filter: blur(10px);
1997
- border-bottom: 1px solid #333;
1998
- position: sticky;
1999
- top: 0;
2000
- z-index: 100;
2001
- }
2002
-
2003
- .sd-header-left, .sd-header-right {
2004
- display: flex;
2005
- align-items: center;
2006
- gap: 1rem;
2007
- }
3787
+ /* @__PURE__ */ jsx("style", { children: dashboardStyles })
3788
+ ] });
3789
+ }
3790
+ var dashboardStyles = `
3791
+ .surgeon-dashboard-v2 {
3792
+ min-height: 100vh;
3793
+ display: flex;
3794
+ flex-direction: column;
3795
+ background: linear-gradient(135deg, #0f0f23 0%, #1a1a2e 100%);
3796
+ color: #e0e0e0;
3797
+ font-family: system-ui, -apple-system, sans-serif;
3798
+ }
2008
3799
 
2009
- .sd-logo {
2010
- display: flex;
2011
- align-items: center;
2012
- gap: 0.5rem;
2013
- margin: 0;
2014
- font-size: 1.25rem;
2015
- }
3800
+ /* Header */
3801
+ .sd2-header {
3802
+ display: flex;
3803
+ justify-content: space-between;
3804
+ align-items: center;
3805
+ padding: 0.75rem 1.5rem;
3806
+ background: rgba(22, 33, 62, 0.95);
3807
+ backdrop-filter: blur(10px);
3808
+ border-bottom: 1px solid #333;
3809
+ position: sticky;
3810
+ top: 0;
3811
+ z-index: 100;
3812
+ flex-wrap: wrap;
3813
+ gap: 0.75rem;
3814
+ }
2016
3815
 
2017
- .sd-logo-icon {
2018
- font-size: 1.5rem;
2019
- }
3816
+ .sd2-header-left, .sd2-header-right {
3817
+ display: flex;
3818
+ align-items: center;
3819
+ gap: 1rem;
3820
+ }
2020
3821
 
2021
- .sd-logo-text {
2022
- background: linear-gradient(135deg, #00d9ff, #a78bfa);
2023
- -webkit-background-clip: text;
2024
- -webkit-text-fill-color: transparent;
2025
- background-clip: text;
2026
- font-weight: 700;
2027
- }
3822
+ .sd2-logo {
3823
+ display: flex;
3824
+ align-items: center;
3825
+ gap: 0.5rem;
3826
+ margin: 0;
3827
+ font-size: 1.125rem;
3828
+ }
2028
3829
 
2029
- .sd-version {
2030
- font-size: 0.7rem;
2031
- padding: 0.2rem 0.4rem;
2032
- background: #333;
2033
- border-radius: 4px;
2034
- color: #888;
2035
- }
3830
+ .sd2-logo-icon { font-size: 1.25rem; }
2036
3831
 
2037
- .sd-session-select {
2038
- padding: 0.625rem 1.25rem;
2039
- min-width: 350px;
2040
- border: 1px solid #333;
2041
- border-radius: 8px;
2042
- background: #0f0f23;
2043
- color: #e0e0e0;
2044
- font-size: 0.875rem;
2045
- cursor: pointer;
2046
- }
3832
+ .sd2-logo-text {
3833
+ background: linear-gradient(135deg, #00d9ff, #a78bfa);
3834
+ -webkit-background-clip: text;
3835
+ -webkit-text-fill-color: transparent;
3836
+ background-clip: text;
3837
+ font-weight: 700;
3838
+ }
2047
3839
 
2048
- .sd-session-select:focus {
2049
- outline: none;
2050
- border-color: #00d9ff;
2051
- }
3840
+ .sd2-version {
3841
+ font-size: 0.65rem;
3842
+ padding: 0.15rem 0.35rem;
3843
+ background: linear-gradient(135deg, #22c55e, #06b6d4);
3844
+ border-radius: 4px;
3845
+ color: #000;
3846
+ font-weight: 600;
3847
+ }
2052
3848
 
2053
- .sd-auto-refresh {
2054
- display: flex;
2055
- align-items: center;
2056
- gap: 0.5rem;
2057
- font-size: 0.875rem;
2058
- color: #888;
2059
- cursor: pointer;
2060
- }
3849
+ .sd2-issue-badges {
3850
+ display: flex;
3851
+ gap: 0.5rem;
3852
+ }
2061
3853
 
2062
- .sd-refresh-btn {
2063
- padding: 0.5rem;
2064
- border: 1px solid #333;
2065
- border-radius: 8px;
2066
- background: transparent;
2067
- color: #e0e0e0;
2068
- cursor: pointer;
2069
- font-size: 1rem;
2070
- }
3854
+ .sd2-badge {
3855
+ padding: 0.25rem 0.5rem;
3856
+ border-radius: 12px;
3857
+ font-size: 0.75rem;
3858
+ font-weight: 600;
3859
+ }
2071
3860
 
2072
- .sd-refresh-btn:hover {
2073
- background: #16213e;
2074
- }
3861
+ .sd2-badge-critical { background: #ef444430; color: #fca5a5; }
3862
+ .sd2-badge-warning { background: #fbbf2430; color: #fcd34d; }
3863
+ .sd2-badge-info { background: #22c55e30; color: #86efac; }
3864
+
3865
+ .sd2-session-select {
3866
+ padding: 0.5rem 1rem;
3867
+ min-width: 300px;
3868
+ border: 1px solid #333;
3869
+ border-radius: 8px;
3870
+ background: #0f0f23;
3871
+ color: #e0e0e0;
3872
+ font-size: 0.875rem;
3873
+ cursor: pointer;
3874
+ }
2075
3875
 
2076
- .sd-error {
2077
- display: flex;
2078
- align-items: center;
2079
- gap: 0.75rem;
2080
- padding: 1rem 2rem;
2081
- background: rgba(239, 68, 68, 0.1);
2082
- border-bottom: 1px solid #ef4444;
2083
- }
3876
+ .sd2-search {
3877
+ padding: 0.5rem 1rem;
3878
+ width: 200px;
3879
+ border: 1px solid #333;
3880
+ border-radius: 8px;
3881
+ background: #0f0f23;
3882
+ color: #e0e0e0;
3883
+ font-size: 0.875rem;
3884
+ }
2084
3885
 
2085
- .sd-error-icon {
2086
- font-size: 1.25rem;
2087
- }
3886
+ .sd2-auto-refresh {
3887
+ display: flex;
3888
+ align-items: center;
3889
+ gap: 0.375rem;
3890
+ font-size: 0.8rem;
3891
+ color: #888;
3892
+ cursor: pointer;
3893
+ }
2088
3894
 
2089
- .sd-error p {
2090
- margin: 0;
2091
- color: #fca5a5;
2092
- }
3895
+ .sd2-refresh-btn {
3896
+ padding: 0.375rem 0.625rem;
3897
+ border: 1px solid #333;
3898
+ border-radius: 6px;
3899
+ background: transparent;
3900
+ color: #e0e0e0;
3901
+ cursor: pointer;
3902
+ font-size: 1rem;
3903
+ }
2093
3904
 
2094
- .sd-welcome {
2095
- flex: 1;
2096
- display: flex;
2097
- align-items: center;
2098
- justify-content: center;
2099
- padding: 2rem;
2100
- }
3905
+ .sd2-refresh-btn:hover { background: #16213e; }
2101
3906
 
2102
- .sd-welcome-content {
2103
- text-align: center;
2104
- max-width: 600px;
2105
- }
3907
+ .sd2-error {
3908
+ padding: 0.75rem 1.5rem;
3909
+ background: rgba(239, 68, 68, 0.1);
3910
+ border-bottom: 1px solid #ef4444;
3911
+ color: #fca5a5;
3912
+ }
2106
3913
 
2107
- .sd-welcome-icon {
2108
- font-size: 5rem;
2109
- margin-bottom: 1rem;
2110
- animation: float 3s ease-in-out infinite;
2111
- }
3914
+ /* Welcome */
3915
+ .sd2-welcome {
3916
+ flex: 1;
3917
+ display: flex;
3918
+ align-items: center;
3919
+ justify-content: center;
3920
+ padding: 2rem;
3921
+ }
2112
3922
 
2113
- @keyframes float {
2114
- 0%, 100% { transform: translateY(0); }
2115
- 50% { transform: translateY(-10px); }
2116
- }
3923
+ .sd2-welcome-content {
3924
+ text-align: center;
3925
+ max-width: 700px;
3926
+ }
2117
3927
 
2118
- .sd-welcome h2 {
2119
- margin: 0 0 0.5rem;
2120
- font-size: 2rem;
2121
- background: linear-gradient(135deg, #00d9ff, #a78bfa);
2122
- -webkit-background-clip: text;
2123
- -webkit-text-fill-color: transparent;
2124
- background-clip: text;
2125
- }
3928
+ .sd2-welcome-icon {
3929
+ font-size: 4rem;
3930
+ margin-bottom: 1rem;
3931
+ animation: float 3s ease-in-out infinite;
3932
+ }
2126
3933
 
2127
- .sd-welcome > p {
2128
- margin: 0 0 2rem;
2129
- color: #888;
2130
- }
3934
+ @keyframes float {
3935
+ 0%, 100% { transform: translateY(0); }
3936
+ 50% { transform: translateY(-10px); }
3937
+ }
2131
3938
 
2132
- .sd-welcome-steps {
2133
- text-align: left;
2134
- background: #16213e;
2135
- border-radius: 12px;
2136
- padding: 1.5rem;
2137
- }
3939
+ .sd2-welcome h2 {
3940
+ margin: 0 0 0.5rem;
3941
+ font-size: 1.75rem;
3942
+ background: linear-gradient(135deg, #00d9ff, #a78bfa);
3943
+ -webkit-background-clip: text;
3944
+ -webkit-text-fill-color: transparent;
3945
+ background-clip: text;
3946
+ }
2138
3947
 
2139
- .sd-welcome-steps h3 {
2140
- margin: 0 0 1rem;
2141
- color: #00d9ff;
2142
- font-size: 1rem;
2143
- }
3948
+ .sd2-welcome > div > p {
3949
+ color: #888;
3950
+ margin-bottom: 2rem;
3951
+ }
2144
3952
 
2145
- .sd-welcome-steps ol {
2146
- margin: 0;
2147
- padding-left: 1.25rem;
2148
- }
3953
+ .sd2-features {
3954
+ display: grid;
3955
+ grid-template-columns: repeat(2, 1fr);
3956
+ gap: 1rem;
3957
+ margin-bottom: 2rem;
3958
+ }
2149
3959
 
2150
- .sd-welcome-steps li {
2151
- margin-bottom: 1rem;
2152
- color: #ccc;
2153
- }
3960
+ .sd2-feature {
3961
+ display: flex;
3962
+ flex-direction: column;
3963
+ align-items: center;
3964
+ padding: 1.25rem;
3965
+ background: #16213e;
3966
+ border-radius: 12px;
3967
+ text-align: center;
3968
+ }
2154
3969
 
2155
- .sd-welcome-steps pre {
2156
- margin: 0.5rem 0 0;
2157
- padding: 1rem;
2158
- background: #0f0f23;
2159
- border-radius: 8px;
2160
- font-size: 0.8rem;
2161
- overflow-x: auto;
2162
- }
3970
+ .sd2-feature-icon {
3971
+ font-size: 2rem;
3972
+ margin-bottom: 0.5rem;
3973
+ }
2163
3974
 
2164
- .sd-welcome-sessions h3 {
2165
- margin: 0 0 1rem;
2166
- color: #00d9ff;
2167
- }
3975
+ .sd2-feature strong {
3976
+ color: #00d9ff;
3977
+ margin-bottom: 0.25rem;
3978
+ }
2168
3979
 
2169
- .sd-session-list {
2170
- display: grid;
2171
- gap: 0.75rem;
2172
- }
3980
+ .sd2-feature span:last-child {
3981
+ font-size: 0.8rem;
3982
+ color: #888;
3983
+ }
2173
3984
 
2174
- .sd-session-card {
2175
- display: flex;
2176
- justify-content: space-between;
2177
- align-items: center;
2178
- padding: 1rem 1.25rem;
2179
- background: #16213e;
2180
- border: 1px solid #333;
2181
- border-radius: 8px;
2182
- cursor: pointer;
2183
- transition: all 0.2s;
2184
- text-align: left;
2185
- color: inherit;
2186
- }
3985
+ .sd2-session-list h3 {
3986
+ color: #00d9ff;
3987
+ margin-bottom: 0.75rem;
3988
+ }
2187
3989
 
2188
- .sd-session-card:hover {
2189
- border-color: #00d9ff;
2190
- background: #1a2744;
2191
- }
3990
+ .sd2-session-card {
3991
+ display: flex;
3992
+ justify-content: space-between;
3993
+ width: 100%;
3994
+ padding: 0.875rem 1.25rem;
3995
+ margin-bottom: 0.5rem;
3996
+ background: #16213e;
3997
+ border: 1px solid #333;
3998
+ border-radius: 8px;
3999
+ cursor: pointer;
4000
+ color: inherit;
4001
+ transition: all 0.2s;
4002
+ }
2192
4003
 
2193
- .sd-session-app {
2194
- font-weight: 600;
2195
- color: #a78bfa;
2196
- }
4004
+ .sd2-session-card:hover {
4005
+ border-color: #00d9ff;
4006
+ background: #1a2744;
4007
+ }
2197
4008
 
2198
- .sd-session-id {
2199
- font-family: monospace;
2200
- font-size: 0.8rem;
2201
- color: #666;
2202
- }
4009
+ .sd2-session-app { color: #a78bfa; font-weight: 600; }
4010
+ .sd2-session-count { color: #00d9ff; }
2203
4011
 
2204
- .sd-session-count {
2205
- font-size: 0.875rem;
2206
- color: #00d9ff;
2207
- }
4012
+ .sd2-loading {
4013
+ flex: 1;
4014
+ display: flex;
4015
+ flex-direction: column;
4016
+ align-items: center;
4017
+ justify-content: center;
4018
+ gap: 1rem;
4019
+ }
2208
4020
 
2209
- .sd-loading {
2210
- flex: 1;
2211
- display: flex;
2212
- flex-direction: column;
2213
- align-items: center;
2214
- justify-content: center;
2215
- gap: 1rem;
2216
- }
4021
+ .sd2-spinner {
4022
+ width: 36px;
4023
+ height: 36px;
4024
+ border: 3px solid #333;
4025
+ border-top-color: #00d9ff;
4026
+ border-radius: 50%;
4027
+ animation: spin 1s linear infinite;
4028
+ }
2217
4029
 
2218
- .sd-spinner {
2219
- width: 40px;
2220
- height: 40px;
2221
- border: 3px solid #333;
2222
- border-top-color: #00d9ff;
2223
- border-radius: 50%;
2224
- animation: spin 1s linear infinite;
2225
- }
4030
+ @keyframes spin { to { transform: rotate(360deg); } }
2226
4031
 
2227
- @keyframes spin {
2228
- to { transform: rotate(360deg); }
2229
- }
4032
+ /* Main content */
4033
+ .sd2-main {
4034
+ flex: 1;
4035
+ display: grid;
4036
+ grid-template-columns: 1fr 1fr;
4037
+ gap: 1rem;
4038
+ padding: 1rem;
4039
+ min-height: 0;
4040
+ }
2230
4041
 
2231
- .sd-main {
2232
- flex: 1;
2233
- padding: 1.5rem 2rem;
2234
- display: flex;
2235
- flex-direction: column;
2236
- gap: 1.5rem;
2237
- }
4042
+ @media (max-width: 1200px) {
4043
+ .sd2-main { grid-template-columns: 1fr; }
4044
+ }
2238
4045
 
2239
- .sd-timeline {
2240
- flex-shrink: 0;
2241
- }
4046
+ .sd2-left-panel, .sd2-right-panel {
4047
+ display: flex;
4048
+ flex-direction: column;
4049
+ background: #1a1a2e;
4050
+ border-radius: 12px;
4051
+ overflow: hidden;
4052
+ min-height: 500px;
4053
+ }
2242
4054
 
2243
- .sd-panels {
2244
- flex: 1;
2245
- background: #1a1a2e;
2246
- border-radius: 12px;
2247
- overflow: hidden;
2248
- display: flex;
2249
- flex-direction: column;
2250
- min-height: 400px;
2251
- }
4055
+ .sd2-tabs {
4056
+ display: flex;
4057
+ background: #16213e;
4058
+ border-bottom: 1px solid #333;
4059
+ }
2252
4060
 
2253
- .sd-panel-tabs {
2254
- display: flex;
2255
- background: #16213e;
2256
- border-bottom: 1px solid #333;
2257
- }
4061
+ .sd2-tab {
4062
+ flex: 1;
4063
+ padding: 0.75rem 1rem;
4064
+ border: none;
4065
+ background: transparent;
4066
+ color: #888;
4067
+ cursor: pointer;
4068
+ font-size: 0.875rem;
4069
+ transition: all 0.2s;
4070
+ border-bottom: 2px solid transparent;
4071
+ }
2258
4072
 
2259
- .sd-panel-tab {
2260
- padding: 1rem 1.5rem;
2261
- border: none;
2262
- background: transparent;
2263
- color: #888;
2264
- cursor: pointer;
2265
- font-size: 0.875rem;
2266
- transition: all 0.2s;
2267
- }
4073
+ .sd2-tab:hover { color: #ccc; background: #1a2744; }
2268
4074
 
2269
- .sd-panel-tab:hover {
2270
- color: #ccc;
2271
- background: #1a2744;
2272
- }
4075
+ .sd2-tab.active {
4076
+ color: #00d9ff;
4077
+ background: #1a1a2e;
4078
+ border-bottom-color: #00d9ff;
4079
+ }
2273
4080
 
2274
- .sd-panel-tab.active {
2275
- color: #00d9ff;
2276
- background: #1a1a2e;
2277
- border-bottom: 2px solid #00d9ff;
2278
- }
4081
+ .sd2-tab-content {
4082
+ flex: 1;
4083
+ overflow: auto;
4084
+ }
2279
4085
 
2280
- .sd-panel-content {
2281
- flex: 1;
2282
- overflow: auto;
2283
- }
4086
+ .sd2-timeline-panel {
4087
+ padding: 1rem;
4088
+ }
2284
4089
 
2285
- .sd-panel-empty {
2286
- height: 100%;
2287
- display: flex;
2288
- align-items: center;
2289
- justify-content: center;
2290
- color: #666;
2291
- }
4090
+ .sd2-panel-empty {
4091
+ height: 100%;
4092
+ display: flex;
4093
+ align-items: center;
4094
+ justify-content: center;
4095
+ color: #666;
4096
+ }
2292
4097
 
2293
- .sd-footer {
2294
- padding: 0.75rem 2rem;
2295
- background: rgba(22, 33, 62, 0.5);
2296
- border-top: 1px solid #333;
2297
- font-size: 0.75rem;
2298
- color: #666;
2299
- text-align: center;
2300
- }
4098
+ /* Footer */
4099
+ .sd2-footer {
4100
+ padding: 0.625rem 1.5rem;
4101
+ background: rgba(22, 33, 62, 0.5);
4102
+ border-top: 1px solid #333;
4103
+ font-size: 0.75rem;
4104
+ color: #666;
4105
+ text-align: center;
4106
+ }
2301
4107
 
2302
- .sd-footer-sep {
2303
- margin: 0 0.5rem;
2304
- }
2305
- ` })
2306
- ] });
2307
- }
4108
+ .sd2-sep { margin: 0 0.5rem; }
4109
+ `;
2308
4110
 
2309
4111
  export { DashboardProvider, MutationInspector, StateDiffViewer, StateSurgeonDashboard, TimelineScrubber, useDashboard };
2310
4112
  //# sourceMappingURL=index.mjs.map