@treelocator/runtime 0.5.2 → 0.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.
Files changed (163) hide show
  1. package/.eslintignore +1 -0
  2. package/dist/_generated_styles.d.ts +1 -1
  3. package/dist/_generated_styles.js +20 -0
  4. package/dist/_generated_tree_icon.d.ts +1 -1
  5. package/dist/adapters/HtmlElementTreeNode.d.ts +2 -2
  6. package/dist/adapters/HtmlElementTreeNode.js +4 -6
  7. package/dist/adapters/createTreeNode.js +17 -44
  8. package/dist/adapters/detectFramework.d.ts +8 -0
  9. package/dist/adapters/detectFramework.js +25 -0
  10. package/dist/adapters/detectFramework.test.d.ts +1 -0
  11. package/dist/adapters/detectFramework.test.js +60 -0
  12. package/dist/adapters/jsx/jsxAdapter.js +54 -89
  13. package/dist/adapters/jsx/jsxAdapter.test.d.ts +1 -0
  14. package/dist/adapters/jsx/jsxAdapter.test.js +273 -0
  15. package/dist/adapters/nextjs/parseNextjsDataAttributes.js +1 -1
  16. package/dist/adapters/nextjs/parseNextjsDataAttributes.test.d.ts +1 -0
  17. package/dist/adapters/nextjs/parseNextjsDataAttributes.test.js +158 -0
  18. package/dist/adapters/react/findFiberByHtmlElement.d.ts +1 -1
  19. package/dist/adapters/react/findFiberByHtmlElement.js +1 -1
  20. package/dist/adapters/react/getAllParentsElementsAndRootComponent.js +4 -0
  21. package/dist/adapters/resolveAdapter.d.ts +1 -1
  22. package/dist/adapters/resolveAdapter.js +4 -8
  23. package/dist/adapters/svelte/svelteAdapter.test.d.ts +1 -0
  24. package/dist/adapters/svelte/svelteAdapter.test.js +280 -0
  25. package/dist/adapters/vue/vueAdapter.test.d.ts +1 -0
  26. package/dist/adapters/vue/vueAdapter.test.js +222 -0
  27. package/dist/browserApi.d.ts +148 -0
  28. package/dist/browserApi.js +146 -5
  29. package/dist/browserApi.test.d.ts +1 -0
  30. package/dist/browserApi.test.js +287 -0
  31. package/dist/components/RecordingPillButton.d.ts +11 -0
  32. package/dist/components/RecordingPillButton.js +202 -0
  33. package/dist/components/RecordingResults.d.ts +2 -0
  34. package/dist/components/RecordingResults.js +213 -78
  35. package/dist/components/Runtime.js +161 -554
  36. package/dist/components/SettingsPanel.d.ts +5 -0
  37. package/dist/components/SettingsPanel.js +312 -0
  38. package/dist/consoleCapture.d.ts +9 -0
  39. package/dist/consoleCapture.js +95 -0
  40. package/dist/functions/cssRuleInspector.d.ts +83 -0
  41. package/dist/functions/cssRuleInspector.js +608 -0
  42. package/dist/functions/cssRuleInspector.test.d.ts +1 -0
  43. package/dist/functions/cssRuleInspector.test.js +439 -0
  44. package/dist/functions/deduplicateLabels.test.d.ts +1 -0
  45. package/dist/functions/deduplicateLabels.test.js +178 -0
  46. package/dist/functions/enrichAncestrySourceMaps.js +0 -1
  47. package/dist/functions/extractComputedStyles.d.ts +51 -0
  48. package/dist/functions/extractComputedStyles.js +447 -0
  49. package/dist/functions/extractComputedStyles.test.d.ts +1 -0
  50. package/dist/functions/extractComputedStyles.test.js +549 -0
  51. package/dist/functions/formatAncestryChain.d.ts +8 -0
  52. package/dist/functions/formatAncestryChain.js +21 -1
  53. package/dist/functions/formatAncestryChain.test.js +18 -0
  54. package/dist/functions/getUsableName.test.d.ts +1 -0
  55. package/dist/functions/getUsableName.test.js +219 -0
  56. package/dist/functions/isCombinationModifiersPressed.test.d.ts +1 -0
  57. package/dist/functions/isCombinationModifiersPressed.test.js +192 -0
  58. package/dist/functions/mergeRects.test.js +210 -1
  59. package/dist/functions/namedSnapshots.d.ts +52 -0
  60. package/dist/functions/namedSnapshots.js +161 -0
  61. package/dist/functions/namedSnapshots.test.d.ts +1 -0
  62. package/dist/functions/namedSnapshots.test.js +85 -0
  63. package/dist/functions/normalizeFilePath.test.d.ts +1 -0
  64. package/dist/functions/normalizeFilePath.test.js +66 -0
  65. package/dist/functions/parseDataId.test.d.ts +1 -0
  66. package/dist/functions/parseDataId.test.js +101 -0
  67. package/dist/hooks/getStorage.d.ts +3 -0
  68. package/dist/hooks/getStorage.js +17 -0
  69. package/dist/hooks/useEventListeners.d.ts +15 -0
  70. package/dist/hooks/useEventListeners.js +56 -0
  71. package/dist/hooks/useLocatorStorage.d.ts +18 -0
  72. package/dist/hooks/useLocatorStorage.js +41 -0
  73. package/dist/hooks/useLocatorStorage.test.d.ts +1 -0
  74. package/dist/hooks/useLocatorStorage.test.js +124 -0
  75. package/dist/hooks/useRecordingState.d.ts +43 -0
  76. package/dist/hooks/useRecordingState.js +387 -0
  77. package/dist/hooks/useSettings.d.ts +13 -0
  78. package/dist/hooks/useSettings.js +66 -0
  79. package/dist/index.d.ts +5 -2
  80. package/dist/index.js +4 -2
  81. package/dist/initRuntime.d.ts +3 -1
  82. package/dist/initRuntime.js +4 -1
  83. package/dist/mcpBridge.d.ts +61 -0
  84. package/dist/mcpBridge.js +534 -0
  85. package/dist/mcpBridge.test.d.ts +1 -0
  86. package/dist/mcpBridge.test.js +248 -0
  87. package/dist/output.css +20 -0
  88. package/dist/visualDiff/diff.d.ts +9 -0
  89. package/dist/visualDiff/diff.js +209 -0
  90. package/dist/visualDiff/diff.test.d.ts +1 -0
  91. package/dist/visualDiff/diff.test.js +253 -0
  92. package/dist/visualDiff/settle.d.ts +3 -0
  93. package/dist/visualDiff/settle.js +50 -0
  94. package/dist/visualDiff/settle.test.d.ts +1 -0
  95. package/dist/visualDiff/settle.test.js +65 -0
  96. package/dist/visualDiff/snapshot.d.ts +4 -0
  97. package/dist/visualDiff/snapshot.js +84 -0
  98. package/dist/visualDiff/snapshot.test.d.ts +1 -0
  99. package/dist/visualDiff/snapshot.test.js +245 -0
  100. package/dist/visualDiff/types.d.ts +37 -0
  101. package/dist/visualDiff/types.js +1 -0
  102. package/package.json +2 -2
  103. package/scripts/wrapCSS.js +1 -1
  104. package/scripts/wrapImage.js +1 -1
  105. package/src/_generated_styles.ts +21 -1
  106. package/src/_generated_tree_icon.ts +1 -1
  107. package/src/adapters/HtmlElementTreeNode.ts +10 -7
  108. package/src/adapters/createTreeNode.ts +12 -51
  109. package/src/adapters/detectFramework.test.ts +73 -0
  110. package/src/adapters/detectFramework.ts +28 -0
  111. package/src/adapters/jsx/jsxAdapter.test.ts +240 -0
  112. package/src/adapters/jsx/jsxAdapter.ts +53 -106
  113. package/src/adapters/nextjs/parseNextjsDataAttributes.test.ts +212 -0
  114. package/src/adapters/nextjs/parseNextjsDataAttributes.ts +1 -1
  115. package/src/adapters/react/findDebugSource.ts +5 -6
  116. package/src/adapters/react/findFiberByHtmlElement.ts +3 -3
  117. package/src/adapters/react/getAllParentsElementsAndRootComponent.ts +3 -0
  118. package/src/adapters/react/reactAdapter.ts +1 -2
  119. package/src/adapters/resolveAdapter.ts +4 -14
  120. package/src/adapters/svelte/svelteAdapter.test.ts +334 -0
  121. package/src/adapters/vue/vueAdapter.test.ts +259 -0
  122. package/src/browserApi.test.ts +329 -0
  123. package/src/browserApi.ts +351 -4
  124. package/src/components/RecordingPillButton.tsx +301 -0
  125. package/src/components/RecordingResults.tsx +114 -13
  126. package/src/components/Runtime.tsx +176 -621
  127. package/src/components/SettingsPanel.tsx +339 -0
  128. package/src/consoleCapture.ts +113 -0
  129. package/src/functions/cssRuleInspector.test.ts +517 -0
  130. package/src/functions/cssRuleInspector.ts +708 -0
  131. package/src/functions/deduplicateLabels.test.ts +115 -0
  132. package/src/functions/enrichAncestrySourceMaps.ts +6 -3
  133. package/src/functions/extractComputedStyles.test.ts +681 -0
  134. package/src/functions/extractComputedStyles.ts +768 -0
  135. package/src/functions/formatAncestryChain.test.ts +23 -1
  136. package/src/functions/formatAncestryChain.ts +22 -1
  137. package/src/functions/getUsableName.test.ts +242 -0
  138. package/src/functions/isCombinationModifiersPressed.test.ts +156 -0
  139. package/src/functions/mergeRects.test.ts +111 -1
  140. package/src/functions/namedSnapshots.test.ts +106 -0
  141. package/src/functions/namedSnapshots.ts +232 -0
  142. package/src/functions/normalizeFilePath.test.ts +80 -0
  143. package/src/functions/parseDataId.test.ts +125 -0
  144. package/src/hooks/getStorage.ts +26 -0
  145. package/src/hooks/useEventListeners.ts +97 -0
  146. package/src/hooks/useLocatorStorage.test.ts +127 -0
  147. package/src/hooks/useLocatorStorage.ts +60 -0
  148. package/src/hooks/useRecordingState.ts +516 -0
  149. package/src/hooks/useSettings.ts +83 -0
  150. package/src/index.ts +10 -5
  151. package/src/initRuntime.ts +5 -0
  152. package/src/mcpBridge.test.ts +260 -0
  153. package/src/mcpBridge.ts +677 -0
  154. package/src/visualDiff/diff.test.ts +167 -0
  155. package/src/visualDiff/diff.ts +242 -0
  156. package/src/visualDiff/settle.test.ts +77 -0
  157. package/src/visualDiff/settle.ts +62 -0
  158. package/src/visualDiff/snapshot.test.ts +200 -0
  159. package/src/visualDiff/snapshot.ts +119 -0
  160. package/src/visualDiff/types.ts +40 -0
  161. package/tsconfig.json +3 -1
  162. package/vitest.config.ts +18 -0
  163. package/jest.config.ts +0 -195
@@ -0,0 +1,202 @@
1
+ import { template as _$template } from "solid-js/web";
2
+ import { delegateEvents as _$delegateEvents } from "solid-js/web";
3
+ import { memo as _$memo } from "solid-js/web";
4
+ import { effect as _$effect } from "solid-js/web";
5
+ import { insert as _$insert } from "solid-js/web";
6
+ import { setAttribute as _$setAttribute } from "solid-js/web";
7
+ import { createComponent as _$createComponent } from "solid-js/web";
8
+ import { setStyleProperty as _$setStyleProperty } from "solid-js/web";
9
+ var _tmpl$ = /*#__PURE__*/_$template(`<div class=fixed data-treelocator-api=window.__treelocator__ data-treelocator-help=window.__treelocator__.help() style=z-index:2147483646;pointer-events:auto><style>\n @keyframes treelocator-rec-pulse \{\n 0%, 100% \{ opacity: 1; }\n 50% \{ opacity: 0.3; }\n }\n\n @keyframes treelocator-rec-puddle \{\n 0% \{\n transform: translate(-50%, -50%) scale(0.9);\n opacity: 0;\n }\n 20% \{\n opacity: 0.38;\n }\n 100% \{\n transform: translate(-50%, -50%) scale(2);\n opacity: 0;\n }\n }\n </style><svg width=154 height=138 style=pointer-events:none><path d="M 31 55 A 65 65 0 0 1 123 55"fill=none stroke="rgba(255, 255, 255, 0.6)"stroke-width=1></path></svg><div aria-label="TreeLocatorJS: Get component paths using window.__treelocator__.getPath(selector)"role=button style=border-radius:50%;align-items:center;justify-content:center><img alt=TreeLocatorJS width=44 height=44></div><div role=note aria-label="TreeLocatorJS Developer Tool API — Run: window.__treelocator__.help() for full usage instructions. Quick start: window.__treelocator__.getPath('button.submit') returns component ancestry. window.__treelocator__.replayWithRecord('.my-element') replays recorded interactions while tracking visual changes."style=white-space:nowrap>TreeLocatorJS: Run window.__treelocator__.help() for API docs`),
10
+ _tmpl$2 = /*#__PURE__*/_$template(`<div role=button style="border-radius:50%;align-items:center;justify-content:center;box-shadow:0 4px 14px rgba(0, 0, 0, 0.25);transform-origin:center center">`),
11
+ _tmpl$3 = /*#__PURE__*/_$template(`<div aria-hidden=true style=border-radius:50%;pointer-events:none;z-index:0>`),
12
+ _tmpl$4 = /*#__PURE__*/_$template(`<div style=border-radius:2px;z-index:1>`),
13
+ _tmpl$5 = /*#__PURE__*/_$template(`<div style=border-radius:50%>`),
14
+ _tmpl$6 = /*#__PURE__*/_$template(`<svg width=18 height=18 viewBox="0 0 24 24"fill=none stroke-width=2 stroke-linecap=round stroke-linejoin=round><circle cx=12 cy=12 r=3></circle><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z">`);
15
+ import { createSignal, For } from "solid-js";
16
+ import treeIconUrl from "../_generated_tree_icon";
17
+ const CIRCLE_SIZE = 42;
18
+ const WRAPPER_W = 154;
19
+ const WRAPPER_H = 138;
20
+ // Positions are relative to the 154x138 wrapper anchored at right:23 bottom:23.
21
+ // Button center is at (77, 87); circles sit on an arc of radius ~65 around that
22
+ // point at angles 225° / 270° / 315°.
23
+ const CIRCLES = [{
24
+ left: 10,
25
+ top: 34,
26
+ openDelay: 0,
27
+ closeDelay: 200,
28
+ kind: "record"
29
+ }, {
30
+ left: 56,
31
+ top: 10,
32
+ openDelay: 100,
33
+ closeDelay: 100,
34
+ kind: "stub1"
35
+ }, {
36
+ left: 102,
37
+ top: 34,
38
+ openDelay: 200,
39
+ closeDelay: 0,
40
+ kind: "settings"
41
+ }];
42
+ export function RecordingPillButton(props) {
43
+ const [hovered, setHovered] = createSignal(false);
44
+ const open = () => hovered() || props.settingsOpen || props.recordingState === "selecting" || props.recordingState === "recording";
45
+ return (() => {
46
+ var _el$ = _tmpl$(),
47
+ _el$2 = _el$.firstChild,
48
+ _el$3 = _el$2.nextSibling,
49
+ _el$4 = _el$3.nextSibling,
50
+ _el$5 = _el$4.firstChild,
51
+ _el$6 = _el$4.nextSibling;
52
+ _el$.addEventListener("mouseleave", () => setHovered(false));
53
+ _el$.addEventListener("mouseenter", () => setHovered(true));
54
+ _$setStyleProperty(_el$, "bottom", "23px");
55
+ _$setStyleProperty(_el$, "right", "23px");
56
+ _$setStyleProperty(_el$, "width", "154px");
57
+ _$setStyleProperty(_el$, "height", "138px");
58
+ _$setStyleProperty(_el$3, "position", "absolute");
59
+ _$setStyleProperty(_el$3, "left", "0");
60
+ _$setStyleProperty(_el$3, "top", "0");
61
+ _$setStyleProperty(_el$3, "transition", "opacity 0.3s ease-out");
62
+ _$insert(_el$, _$createComponent(For, {
63
+ each: CIRCLES,
64
+ children: circle => (() => {
65
+ var _el$7 = _tmpl$2();
66
+ _el$7.$$click = e => {
67
+ if (circle.kind === "record") {
68
+ e.stopPropagation();
69
+ props.onRecordClick();
70
+ return;
71
+ }
72
+ if (circle.kind === "settings") {
73
+ e.stopPropagation();
74
+ props.onSettingsClick();
75
+ return;
76
+ }
77
+ };
78
+ _$setStyleProperty(_el$7, "position", "absolute");
79
+ _$setStyleProperty(_el$7, "width", "42px");
80
+ _$setStyleProperty(_el$7, "height", "42px");
81
+ _$setStyleProperty(_el$7, "background", "#ffffff");
82
+ _$setStyleProperty(_el$7, "display", "flex");
83
+ _$setStyleProperty(_el$7, "transition", "transform 0.35s cubic-bezier(0.34, 1.56, 0.64, 1), opacity 0.2s ease-out");
84
+ _$setStyleProperty(_el$7, "overflow", "visible");
85
+ _$insert(_el$7, (() => {
86
+ var _c$ = _$memo(() => circle.kind === "record");
87
+ return () => _c$() ? _$memo(() => props.recordingState === "recording")() ? [(() => {
88
+ var _el$8 = _tmpl$3();
89
+ _$setStyleProperty(_el$8, "position", "absolute");
90
+ _$setStyleProperty(_el$8, "left", "50%");
91
+ _$setStyleProperty(_el$8, "top", "50%");
92
+ _$setStyleProperty(_el$8, "width", "42px");
93
+ _$setStyleProperty(_el$8, "height", "42px");
94
+ _$setStyleProperty(_el$8, "border", "2px solid rgba(239, 68, 68, 0.48)");
95
+ _$setStyleProperty(_el$8, "transform", "translate(-50%, -50%) scale(0.9)");
96
+ _$setStyleProperty(_el$8, "opacity", "0");
97
+ _$setStyleProperty(_el$8, "filter", "blur(1px)");
98
+ _$setStyleProperty(_el$8, "animation", "treelocator-rec-puddle 1.8s ease-out 0s infinite both");
99
+ return _el$8;
100
+ })(), (() => {
101
+ var _el$9 = _tmpl$3();
102
+ _$setStyleProperty(_el$9, "position", "absolute");
103
+ _$setStyleProperty(_el$9, "left", "50%");
104
+ _$setStyleProperty(_el$9, "top", "50%");
105
+ _$setStyleProperty(_el$9, "width", "42px");
106
+ _$setStyleProperty(_el$9, "height", "42px");
107
+ _$setStyleProperty(_el$9, "border", "2px solid rgba(239, 68, 68, 0.4)");
108
+ _$setStyleProperty(_el$9, "transform", "translate(-50%, -50%) scale(0.9)");
109
+ _$setStyleProperty(_el$9, "opacity", "0");
110
+ _$setStyleProperty(_el$9, "filter", "blur(1px)");
111
+ _$setStyleProperty(_el$9, "animation", "treelocator-rec-puddle 1.8s ease-out 0.9s infinite both");
112
+ return _el$9;
113
+ })(), (() => {
114
+ var _el$0 = _tmpl$4();
115
+ _$setStyleProperty(_el$0, "width", "14px");
116
+ _$setStyleProperty(_el$0, "height", "14px");
117
+ _$setStyleProperty(_el$0, "background", "#ef4444");
118
+ return _el$0;
119
+ })()] : (() => {
120
+ var _el$1 = _tmpl$5();
121
+ _$setStyleProperty(_el$1, "width", "14px");
122
+ _$setStyleProperty(_el$1, "height", "14px");
123
+ _$setStyleProperty(_el$1, "background", "#ef4444");
124
+ _$effect(_$p => _$setStyleProperty(_el$1, "animation", props.recordingState === "selecting" ? "treelocator-rec-pulse 1s ease-in-out infinite" : "none"));
125
+ return _el$1;
126
+ })() : _$memo(() => circle.kind === "settings")() ? (() => {
127
+ var _el$10 = _tmpl$6();
128
+ _$effect(() => _$setAttribute(_el$10, "stroke", props.settingsOpen ? "#3b82f6" : "#1f2937"));
129
+ return _el$10;
130
+ })() : null;
131
+ })());
132
+ _$effect(_p$ => {
133
+ var _v$4 = circle.kind === "settings" ? "" : undefined,
134
+ _v$5 = circle.left + "px",
135
+ _v$6 = circle.top + "px",
136
+ _v$7 = circle.kind === "record" || circle.kind === "settings" ? "pointer" : "default",
137
+ _v$8 = open() ? "scale(1)" : "scale(0)",
138
+ _v$9 = open() ? "1" : "0",
139
+ _v$0 = open() ? circle.openDelay + "ms" : circle.closeDelay + "ms",
140
+ _v$1 = circle.kind === "record" ? props.recordingState === "idle" ? "Record element changes" : props.recordingState === "selecting" ? "Cancel recording selection" : props.recordingState === "recording" ? "Stop recording" : "Dismiss results" : circle.kind === "settings" ? props.settingsOpen ? "Close settings" : "Open settings" : "Coming soon";
141
+ _v$4 !== _p$.e && _$setAttribute(_el$7, "data-treelocator-settings-toggle", _p$.e = _v$4);
142
+ _v$5 !== _p$.t && _$setStyleProperty(_el$7, "left", _p$.t = _v$5);
143
+ _v$6 !== _p$.a && _$setStyleProperty(_el$7, "top", _p$.a = _v$6);
144
+ _v$7 !== _p$.o && _$setStyleProperty(_el$7, "cursor", _p$.o = _v$7);
145
+ _v$8 !== _p$.i && _$setStyleProperty(_el$7, "transform", _p$.i = _v$8);
146
+ _v$9 !== _p$.n && _$setStyleProperty(_el$7, "opacity", _p$.n = _v$9);
147
+ _v$0 !== _p$.s && _$setStyleProperty(_el$7, "transition-delay", _p$.s = _v$0);
148
+ _v$1 !== _p$.h && _$setAttribute(_el$7, "aria-label", _p$.h = _v$1);
149
+ return _p$;
150
+ }, {
151
+ e: undefined,
152
+ t: undefined,
153
+ a: undefined,
154
+ o: undefined,
155
+ i: undefined,
156
+ n: undefined,
157
+ s: undefined,
158
+ h: undefined
159
+ });
160
+ return _el$7;
161
+ })()
162
+ }), _el$4);
163
+ _el$4.$$click = e => {
164
+ e.stopPropagation();
165
+ props.onLocatorToggle();
166
+ };
167
+ _$setStyleProperty(_el$4, "position", "absolute");
168
+ _$setStyleProperty(_el$4, "right", "50px");
169
+ _$setStyleProperty(_el$4, "bottom", "10px");
170
+ _$setStyleProperty(_el$4, "width", "54px");
171
+ _$setStyleProperty(_el$4, "height", "54px");
172
+ _$setStyleProperty(_el$4, "background", "#ffffff");
173
+ _$setStyleProperty(_el$4, "display", "flex");
174
+ _$setStyleProperty(_el$4, "cursor", "pointer");
175
+ _$setStyleProperty(_el$4, "overflow", "hidden");
176
+ _$setStyleProperty(_el$4, "transition", "box-shadow 0.2s ease-in-out");
177
+ _$setAttribute(_el$5, "src", treeIconUrl);
178
+ _$setStyleProperty(_el$6, "position", "absolute");
179
+ _$setStyleProperty(_el$6, "width", "1px");
180
+ _$setStyleProperty(_el$6, "height", "1px");
181
+ _$setStyleProperty(_el$6, "padding", "0");
182
+ _$setStyleProperty(_el$6, "margin", "-1px");
183
+ _$setStyleProperty(_el$6, "overflow", "hidden");
184
+ _$setStyleProperty(_el$6, "clip", "rect(0,0,0,0)");
185
+ _$setStyleProperty(_el$6, "border", "0");
186
+ _$effect(_p$ => {
187
+ var _v$ = open() ? "0.4" : "0",
188
+ _v$2 = open() ? "50ms" : "0ms",
189
+ _v$3 = props.locatorActive ? "0 0 0 3px #3b82f6, 0 4px 14px rgba(0, 0, 0, 0.25)" : "0 4px 14px rgba(0, 0, 0, 0.25)";
190
+ _v$ !== _p$.e && _$setStyleProperty(_el$3, "opacity", _p$.e = _v$);
191
+ _v$2 !== _p$.t && _$setStyleProperty(_el$3, "transition-delay", _p$.t = _v$2);
192
+ _v$3 !== _p$.a && _$setStyleProperty(_el$4, "box-shadow", _p$.a = _v$3);
193
+ return _p$;
194
+ }, {
195
+ e: undefined,
196
+ t: undefined,
197
+ a: undefined
198
+ });
199
+ return _el$;
200
+ })();
201
+ }
202
+ _$delegateEvents(["click"]);
@@ -1,4 +1,5 @@
1
1
  import type { DejitterFinding, DejitterSummary } from "../dejitter/recorder";
2
+ import type { DeltaReport } from "../visualDiff/types";
2
3
  export type InteractionEvent = {
3
4
  t: number;
4
5
  type: string;
@@ -12,6 +13,7 @@ type RecordingResultsProps = {
12
13
  data: any;
13
14
  elementPath: string;
14
15
  interactions: InteractionEvent[];
16
+ visualDiff?: DeltaReport | null;
15
17
  onDismiss: () => void;
16
18
  onReplay?: () => void;
17
19
  replaying?: boolean;
@@ -9,13 +9,21 @@ import { insert as _$insert } from "solid-js/web";
9
9
  import { setStyleProperty as _$setStyleProperty } from "solid-js/web";
10
10
  var _tmpl$ = /*#__PURE__*/_$template(`<div style=margin-bottom:4px;font-size:11px;text-transform:uppercase;letter-spacing:0.5px>Findings (<!>)`),
11
11
  _tmpl$2 = /*#__PURE__*/_$template(`<div style="border-top:1px solid rgba(255, 255, 255, 0.08)"><div style=margin-bottom:4px;font-size:11px;text-transform:uppercase;letter-spacing:0.5px>Interactions (<!>)`),
12
- _tmpl$3 = /*#__PURE__*/_$template(`<div style="z-index:2147483646;max-height:400px;overflow-y:auto;backdrop-filter:blur(12px);border-radius:12px;box-shadow:0 8px 32px rgba(0, 0, 0, 0.4);font-family:system-ui, -apple-system, sans-serif;font-size:12px;pointer-events:auto"><div style="align-items:center;justify-content:space-between;border-bottom:1px solid rgba(255, 255, 255, 0.08)"><div><div style=font-weight:600;font-size:13px>Recording Results</div><div style=margin-top:2px>ms &middot; <!> frames</div></div><div style=align-items:center><div>Copy</div><div style=border-radius:4px;font-size:16px;line-height:1>&times;</div></div></div><div>`),
13
- _tmpl$4 = /*#__PURE__*/_$template(`<div>`),
14
- _tmpl$5 = /*#__PURE__*/_$template(`<div>Prev`),
15
- _tmpl$6 = /*#__PURE__*/_$template(`<div>Next`),
16
- _tmpl$7 = /*#__PURE__*/_$template(`<div style=align-items:center><span style=font-size:14px>&#10003;</span>No anomalies detected`),
17
- _tmpl$8 = /*#__PURE__*/_$template(`<div style="align-items:flex-start;border-bottom:1px solid rgba(255, 255, 255, 0.05)"><div style=border-radius:50%;flex-shrink:0;margin-top:3px></div><div style=min-width:0><div style=align-items:center><span style=font-weight:600></span><span style=font-size:11px></span></div><div style=margin-top:2px;line-height:1.4;word-break:break-word>`),
18
- _tmpl$9 = /*#__PURE__*/_$template(`<div style=font-family:monospace;font-size:11px><span>ms</span> <span></span> <span>`);
12
+ _tmpl$3 = /*#__PURE__*/_$template(`<span>+`),
13
+ _tmpl$4 = /*#__PURE__*/_$template(`<span>-`),
14
+ _tmpl$5 = /*#__PURE__*/_$template(`<span>~`),
15
+ _tmpl$6 = /*#__PURE__*/_$template(`<span>→`),
16
+ _tmpl$7 = /*#__PURE__*/_$template(`<div style=font-size:11px;margin-top:4px>… <!> more (copy to see all)`),
17
+ _tmpl$8 = /*#__PURE__*/_$template(`<div style="border-top:1px solid rgba(255, 255, 255, 0.08)"><div style=margin-bottom:4px;font-size:11px;text-transform:uppercase;letter-spacing:0.5px>Visual diff (<!>)</div><div style=margin-bottom:6px;font-size:11px><span style=margin-left:auto>ms · settle: `),
18
+ _tmpl$9 = /*#__PURE__*/_$template(`<div style="z-index:2147483646;max-height:400px;overflow-y:auto;backdrop-filter:blur(12px);border-radius:12px;box-shadow:0 8px 32px rgba(0, 0, 0, 0.4);font-family:system-ui, -apple-system, sans-serif;font-size:12px;pointer-events:auto"><div style="align-items:center;justify-content:space-between;border-bottom:1px solid rgba(255, 255, 255, 0.08)"><div><div style=font-weight:600;font-size:13px>Recording Results</div><div style=margin-top:2px>ms &middot; <!> frames</div></div><div style=align-items:center><div>Copy</div><div style=border-radius:4px;font-size:16px;line-height:1>&times;</div></div></div><div>`),
19
+ _tmpl$0 = /*#__PURE__*/_$template(`<div>`),
20
+ _tmpl$1 = /*#__PURE__*/_$template(`<div>Prev`),
21
+ _tmpl$10 = /*#__PURE__*/_$template(`<div>Next`),
22
+ _tmpl$11 = /*#__PURE__*/_$template(`<div style=align-items:center><span style=font-size:14px>&#10003;</span>No anomalies detected`),
23
+ _tmpl$12 = /*#__PURE__*/_$template(`<div style="align-items:flex-start;border-bottom:1px solid rgba(255, 255, 255, 0.05)"><div style=border-radius:50%;flex-shrink:0;margin-top:3px></div><div style=min-width:0><div style=align-items:center><span style=font-weight:600></span><span style=font-size:11px></span></div><div style=margin-top:2px;line-height:1.4;word-break:break-word>`),
24
+ _tmpl$13 = /*#__PURE__*/_$template(`<div style=font-family:monospace;font-size:11px><span>ms</span> <span></span> <span>`),
25
+ _tmpl$14 = /*#__PURE__*/_$template(`<span> (<!>)`),
26
+ _tmpl$15 = /*#__PURE__*/_$template(`<div style=font-family:monospace;font-size:11px;word-break:break-all><span style=font-weight:600;margin-right:6px></span><span>`);
19
27
  import { For, Show } from "solid-js";
20
28
  const SEVERITY_COLORS = {
21
29
  high: "#ef4444",
@@ -23,7 +31,7 @@ const SEVERITY_COLORS = {
23
31
  low: "#eab308",
24
32
  info: "#9ca3af"
25
33
  };
26
- function formatDataForClipboard(data, elementPath, summary, findings, interactions) {
34
+ function formatDataForClipboard(data, elementPath, summary, findings, interactions, visualDiff) {
27
35
  const lines = [];
28
36
 
29
37
  // Element ancestry path from treelocator
@@ -32,21 +40,26 @@ function formatDataForClipboard(data, elementPath, summary, findings, interactio
32
40
  }
33
41
 
34
42
  // Header
35
- lines.push(`Recording: ${Math.round(summary?.duration ?? 0)}ms, ${summary?.rawFrameCount ?? 0} frames`);
43
+ const frameCount = summary?.rawFrameCount ?? 0;
44
+ lines.push(`Recording: ${Math.round(summary?.duration ?? 0)}ms, ${frameCount} frames`);
36
45
  lines.push('');
37
46
 
38
- // Property changes
39
- if (data?.propStats?.props && data.propStats.props.length > 0) {
40
- lines.push('Changed properties:');
41
- for (const p of data.propStats.props) {
42
- if (p.raw === 0) continue;
43
- lines.push(` ${p.prop}: ${p.raw} changes (${p.mode})`);
47
+ // Property changes — only meaningful with >1 frame; a single sample is
48
+ // the initial reading, not a change, and dejitter labels it "anomaly" which
49
+ // is misleading.
50
+ if (frameCount > 1 && data?.propStats?.props && data.propStats.props.length > 0) {
51
+ const realChanges = data.propStats.props.filter(p => p.raw > 1);
52
+ if (realChanges.length > 0) {
53
+ lines.push('Changed properties:');
54
+ for (const p of realChanges) {
55
+ lines.push(` ${p.prop}: ${p.raw - 1} changes (${p.mode})`);
56
+ }
57
+ lines.push('');
44
58
  }
45
- lines.push('');
46
59
  }
47
60
 
48
- // Samples with actual values — replace dejitter element IDs with the ancestry path
49
- if (data?.samples && data.samples.length > 0) {
61
+ // Samples with actual values — skip if we only captured the initial frame.
62
+ if (frameCount > 1 && data?.samples && data.samples.length > 0) {
50
63
  lines.push('Timeline:');
51
64
  for (const frame of data.samples) {
52
65
  for (const change of frame.changes) {
@@ -79,6 +92,12 @@ function formatDataForClipboard(data, elementPath, summary, findings, interactio
79
92
  for (const evt of interactions) {
80
93
  lines.push(` ${evt.t}ms ${evt.type} ${evt.target} (${evt.x},${evt.y})`);
81
94
  }
95
+ lines.push('');
96
+ }
97
+
98
+ // Visual diff
99
+ if (visualDiff && visualDiff.entries.length > 0) {
100
+ lines.push(visualDiff.text);
82
101
  }
83
102
  return lines.join('\n');
84
103
  }
@@ -97,13 +116,13 @@ export function RecordingResults(props) {
97
116
  const duration = () => props.summary?.duration ?? 0;
98
117
  const frameCount = () => props.summary?.rawFrameCount ?? 0;
99
118
  function handleCopy() {
100
- const text = formatDataForClipboard(props.data, props.elementPath, props.summary, props.findings, props.interactions);
119
+ const text = formatDataForClipboard(props.data, props.elementPath, props.summary, props.findings, props.interactions, props.visualDiff);
101
120
  navigator.clipboard.writeText(text).then(() => {
102
121
  props.onToast?.("Copied to clipboard");
103
122
  });
104
123
  }
105
124
  return (() => {
106
- var _el$ = _tmpl$3(),
125
+ var _el$ = _tmpl$9(),
107
126
  _el$2 = _el$.firstChild,
108
127
  _el$3 = _el$2.firstChild,
109
128
  _el$4 = _el$3.firstChild,
@@ -116,7 +135,7 @@ export function RecordingResults(props) {
116
135
  _el$1 = _el$0.nextSibling,
117
136
  _el$10 = _el$2.nextSibling;
118
137
  _$setStyleProperty(_el$, "position", "fixed");
119
- _$setStyleProperty(_el$, "bottom", "84px");
138
+ _$setStyleProperty(_el$, "bottom", "180px");
120
139
  _$setStyleProperty(_el$, "right", "20px");
121
140
  _$setStyleProperty(_el$, "width", "340px");
122
141
  _$setStyleProperty(_el$, "background", "rgba(15, 15, 15, 0.92)");
@@ -134,29 +153,29 @@ export function RecordingResults(props) {
134
153
  _$insert(_el$9, (() => {
135
154
  var _c$ = _$memo(() => !!props.onReplay);
136
155
  return () => _c$() && (() => {
137
- var _el$20 = _tmpl$4();
138
- _$addEventListener(_el$20, "click", props.onReplay, true);
139
- _$insert(_el$20, () => props.replaying ? "Replaying..." : "Replay");
140
- _$effect(_$p => _$style(_el$20, buttonStyle(props.replaying), _$p));
141
- return _el$20;
156
+ var _el$40 = _tmpl$0();
157
+ _$addEventListener(_el$40, "click", props.onReplay, true);
158
+ _$insert(_el$40, () => props.replaying ? "Replaying..." : "Replay");
159
+ _$effect(_$p => _$style(_el$40, buttonStyle(props.replaying), _$p));
160
+ return _el$40;
142
161
  })();
143
162
  })(), _el$1);
144
163
  _$insert(_el$9, (() => {
145
164
  var _c$2 = _$memo(() => !!(props.hasPrevious && props.onLoadPrevious));
146
165
  return () => _c$2() && (() => {
147
- var _el$21 = _tmpl$5();
148
- _$addEventListener(_el$21, "click", props.onLoadPrevious, true);
149
- _$effect(_$p => _$style(_el$21, buttonStyle(), _$p));
150
- return _el$21;
166
+ var _el$41 = _tmpl$1();
167
+ _$addEventListener(_el$41, "click", props.onLoadPrevious, true);
168
+ _$effect(_$p => _$style(_el$41, buttonStyle(), _$p));
169
+ return _el$41;
151
170
  })();
152
171
  })(), _el$1);
153
172
  _$insert(_el$9, (() => {
154
173
  var _c$3 = _$memo(() => !!(props.hasNext && props.onLoadNext));
155
174
  return () => _c$3() && (() => {
156
- var _el$22 = _tmpl$6();
157
- _$addEventListener(_el$22, "click", props.onLoadNext, true);
158
- _$effect(_$p => _$style(_el$22, buttonStyle(), _$p));
159
- return _el$22;
175
+ var _el$42 = _tmpl$10();
176
+ _$addEventListener(_el$42, "click", props.onLoadNext, true);
177
+ _$effect(_$p => _$style(_el$42, buttonStyle(), _$p));
178
+ return _el$42;
160
179
  })();
161
180
  })(), _el$1);
162
181
  _$addEventListener(_el$1, "click", props.onDismiss, true);
@@ -170,13 +189,13 @@ export function RecordingResults(props) {
170
189
  },
171
190
  get fallback() {
172
191
  return (() => {
173
- var _el$23 = _tmpl$7(),
174
- _el$24 = _el$23.firstChild;
175
- _$setStyleProperty(_el$23, "display", "flex");
176
- _$setStyleProperty(_el$23, "gap", "6px");
177
- _$setStyleProperty(_el$23, "padding", "8px 0");
178
- _$setStyleProperty(_el$23, "color", "#4ade80");
179
- return _el$23;
192
+ var _el$43 = _tmpl$11(),
193
+ _el$44 = _el$43.firstChild;
194
+ _$setStyleProperty(_el$43, "display", "flex");
195
+ _$setStyleProperty(_el$43, "gap", "6px");
196
+ _$setStyleProperty(_el$43, "padding", "8px 0");
197
+ _$setStyleProperty(_el$43, "color", "#4ade80");
198
+ return _el$43;
180
199
  })();
181
200
  },
182
201
  get children() {
@@ -193,36 +212,36 @@ export function RecordingResults(props) {
193
212
  return props.findings;
194
213
  },
195
214
  children: finding => (() => {
196
- var _el$25 = _tmpl$8(),
197
- _el$26 = _el$25.firstChild,
198
- _el$27 = _el$26.nextSibling,
199
- _el$28 = _el$27.firstChild,
200
- _el$29 = _el$28.firstChild,
201
- _el$30 = _el$29.nextSibling,
202
- _el$31 = _el$28.nextSibling;
203
- _$setStyleProperty(_el$25, "display", "flex");
204
- _$setStyleProperty(_el$25, "gap", "8px");
205
- _$setStyleProperty(_el$25, "padding", "6px 0");
206
- _$setStyleProperty(_el$26, "width", "8px");
207
- _$setStyleProperty(_el$26, "height", "8px");
208
- _$setStyleProperty(_el$28, "display", "flex");
209
- _$setStyleProperty(_el$28, "gap", "6px");
210
- _$setStyleProperty(_el$29, "color", "#fff");
211
- _$insert(_el$29, () => finding.type);
212
- _$insert(_el$30, () => finding.severity);
213
- _$setStyleProperty(_el$31, "color", "#a1a1aa");
214
- _$insert(_el$31, () => finding.description);
215
+ var _el$45 = _tmpl$12(),
216
+ _el$46 = _el$45.firstChild,
217
+ _el$47 = _el$46.nextSibling,
218
+ _el$48 = _el$47.firstChild,
219
+ _el$49 = _el$48.firstChild,
220
+ _el$50 = _el$49.nextSibling,
221
+ _el$51 = _el$48.nextSibling;
222
+ _$setStyleProperty(_el$45, "display", "flex");
223
+ _$setStyleProperty(_el$45, "gap", "8px");
224
+ _$setStyleProperty(_el$45, "padding", "6px 0");
225
+ _$setStyleProperty(_el$46, "width", "8px");
226
+ _$setStyleProperty(_el$46, "height", "8px");
227
+ _$setStyleProperty(_el$48, "display", "flex");
228
+ _$setStyleProperty(_el$48, "gap", "6px");
229
+ _$setStyleProperty(_el$49, "color", "#fff");
230
+ _$insert(_el$49, () => finding.type);
231
+ _$insert(_el$50, () => finding.severity);
232
+ _$setStyleProperty(_el$51, "color", "#a1a1aa");
233
+ _$insert(_el$51, () => finding.description);
215
234
  _$effect(_p$ => {
216
235
  var _v$ = SEVERITY_COLORS[finding.severity] || "#9ca3af",
217
236
  _v$2 = SEVERITY_COLORS[finding.severity];
218
- _v$ !== _p$.e && _$setStyleProperty(_el$26, "background", _p$.e = _v$);
219
- _v$2 !== _p$.t && _$setStyleProperty(_el$30, "color", _p$.t = _v$2);
237
+ _v$ !== _p$.e && _$setStyleProperty(_el$46, "background", _p$.e = _v$);
238
+ _v$2 !== _p$.t && _$setStyleProperty(_el$50, "color", _p$.t = _v$2);
220
239
  return _p$;
221
240
  }, {
222
241
  e: undefined,
223
242
  t: undefined
224
243
  });
225
- return _el$25;
244
+ return _el$45;
226
245
  })()
227
246
  })];
228
247
  }
@@ -245,26 +264,142 @@ export function RecordingResults(props) {
245
264
  return props.interactions;
246
265
  },
247
266
  children: evt => (() => {
248
- var _el$32 = _tmpl$9(),
249
- _el$33 = _el$32.firstChild,
250
- _el$34 = _el$33.firstChild,
251
- _el$35 = _el$33.nextSibling,
252
- _el$36 = _el$35.nextSibling,
253
- _el$37 = _el$36.nextSibling,
254
- _el$38 = _el$37.nextSibling;
255
- _$setStyleProperty(_el$32, "padding", "3px 0");
256
- _$setStyleProperty(_el$32, "color", "#a1a1aa");
257
- _$setStyleProperty(_el$33, "color", "#9ca3af");
258
- _$insert(_el$33, () => evt.t, _el$34);
259
- _$setStyleProperty(_el$36, "color", "#60a5fa");
260
- _$insert(_el$36, () => evt.type);
261
- _$insert(_el$38, () => evt.target);
262
- return _el$32;
267
+ var _el$52 = _tmpl$13(),
268
+ _el$53 = _el$52.firstChild,
269
+ _el$54 = _el$53.firstChild,
270
+ _el$55 = _el$53.nextSibling,
271
+ _el$56 = _el$55.nextSibling,
272
+ _el$57 = _el$56.nextSibling,
273
+ _el$58 = _el$57.nextSibling;
274
+ _$setStyleProperty(_el$52, "padding", "3px 0");
275
+ _$setStyleProperty(_el$52, "color", "#a1a1aa");
276
+ _$setStyleProperty(_el$53, "color", "#9ca3af");
277
+ _$insert(_el$53, () => evt.t, _el$54);
278
+ _$setStyleProperty(_el$56, "color", "#60a5fa");
279
+ _$insert(_el$56, () => evt.type);
280
+ _$insert(_el$58, () => evt.target);
281
+ return _el$52;
263
282
  })()
264
283
  }), null);
265
284
  return _el$15;
266
285
  }
267
286
  }), null);
287
+ _$insert(_el$, _$createComponent(Show, {
288
+ get when() {
289
+ return _$memo(() => !!props.visualDiff)() && props.visualDiff.entries.length > 0;
290
+ },
291
+ get children() {
292
+ var _el$20 = _tmpl$8(),
293
+ _el$21 = _el$20.firstChild,
294
+ _el$22 = _el$21.firstChild,
295
+ _el$24 = _el$22.nextSibling,
296
+ _el$23 = _el$24.nextSibling,
297
+ _el$25 = _el$21.nextSibling,
298
+ _el$34 = _el$25.firstChild,
299
+ _el$35 = _el$34.firstChild;
300
+ _$setStyleProperty(_el$20, "padding", "8px 14px 12px");
301
+ _$setStyleProperty(_el$21, "color", "#9ca3af");
302
+ _$insert(_el$21, () => props.visualDiff.entries.length, _el$24);
303
+ _$setStyleProperty(_el$25, "display", "flex");
304
+ _$setStyleProperty(_el$25, "gap", "8px");
305
+ _$setStyleProperty(_el$25, "color", "#9ca3af");
306
+ _$insert(_el$25, _$createComponent(Show, {
307
+ get when() {
308
+ return props.visualDiff.counts.added > 0;
309
+ },
310
+ get children() {
311
+ var _el$26 = _tmpl$3(),
312
+ _el$27 = _el$26.firstChild;
313
+ _$setStyleProperty(_el$26, "color", "#4ade80");
314
+ _$insert(_el$26, () => props.visualDiff.counts.added, null);
315
+ return _el$26;
316
+ }
317
+ }), _el$34);
318
+ _$insert(_el$25, _$createComponent(Show, {
319
+ get when() {
320
+ return props.visualDiff.counts.removed > 0;
321
+ },
322
+ get children() {
323
+ var _el$28 = _tmpl$4(),
324
+ _el$29 = _el$28.firstChild;
325
+ _$setStyleProperty(_el$28, "color", "#ef4444");
326
+ _$insert(_el$28, () => props.visualDiff.counts.removed, null);
327
+ return _el$28;
328
+ }
329
+ }), _el$34);
330
+ _$insert(_el$25, _$createComponent(Show, {
331
+ get when() {
332
+ return props.visualDiff.counts.changed > 0;
333
+ },
334
+ get children() {
335
+ var _el$30 = _tmpl$5(),
336
+ _el$31 = _el$30.firstChild;
337
+ _$setStyleProperty(_el$30, "color", "#eab308");
338
+ _$insert(_el$30, () => props.visualDiff.counts.changed, null);
339
+ return _el$30;
340
+ }
341
+ }), _el$34);
342
+ _$insert(_el$25, _$createComponent(Show, {
343
+ get when() {
344
+ return props.visualDiff.counts.moved > 0;
345
+ },
346
+ get children() {
347
+ var _el$32 = _tmpl$6(),
348
+ _el$33 = _el$32.firstChild;
349
+ _$setStyleProperty(_el$32, "color", "#60a5fa");
350
+ _$insert(_el$32, () => props.visualDiff.counts.moved, null);
351
+ return _el$32;
352
+ }
353
+ }), _el$34);
354
+ _$insert(_el$34, () => Math.round(props.visualDiff.elapsedMs), _el$35);
355
+ _$insert(_el$34, () => props.visualDiff.settle, null);
356
+ _$insert(_el$20, _$createComponent(For, {
357
+ get each() {
358
+ return props.visualDiff.entries.slice(0, 20);
359
+ },
360
+ children: entry => (() => {
361
+ var _el$59 = _tmpl$15(),
362
+ _el$60 = _el$59.firstChild,
363
+ _el$61 = _el$60.nextSibling;
364
+ _$setStyleProperty(_el$59, "padding", "2px 0");
365
+ _$setStyleProperty(_el$59, "color", "#a1a1aa");
366
+ _$insert(_el$60, () => entry.type);
367
+ _$insert(_el$61, () => entry.label);
368
+ _$insert(_el$59, _$createComponent(Show, {
369
+ get when() {
370
+ return _$memo(() => !!entry.changedFields)() && entry.changedFields.length > 0;
371
+ },
372
+ get children() {
373
+ var _el$62 = _tmpl$14(),
374
+ _el$63 = _el$62.firstChild,
375
+ _el$65 = _el$63.nextSibling,
376
+ _el$64 = _el$65.nextSibling;
377
+ _$setStyleProperty(_el$62, "color", "#6b7280");
378
+ _$insert(_el$62, () => entry.changedFields.join(", "), _el$65);
379
+ return _el$62;
380
+ }
381
+ }), null);
382
+ _$effect(_$p => _$setStyleProperty(_el$60, "color", entry.type === "+" ? "#4ade80" : entry.type === "-" ? "#ef4444" : entry.type === "~" ? "#eab308" : "#60a5fa"));
383
+ return _el$59;
384
+ })()
385
+ }), null);
386
+ _$insert(_el$20, _$createComponent(Show, {
387
+ get when() {
388
+ return props.visualDiff.entries.length > 20;
389
+ },
390
+ get children() {
391
+ var _el$36 = _tmpl$7(),
392
+ _el$37 = _el$36.firstChild,
393
+ _el$39 = _el$37.nextSibling,
394
+ _el$38 = _el$39.nextSibling;
395
+ _$setStyleProperty(_el$36, "color", "#6b7280");
396
+ _$insert(_el$36, () => props.visualDiff.entries.length - 20, _el$39);
397
+ return _el$36;
398
+ }
399
+ }), null);
400
+ return _el$20;
401
+ }
402
+ }), null);
268
403
  _$effect(_$p => _$style(_el$0, buttonStyle(), _$p));
269
404
  return _el$;
270
405
  })();