@gemx-dev/heatmap-react 3.5.21 → 3.5.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/components/HeatmapLayout/ContentHeader.d.ts +4 -0
- package/dist/esm/components/HeatmapLayout/ContentHeader.d.ts.map +1 -0
- package/dist/esm/components/HeatmapLayout/HeatmapLayout.d.ts +7 -0
- package/dist/esm/components/HeatmapLayout/HeatmapLayout.d.ts.map +1 -0
- package/dist/esm/components/HeatmapLayout/LeftSidebar.d.ts +4 -0
- package/dist/esm/components/HeatmapLayout/LeftSidebar.d.ts.map +1 -0
- package/dist/esm/components/HeatmapLayout/ReplayControls.d.ts +2 -0
- package/dist/esm/components/HeatmapLayout/ReplayControls.d.ts.map +1 -0
- package/dist/esm/components/HeatmapLayout/VizDomContainer.d.ts +2 -0
- package/dist/esm/components/HeatmapLayout/VizDomContainer.d.ts.map +1 -0
- package/dist/esm/components/HeatmapLayout/VizDomRenderer.d.ts +6 -0
- package/dist/esm/components/HeatmapLayout/VizDomRenderer.d.ts.map +1 -0
- package/dist/esm/components/HeatmapLayout/WrapperLayout.d.ts +7 -0
- package/dist/esm/components/HeatmapLayout/WrapperLayout.d.ts.map +1 -0
- package/dist/esm/components/HeatmapLayout/WrapperPreview.d.ts +4 -0
- package/dist/esm/components/HeatmapLayout/WrapperPreview.d.ts.map +1 -0
- package/dist/esm/components/HeatmapLayout/index.d.ts +2 -0
- package/dist/esm/components/HeatmapLayout/index.d.ts.map +1 -0
- package/dist/esm/components/VizElement/ClarityVisualizer.d.ts +150 -0
- package/dist/esm/components/VizElement/ClarityVisualizer.d.ts.map +1 -0
- package/dist/esm/components/VizElement/HeatmapElementV2.d.ts +45 -0
- package/dist/esm/components/VizElement/HeatmapElementV2.d.ts.map +1 -0
- package/dist/esm/components/VizElement/PayloadProcessor.d.ts +65 -0
- package/dist/esm/components/VizElement/PayloadProcessor.d.ts.map +1 -0
- package/dist/esm/components/VizElement/VizElementRank.d.ts +74 -0
- package/dist/esm/components/VizElement/VizElementRank.d.ts.map +1 -0
- package/dist/esm/components/VizElement/constants.d.ts +5 -0
- package/dist/esm/components/VizElement/constants.d.ts.map +1 -0
- package/dist/esm/components/VizElement/helpers.d.ts +20 -0
- package/dist/esm/components/VizElement/helpers.d.ts.map +1 -0
- package/dist/esm/components/VizElement/types.d.ts +13 -0
- package/dist/esm/components/VizElement/types.d.ts.map +1 -0
- package/dist/esm/hooks/useHeatmapByMode.d.ts +6 -0
- package/dist/esm/hooks/useHeatmapByMode.d.ts.map +1 -0
- package/dist/esm/hooks/useHeatmapRender.d.ts +6 -0
- package/dist/esm/hooks/useHeatmapRender.d.ts.map +1 -0
- package/dist/esm/hooks/useHeatmapScale.d.ts +28 -0
- package/dist/esm/hooks/useHeatmapScale.d.ts.map +1 -0
- package/dist/esm/hooks/useReplayRender.d.ts +9 -0
- package/dist/esm/hooks/useReplayRender.d.ts.map +1 -0
- package/dist/esm/types/index.d.ts +3 -0
- package/dist/esm/types/index.d.ts.map +1 -0
- package/dist/umd/components/VizElement/VizElementRank.d.ts +75 -0
- package/dist/umd/components/VizElement/VizElementRank.d.ts.map +1 -0
- package/package.json +11 -14
- package/src/components/GraphView.tsx +58 -0
- package/src/components/Layout/ContentHeader.tsx +15 -0
- package/src/components/Layout/HeatmapLayout.tsx +63 -0
- package/src/components/Layout/LeftSidebar.tsx +32 -0
- package/src/components/Layout/WrapperLayout.tsx +18 -0
- package/src/components/Layout/WrapperPreview.tsx +14 -0
- package/src/components/Layout/index.ts +1 -0
- package/src/components/Test.tsx +1106 -0
- package/src/components/VizDom/ReplayControls.tsx +48 -0
- package/src/components/VizDom/VizDomContainer.tsx +25 -0
- package/src/components/VizDom/VizDomRenderer.tsx +87 -0
- package/src/components/VizDom/index.ts +1 -0
- package/src/components/VizElement/ClickedElementOverlay.tsx +65 -0
- package/src/components/VizElement/DefaultRankBadges.tsx +33 -0
- package/src/components/VizElement/ElementCallout.tsx +337 -0
- package/src/components/VizElement/HeatmapElements.tsx +147 -0
- package/src/components/VizElement/HoveredElementOverlay.tsx +46 -0
- package/src/components/VizElement/MissingElementMessage.tsx +32 -0
- package/src/components/VizElement/RankBadge.tsx +25 -0
- package/src/components/VizElement/VizElements.tsx +80 -0
- package/src/components/VizElement/index.ts +1 -0
- package/src/components/VizElement/temp/ClarityVisualizer.ts +764 -0
- package/src/components/VizElement/temp/VizElementRank.tsx +579 -0
- package/src/components/index.tsx +4 -0
- package/src/configs/iframe.ts +15 -0
- package/src/configs/index.ts +2 -0
- package/src/configs/style.ts +9 -0
- package/src/constants/index.ts +4 -0
- package/src/dist/components/GraphView.d.ts +9 -0
- package/src/dist/components/GraphView.d.ts.map +1 -0
- package/src/dist/components/Layout/ContentHeader.d.ts +4 -0
- package/src/dist/components/Layout/ContentHeader.d.ts.map +1 -0
- package/src/dist/components/Layout/HeatmapLayout.d.ts +11 -0
- package/src/dist/components/Layout/HeatmapLayout.d.ts.map +1 -0
- package/src/dist/components/Layout/LeftSidebar.d.ts +4 -0
- package/src/dist/components/Layout/LeftSidebar.d.ts.map +1 -0
- package/src/dist/components/Layout/WrapperLayout.d.ts +8 -0
- package/src/dist/components/Layout/WrapperLayout.d.ts.map +1 -0
- package/src/dist/components/Layout/WrapperPreview.d.ts +4 -0
- package/src/dist/components/Layout/WrapperPreview.d.ts.map +1 -0
- package/src/dist/components/Layout/index.d.ts +2 -0
- package/src/dist/components/Layout/index.d.ts.map +1 -0
- package/src/dist/components/Test.d.ts +121 -0
- package/src/dist/components/Test.d.ts.map +1 -0
- package/src/dist/components/VizDom/ReplayControls.d.ts +2 -0
- package/src/dist/components/VizDom/ReplayControls.d.ts.map +1 -0
- package/src/dist/components/VizDom/VizDomContainer.d.ts +2 -0
- package/src/dist/components/VizDom/VizDomContainer.d.ts.map +1 -0
- package/src/dist/components/VizDom/VizDomRenderer.d.ts +6 -0
- package/src/dist/components/VizDom/VizDomRenderer.d.ts.map +1 -0
- package/src/dist/components/VizDom/index.d.ts +2 -0
- package/src/dist/components/VizDom/index.d.ts.map +1 -0
- package/src/dist/components/VizElement/ClickedElementOverlay.d.ts +17 -0
- package/src/dist/components/VizElement/ClickedElementOverlay.d.ts.map +1 -0
- package/src/dist/components/VizElement/DefaultRankBadges.d.ts +11 -0
- package/src/dist/components/VizElement/DefaultRankBadges.d.ts.map +1 -0
- package/src/dist/components/VizElement/ElementCallout.d.ts +17 -0
- package/src/dist/components/VizElement/ElementCallout.d.ts.map +1 -0
- package/src/dist/components/VizElement/HeatmapElements.d.ts +23 -0
- package/src/dist/components/VizElement/HeatmapElements.d.ts.map +1 -0
- package/src/dist/components/VizElement/HoveredElementOverlay.d.ts +12 -0
- package/src/dist/components/VizElement/HoveredElementOverlay.d.ts.map +1 -0
- package/src/dist/components/VizElement/MissingElementMessage.d.ts +7 -0
- package/src/dist/components/VizElement/MissingElementMessage.d.ts.map +1 -0
- package/src/dist/components/VizElement/RankBadge.d.ts +10 -0
- package/src/dist/components/VizElement/RankBadge.d.ts.map +1 -0
- package/src/dist/components/VizElement/VizElements.d.ts +10 -0
- package/src/dist/components/VizElement/VizElements.d.ts.map +1 -0
- package/src/dist/components/VizElement/index.d.ts +2 -0
- package/src/dist/components/VizElement/index.d.ts.map +1 -0
- package/src/dist/components/VizElement/temp/ClarityVisualizer.d.ts +150 -0
- package/src/dist/components/VizElement/temp/ClarityVisualizer.d.ts.map +1 -0
- package/src/dist/components/VizElement/temp/VizElementRank.d.ts +74 -0
- package/src/dist/components/VizElement/temp/VizElementRank.d.ts.map +1 -0
- package/src/dist/components/index.d.ts +4 -0
- package/src/dist/components/index.d.ts.map +1 -0
- package/src/dist/configs/iframe.d.ts +10 -0
- package/src/dist/configs/iframe.d.ts.map +1 -0
- package/src/dist/configs/index.d.ts +3 -0
- package/src/dist/configs/index.d.ts.map +1 -0
- package/src/dist/configs/style.d.ts +9 -0
- package/src/dist/configs/style.d.ts.map +1 -0
- package/src/dist/constants/index.d.ts +5 -0
- package/src/dist/constants/index.d.ts.map +1 -0
- package/src/dist/helpers/iframe.d.ts +3 -0
- package/src/dist/helpers/iframe.d.ts.map +1 -0
- package/src/dist/helpers/index.d.ts +2 -0
- package/src/dist/helpers/index.d.ts.map +1 -0
- package/src/dist/helpers/viz-elements.d.ts +10 -0
- package/src/dist/helpers/viz-elements.d.ts.map +1 -0
- package/src/dist/hooks/index.d.ts +4 -0
- package/src/dist/hooks/index.d.ts.map +1 -0
- package/src/dist/hooks/vix-elements/index.d.ts +5 -0
- package/src/dist/hooks/vix-elements/index.d.ts.map +1 -0
- package/src/dist/hooks/vix-elements/useClickedElement.d.ts +14 -0
- package/src/dist/hooks/vix-elements/useClickedElement.d.ts.map +1 -0
- package/src/dist/hooks/vix-elements/useHeatmapEffects.d.ts +8 -0
- package/src/dist/hooks/vix-elements/useHeatmapEffects.d.ts.map +1 -0
- package/src/dist/hooks/vix-elements/useHeatmapElementPosition.d.ts +13 -0
- package/src/dist/hooks/vix-elements/useHeatmapElementPosition.d.ts.map +1 -0
- package/src/dist/hooks/vix-elements/useHoveredElement.d.ts +17 -0
- package/src/dist/hooks/vix-elements/useHoveredElement.d.ts.map +1 -0
- package/src/dist/hooks/viz-render/index.d.ts +2 -0
- package/src/dist/hooks/viz-render/index.d.ts.map +1 -0
- package/src/dist/hooks/viz-render/useHeatmapRender.d.ts +8 -0
- package/src/dist/hooks/viz-render/useHeatmapRender.d.ts.map +1 -0
- package/src/dist/hooks/viz-render/useHeatmapVizRender.d.ts +8 -0
- package/src/dist/hooks/viz-render/useHeatmapVizRender.d.ts.map +1 -0
- package/src/dist/hooks/viz-render/useReplayRender.d.ts +11 -0
- package/src/dist/hooks/viz-render/useReplayRender.d.ts.map +1 -0
- package/src/dist/hooks/viz-scale/index.d.ts +2 -0
- package/src/dist/hooks/viz-scale/index.d.ts.map +1 -0
- package/src/dist/hooks/viz-scale/useContainerDimensions.d.ts +10 -0
- package/src/dist/hooks/viz-scale/useContainerDimensions.d.ts.map +1 -0
- package/src/dist/hooks/viz-scale/useContentDimensions.d.ts +11 -0
- package/src/dist/hooks/viz-scale/useContentDimensions.d.ts.map +1 -0
- package/src/dist/hooks/viz-scale/useHeatmapScale.d.ts +19 -0
- package/src/dist/hooks/viz-scale/useHeatmapScale.d.ts.map +1 -0
- package/src/dist/hooks/viz-scale/useIframeHeight.d.ts +10 -0
- package/src/dist/hooks/viz-scale/useIframeHeight.d.ts.map +1 -0
- package/src/dist/hooks/viz-scale/useScaleCalculation.d.ts +10 -0
- package/src/dist/hooks/viz-scale/useScaleCalculation.d.ts.map +1 -0
- package/src/dist/hooks/viz-scale/useScrollSync.d.ts +10 -0
- package/src/dist/hooks/viz-scale/useScrollSync.d.ts.map +1 -0
- package/src/dist/index.d.ts +4 -0
- package/src/dist/index.d.ts.map +1 -0
- package/src/dist/stores/data.d.ts +18 -0
- package/src/dist/stores/data.d.ts.map +1 -0
- package/src/dist/stores/index.d.ts +2 -0
- package/src/dist/stores/index.d.ts.map +1 -0
- package/src/dist/ui/BoxStack/BoxStack.d.ts +56 -0
- package/src/dist/ui/BoxStack/BoxStack.d.ts.map +1 -0
- package/src/dist/ui/BoxStack/index.d.ts +2 -0
- package/src/dist/ui/BoxStack/index.d.ts.map +1 -0
- package/src/dist/ui/index.d.ts +2 -0
- package/src/dist/ui/index.d.ts.map +1 -0
- package/src/dist/utils/device.d.ts +2 -0
- package/src/dist/utils/device.d.ts.map +1 -0
- package/src/dist/utils/retry.d.ts +2 -0
- package/src/dist/utils/retry.d.ts.map +1 -0
- package/src/dist/utils/sort.d.ts +3 -0
- package/src/dist/utils/sort.d.ts.map +1 -0
- package/src/global.d.ts +5 -0
- package/src/helpers/iframe.ts +33 -0
- package/src/helpers/index.ts +1 -0
- package/src/helpers/viz-elements.ts +34 -0
- package/src/hooks/index.ts +3 -0
- package/src/hooks/vix-elements/index.ts +4 -0
- package/src/hooks/vix-elements/useClickedElement.ts +62 -0
- package/src/hooks/vix-elements/useHeatmapEffects.ts +29 -0
- package/src/hooks/vix-elements/useHeatmapElementPosition.ts +66 -0
- package/src/hooks/vix-elements/useHoveredElement.ts +135 -0
- package/src/hooks/viz-render/index.ts +1 -0
- package/src/hooks/viz-render/useHeatmapRender.ts +98 -0
- package/src/hooks/viz-render/useHeatmapVizRender.ts +22 -0
- package/src/hooks/viz-render/useReplayRender.ts +162 -0
- package/src/hooks/viz-scale/index.ts +1 -0
- package/src/hooks/viz-scale/useContainerDimensions.ts +46 -0
- package/src/hooks/viz-scale/useContentDimensions.ts +29 -0
- package/src/hooks/viz-scale/useHeatmapScale.ts +53 -0
- package/src/hooks/viz-scale/useIframeHeight.ts +119 -0
- package/src/hooks/viz-scale/useScaleCalculation.ts +28 -0
- package/src/hooks/viz-scale/useScrollSync.ts +36 -0
- package/src/index.ts +5 -0
- package/src/stores/data.ts +34 -0
- package/src/stores/index.ts +1 -0
- package/src/styles/base.css +1 -0
- package/src/styles/style.css +30 -0
- package/src/types/clarity.d.ts +38 -0
- package/src/types/heatmap.d.ts +3 -0
- package/src/types/index.d.ts +3 -0
- package/src/types/viz-element.d.ts +39 -0
- package/src/ui/BoxStack/BoxStack.tsx +120 -0
- package/src/ui/BoxStack/index.ts +1 -0
- package/src/ui/index.ts +1 -0
- package/src/utils/device.ts +7 -0
- package/src/utils/retry.ts +20 -0
- package/src/utils/sort.ts +5 -0
|
@@ -0,0 +1,764 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// Type Definitions
|
|
3
|
+
// ============================================================================
|
|
4
|
+
|
|
5
|
+
interface ViewNode {
|
|
6
|
+
x: number;
|
|
7
|
+
y: number;
|
|
8
|
+
width: number;
|
|
9
|
+
height: number;
|
|
10
|
+
visible: boolean;
|
|
11
|
+
hash?: string;
|
|
12
|
+
hashAlpha?: string;
|
|
13
|
+
hashBeta?: string;
|
|
14
|
+
renderNodeId?: number;
|
|
15
|
+
isWebView?: boolean;
|
|
16
|
+
selectorAlpha?: string;
|
|
17
|
+
selectorBeta?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface WebViewInfo {
|
|
21
|
+
renderNodeId: number;
|
|
22
|
+
visualizer: any;
|
|
23
|
+
div: HTMLDivElement;
|
|
24
|
+
scale: number;
|
|
25
|
+
top: number;
|
|
26
|
+
left: number;
|
|
27
|
+
visible: boolean;
|
|
28
|
+
orderInFrame?: number;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
interface ViewHierarchy {
|
|
32
|
+
root: ViewNode;
|
|
33
|
+
timestamp?: number;
|
|
34
|
+
visibleFragments?: string[];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
interface Frame {
|
|
38
|
+
viewHierarchy: ViewHierarchy;
|
|
39
|
+
screenWidth: number;
|
|
40
|
+
screenHeight: number;
|
|
41
|
+
density: number;
|
|
42
|
+
keyboardHeight?: number;
|
|
43
|
+
systemBackgroundColor?: number | null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
interface ClarityVisualizerState {
|
|
47
|
+
div?: HTMLElement;
|
|
48
|
+
window?: Window;
|
|
49
|
+
options?: {
|
|
50
|
+
version?: string;
|
|
51
|
+
platform?: number;
|
|
52
|
+
mute?: boolean;
|
|
53
|
+
canvas?: boolean;
|
|
54
|
+
keyframes?: boolean;
|
|
55
|
+
canvasId?: string;
|
|
56
|
+
heatmap?: boolean;
|
|
57
|
+
logError?: (error: any) => void;
|
|
58
|
+
fetchAssets?: (assets: any) => Promise<any>;
|
|
59
|
+
clickmapScrolledCallback?: () => void;
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
interface NodeLayout {
|
|
64
|
+
x: number;
|
|
65
|
+
y: number;
|
|
66
|
+
width: number;
|
|
67
|
+
height: number;
|
|
68
|
+
isNative?: boolean;
|
|
69
|
+
htmlElement?: HTMLElement;
|
|
70
|
+
selector?: string;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
interface ElementWithHash {
|
|
74
|
+
top: number;
|
|
75
|
+
left: number;
|
|
76
|
+
width: number;
|
|
77
|
+
height: number;
|
|
78
|
+
hash: string;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
interface ResizeEvent {
|
|
82
|
+
event: number;
|
|
83
|
+
time: number;
|
|
84
|
+
webViewRenderNodeId: number;
|
|
85
|
+
data: {
|
|
86
|
+
width: number;
|
|
87
|
+
height: number;
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
interface MutationEvent {
|
|
92
|
+
isKeyFrame: boolean;
|
|
93
|
+
serializedFrame: string;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// ============================================================================
|
|
97
|
+
// ClarityVisualizer Class Implementation
|
|
98
|
+
// ============================================================================
|
|
99
|
+
|
|
100
|
+
export class ClarityVisualizer {
|
|
101
|
+
// State properties
|
|
102
|
+
public state: ClarityVisualizerState | null = null;
|
|
103
|
+
public webViews: { [key: number]: WebViewInfo } = {};
|
|
104
|
+
public lastFrame: Frame | null = null;
|
|
105
|
+
public hashMapAlpha: { [key: string]: ViewNode } = {};
|
|
106
|
+
public hashMapBeta: { [key: string]: ViewNode } = {};
|
|
107
|
+
|
|
108
|
+
private lastWidth: number | null = null;
|
|
109
|
+
private lastHeight: number | null = null;
|
|
110
|
+
|
|
111
|
+
// Canvas/rendering wrapper (simplified - you'll need actual implementation)
|
|
112
|
+
private ckWrapper: any = null;
|
|
113
|
+
public heatmap: any = null;
|
|
114
|
+
|
|
115
|
+
// ============================================================================
|
|
116
|
+
// Constructor
|
|
117
|
+
// ============================================================================
|
|
118
|
+
|
|
119
|
+
constructor(state?: ClarityVisualizerState) {
|
|
120
|
+
if (state) {
|
|
121
|
+
this.setState(state);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// ============================================================================
|
|
126
|
+
// State Management
|
|
127
|
+
// ============================================================================
|
|
128
|
+
|
|
129
|
+
public getState(): ClarityVisualizerState | null {
|
|
130
|
+
return this.state;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
public setState(state: ClarityVisualizerState): void {
|
|
134
|
+
this.state = state;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// ============================================================================
|
|
138
|
+
// Initialization
|
|
139
|
+
// ============================================================================
|
|
140
|
+
|
|
141
|
+
public async init(div: HTMLElement, canvasId?: string, heatmap?: boolean): Promise<void> {
|
|
142
|
+
this.webViews = {};
|
|
143
|
+
this.lastFrame = null;
|
|
144
|
+
|
|
145
|
+
// Initialize state
|
|
146
|
+
this.state = {
|
|
147
|
+
div,
|
|
148
|
+
options: {
|
|
149
|
+
...this.state?.options,
|
|
150
|
+
canvasId,
|
|
151
|
+
heatmap,
|
|
152
|
+
},
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
// Create overlay canvas if not in heatmap mode
|
|
156
|
+
if (!heatmap) {
|
|
157
|
+
const canvas = this.overlay(div, canvasId);
|
|
158
|
+
// TODO: Initialize canvas wrapper
|
|
159
|
+
// await this.ckWrapper?.setBaseCanvas(canvas);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
public reset(): void {
|
|
164
|
+
this.hashMapAlpha = {};
|
|
165
|
+
this.hashMapBeta = {};
|
|
166
|
+
this.lastFrame = null;
|
|
167
|
+
this.webViews = {};
|
|
168
|
+
|
|
169
|
+
if (this.state?.div) {
|
|
170
|
+
this.state.div.innerHTML = '';
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Reset canvas wrapper
|
|
174
|
+
this.ckWrapper?.reset();
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// ============================================================================
|
|
178
|
+
// Node Retrieval - Core Methods
|
|
179
|
+
// ============================================================================
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Get node layout by hash - matches fr.get() implementation
|
|
183
|
+
*/
|
|
184
|
+
public get(
|
|
185
|
+
hash: string,
|
|
186
|
+
checkViewport: boolean = false,
|
|
187
|
+
scrollIntoView: boolean = false,
|
|
188
|
+
): NodeLayout | null {
|
|
189
|
+
// Check hashMapBeta first (primary)
|
|
190
|
+
if (hash in this.hashMapBeta && this.hashMapBeta[hash].visible) {
|
|
191
|
+
const node = this.hashMapBeta[hash];
|
|
192
|
+
return {
|
|
193
|
+
x: node.x,
|
|
194
|
+
y: node.y,
|
|
195
|
+
width: node.width,
|
|
196
|
+
height: node.height,
|
|
197
|
+
isNative: true,
|
|
198
|
+
selector: node.selectorBeta,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Check hashMapAlpha (fallback)
|
|
203
|
+
if (hash in this.hashMapAlpha && this.hashMapAlpha[hash].visible) {
|
|
204
|
+
const node = this.hashMapAlpha[hash];
|
|
205
|
+
return {
|
|
206
|
+
x: node.x,
|
|
207
|
+
y: node.y,
|
|
208
|
+
width: node.width,
|
|
209
|
+
height: node.height,
|
|
210
|
+
isNative: true,
|
|
211
|
+
selector: node.selectorAlpha,
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Check WebViews
|
|
216
|
+
const visibleWebViews = Object.values(this.webViews).filter((wv) => wv.visible);
|
|
217
|
+
|
|
218
|
+
for (const webView of visibleWebViews) {
|
|
219
|
+
const element = webView.visualizer?.get?.(hash);
|
|
220
|
+
|
|
221
|
+
if (element) {
|
|
222
|
+
// Check if element is in viewport if required
|
|
223
|
+
if (checkViewport && !this.isElementInViewport(element, webView.div)) {
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Scroll element into view if requested
|
|
228
|
+
if (scrollIntoView && element) {
|
|
229
|
+
this.scrollElementIntoView(element, webView);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const layout = this.getNodeLayout(webView, element);
|
|
233
|
+
if (layout) {
|
|
234
|
+
return {
|
|
235
|
+
...layout,
|
|
236
|
+
isNative: false,
|
|
237
|
+
htmlElement: element,
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
return null;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Get all elements at given coordinates - matches fr.getViews() implementation
|
|
248
|
+
*/
|
|
249
|
+
public getViews(x: number, y: number, useAlpha: boolean): ElementWithHash[] {
|
|
250
|
+
if (!this.lastFrame) return [];
|
|
251
|
+
|
|
252
|
+
const root = this.lastFrame.viewHierarchy.root;
|
|
253
|
+
const elements: ElementWithHash[] = [];
|
|
254
|
+
|
|
255
|
+
// Traverse view hierarchy for native elements
|
|
256
|
+
this.traverseViewHierarchy(this.lastFrame.viewHierarchy, (node: ViewNode) => {
|
|
257
|
+
if (
|
|
258
|
+
x > node.x &&
|
|
259
|
+
x < node.x + node.width &&
|
|
260
|
+
y > node.y &&
|
|
261
|
+
y < node.y + node.height &&
|
|
262
|
+
node.visible &&
|
|
263
|
+
this.intersectRect(node, root)
|
|
264
|
+
) {
|
|
265
|
+
const hash = useAlpha ? node.hashAlpha : node.hashBeta;
|
|
266
|
+
if (hash) {
|
|
267
|
+
elements.push({
|
|
268
|
+
left: node.x,
|
|
269
|
+
top: node.y,
|
|
270
|
+
width: node.width,
|
|
271
|
+
height: node.height,
|
|
272
|
+
hash,
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
// Check WebView elements
|
|
279
|
+
const hashAttribute = useAlpha ? 'data-clarity-hashalpha' : 'data-clarity-hashbeta';
|
|
280
|
+
const processedHashes: { [key: string]: boolean } = {};
|
|
281
|
+
|
|
282
|
+
const checkWebViewElements = (
|
|
283
|
+
doc: Document | ShadowRoot | null,
|
|
284
|
+
clientX: number,
|
|
285
|
+
clientY: number,
|
|
286
|
+
webView: WebViewInfo,
|
|
287
|
+
) => {
|
|
288
|
+
if (!doc) return;
|
|
289
|
+
|
|
290
|
+
const elementsAtPoint = doc.elementsFromPoint?.(clientX, clientY);
|
|
291
|
+
if (!elementsAtPoint) return;
|
|
292
|
+
|
|
293
|
+
// Reverse to get top-most elements first
|
|
294
|
+
elementsAtPoint.reverse();
|
|
295
|
+
|
|
296
|
+
for (const element of elementsAtPoint) {
|
|
297
|
+
const hash = element.getAttribute(hashAttribute);
|
|
298
|
+
|
|
299
|
+
if (hash && !processedHashes[hash]) {
|
|
300
|
+
processedHashes[hash] = true;
|
|
301
|
+
|
|
302
|
+
const layout = this.getNodeLayout(webView, element as HTMLElement);
|
|
303
|
+
if (layout) {
|
|
304
|
+
elements.push({
|
|
305
|
+
top: layout.y,
|
|
306
|
+
left: layout.x,
|
|
307
|
+
width: layout.width,
|
|
308
|
+
height: layout.height,
|
|
309
|
+
hash,
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// Check shadow root
|
|
314
|
+
const shadowRoot = (element as HTMLElement).shadowRoot;
|
|
315
|
+
if (shadowRoot) {
|
|
316
|
+
checkWebViewElements(shadowRoot, clientX, clientY, webView);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
// Process all visible webviews
|
|
323
|
+
for (const webView of Object.values(this.webViews)) {
|
|
324
|
+
if (webView.visible && webView.visualizer?.state?.window?.document) {
|
|
325
|
+
const doc = webView.visualizer.state.window.document;
|
|
326
|
+
const localX = x / webView.scale - webView.left;
|
|
327
|
+
const localY = y / webView.scale - webView.top;
|
|
328
|
+
|
|
329
|
+
checkWebViewElements(doc, Math.round(localX), Math.round(localY), webView);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
return elements;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Get node layout from HTML element
|
|
338
|
+
*/
|
|
339
|
+
public getNodeLayout(webView: WebViewInfo, element: HTMLElement): NodeLayout | null {
|
|
340
|
+
if (typeof element.getBoundingClientRect !== 'function') {
|
|
341
|
+
return null;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
const rect = element.getBoundingClientRect();
|
|
345
|
+
const scrollLeft =
|
|
346
|
+
'pageXOffset' in window ? window.pageXOffset : document.documentElement.scrollLeft;
|
|
347
|
+
const scrollTop =
|
|
348
|
+
'pageYOffset' in window ? window.pageYOffset : document.documentElement.scrollTop;
|
|
349
|
+
|
|
350
|
+
if (rect && (rect.height !== 0 || rect.width !== 0)) {
|
|
351
|
+
return {
|
|
352
|
+
x:
|
|
353
|
+
(Math.floor(rect.left + scrollLeft) + webView.left - webView.div.scrollLeft) *
|
|
354
|
+
webView.scale,
|
|
355
|
+
y: (Math.floor(rect.top + scrollTop) + webView.top - webView.div.scrollTop) * webView.scale,
|
|
356
|
+
width: Math.floor(rect.width) * webView.scale,
|
|
357
|
+
height: Math.floor(rect.height) * webView.scale,
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
return null;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// ============================================================================
|
|
365
|
+
// Frame Processing
|
|
366
|
+
// ============================================================================
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* Process mutation event and update frame
|
|
370
|
+
*/
|
|
371
|
+
public async mutation(event: MutationEvent): Promise<void> {
|
|
372
|
+
try {
|
|
373
|
+
// Parse frame
|
|
374
|
+
if (event.isKeyFrame) {
|
|
375
|
+
this.lastFrame = JSON.parse(event.serializedFrame);
|
|
376
|
+
} else {
|
|
377
|
+
// Apply incremental update (you'll need to implement merge logic)
|
|
378
|
+
if (this.lastFrame) {
|
|
379
|
+
this.applyFrameUpdate(this.lastFrame, JSON.parse(event.serializedFrame));
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// Update hash maps
|
|
384
|
+
this.updateHashMaps();
|
|
385
|
+
|
|
386
|
+
// Handle resize if dimensions changed
|
|
387
|
+
if (this.lastFrame) {
|
|
388
|
+
if (
|
|
389
|
+
this.lastFrame.screenWidth !== this.lastWidth ||
|
|
390
|
+
this.lastFrame.screenHeight !== this.lastHeight
|
|
391
|
+
) {
|
|
392
|
+
await this.resize({
|
|
393
|
+
event: 11,
|
|
394
|
+
time: 0,
|
|
395
|
+
webViewRenderNodeId: -2,
|
|
396
|
+
data: {
|
|
397
|
+
width: this.lastFrame.screenWidth,
|
|
398
|
+
height: this.lastFrame.screenHeight,
|
|
399
|
+
},
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// Update WebViews
|
|
404
|
+
await this.updateWebViews();
|
|
405
|
+
|
|
406
|
+
// Draw frame
|
|
407
|
+
// await this.ckWrapper?.drawFrame(this.lastFrame);
|
|
408
|
+
|
|
409
|
+
// Draw keyboard if present
|
|
410
|
+
const keyboardHeight = this.lastFrame.keyboardHeight ?? 0;
|
|
411
|
+
this.drawKeyboard(keyboardHeight / this.lastFrame.screenHeight);
|
|
412
|
+
|
|
413
|
+
// Set background color
|
|
414
|
+
this.setSystemBackgroundColor(this.lastFrame.systemBackgroundColor ?? null);
|
|
415
|
+
}
|
|
416
|
+
} catch (error) {
|
|
417
|
+
this.state?.options?.logError?.(error);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* Update hash maps from current frame
|
|
423
|
+
*/
|
|
424
|
+
public updateHashMaps(): void {
|
|
425
|
+
this.hashMapAlpha = {};
|
|
426
|
+
this.hashMapBeta = {};
|
|
427
|
+
|
|
428
|
+
if (!this.lastFrame) return;
|
|
429
|
+
|
|
430
|
+
this.traverseViewHierarchy(this.lastFrame.viewHierarchy, (node: ViewNode) => {
|
|
431
|
+
if (node.visible) {
|
|
432
|
+
if (node.hashAlpha) {
|
|
433
|
+
this.hashMapAlpha[node.hashAlpha] = node;
|
|
434
|
+
}
|
|
435
|
+
if (node.hashBeta) {
|
|
436
|
+
this.hashMapBeta[node.hashBeta] = node;
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
/**
|
|
443
|
+
* Handle resize event
|
|
444
|
+
*/
|
|
445
|
+
public async resize(event: ResizeEvent): Promise<void> {
|
|
446
|
+
const canvas = document.getElementById('clarity-android-canvas') as HTMLCanvasElement;
|
|
447
|
+
|
|
448
|
+
if (canvas) {
|
|
449
|
+
canvas.width = event.data.width;
|
|
450
|
+
canvas.height = event.data.height;
|
|
451
|
+
this.lastWidth = event.data.width;
|
|
452
|
+
this.lastHeight = event.data.height;
|
|
453
|
+
|
|
454
|
+
// await this.ckWrapper?.setBaseCanvas(canvas);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
// ============================================================================
|
|
459
|
+
// WebView Management
|
|
460
|
+
// ============================================================================
|
|
461
|
+
|
|
462
|
+
public getWebViewWindows(): Window[] {
|
|
463
|
+
return Object.keys(this.webViews)
|
|
464
|
+
.map(Number)
|
|
465
|
+
.map((id) => this.webViews[id].visualizer?.state?.window)
|
|
466
|
+
.filter(Boolean);
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
public webViewDivs(): HTMLDivElement[] {
|
|
470
|
+
return Object.keys(this.webViews)
|
|
471
|
+
.map(Number)
|
|
472
|
+
.map((id) => this.webViews[id].div);
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
private async updateWebViews(): Promise<void> {
|
|
476
|
+
if (!this.lastFrame) return;
|
|
477
|
+
|
|
478
|
+
const webViewNodes = this.getWebViewNodes(this.lastFrame.viewHierarchy);
|
|
479
|
+
this.createWebViewDivs(webViewNodes);
|
|
480
|
+
this.hideWebViewDivs(webViewNodes);
|
|
481
|
+
|
|
482
|
+
// Update WebView positions and visibility
|
|
483
|
+
for (const node of webViewNodes) {
|
|
484
|
+
if (this.webViews[node.renderNodeId!]) {
|
|
485
|
+
const webView = this.webViews[node.renderNodeId!];
|
|
486
|
+
webView.visible = node.visible;
|
|
487
|
+
|
|
488
|
+
if (webView.div) {
|
|
489
|
+
webView.div.style.visibility = node.visible ? 'visible' : 'hidden';
|
|
490
|
+
webView.div.style.position = 'absolute';
|
|
491
|
+
webView.div.style.top = `${node.y}px`;
|
|
492
|
+
webView.div.style.left = `${node.x}px`;
|
|
493
|
+
webView.div.style.width = `${node.width}px`;
|
|
494
|
+
webView.div.style.height = `${node.height}px`;
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
private createWebViewDivs(nodes: ViewNode[]): void {
|
|
501
|
+
for (const node of nodes) {
|
|
502
|
+
if (node.renderNodeId && !this.webViews[node.renderNodeId]) {
|
|
503
|
+
const div = document.createElement('div');
|
|
504
|
+
div.id = `clarity-webview-div-${node.renderNodeId}`;
|
|
505
|
+
div.style.visibility = 'hidden';
|
|
506
|
+
|
|
507
|
+
this.state?.div?.appendChild(div);
|
|
508
|
+
|
|
509
|
+
this.webViews[node.renderNodeId] = {
|
|
510
|
+
renderNodeId: node.renderNodeId,
|
|
511
|
+
visualizer: null,
|
|
512
|
+
div,
|
|
513
|
+
scale: 1,
|
|
514
|
+
top: 0,
|
|
515
|
+
left: 0,
|
|
516
|
+
visible: false,
|
|
517
|
+
};
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
private hideWebViewDivs(visibleNodes: ViewNode[]): void {
|
|
523
|
+
const visibleIds = new Set(visibleNodes.map((n) => n.renderNodeId).filter(Boolean));
|
|
524
|
+
|
|
525
|
+
for (const id of Object.keys(this.webViews).map(Number)) {
|
|
526
|
+
if (!visibleIds.has(id)) {
|
|
527
|
+
this.webViews[id].div.style.visibility = 'hidden';
|
|
528
|
+
this.webViews[id].visible = false;
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
// ============================================================================
|
|
534
|
+
// Callback Setup
|
|
535
|
+
// ============================================================================
|
|
536
|
+
|
|
537
|
+
public setAndroidClickmapScrolledCallback(callback: () => void): void {
|
|
538
|
+
if (this.state?.options) {
|
|
539
|
+
this.state.options.clickmapScrolledCallback = callback;
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
// ============================================================================
|
|
544
|
+
// Asset Management
|
|
545
|
+
// ============================================================================
|
|
546
|
+
|
|
547
|
+
public async preloadImpressionAssets(frames: Frame[]): Promise<void> {
|
|
548
|
+
// TODO: Implement asset preloading logic
|
|
549
|
+
console.log('Preloading assets for', frames.length, 'frames');
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
// ============================================================================
|
|
553
|
+
// Helper Methods
|
|
554
|
+
// ============================================================================
|
|
555
|
+
|
|
556
|
+
private traverseViewHierarchy(
|
|
557
|
+
hierarchy: ViewHierarchy,
|
|
558
|
+
callback: (node: ViewNode) => void,
|
|
559
|
+
): void {
|
|
560
|
+
const traverse = (node: ViewNode) => {
|
|
561
|
+
callback(node);
|
|
562
|
+
// If node has children, traverse them (you'll need to add children property)
|
|
563
|
+
// For now, just call on root
|
|
564
|
+
};
|
|
565
|
+
|
|
566
|
+
traverse(hierarchy.root);
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
private getWebViewNodes(hierarchy: ViewHierarchy): ViewNode[] {
|
|
570
|
+
const webViewNodes: ViewNode[] = [];
|
|
571
|
+
|
|
572
|
+
this.traverseViewHierarchy(hierarchy, (node) => {
|
|
573
|
+
if (node.isWebView) {
|
|
574
|
+
webViewNodes.push(node);
|
|
575
|
+
}
|
|
576
|
+
});
|
|
577
|
+
|
|
578
|
+
return webViewNodes;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
private intersectRect(rect1: ViewNode, rect2: ViewNode): boolean {
|
|
582
|
+
return !(
|
|
583
|
+
rect2.x > rect1.x + rect1.width ||
|
|
584
|
+
rect2.x + rect2.width < rect1.x ||
|
|
585
|
+
rect2.y > rect1.y + rect1.height ||
|
|
586
|
+
rect2.y + rect2.height < rect1.y
|
|
587
|
+
);
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
private isElementInViewport(element: HTMLElement, container: HTMLDivElement): boolean {
|
|
591
|
+
const rect = element.getBoundingClientRect();
|
|
592
|
+
|
|
593
|
+
return this.intersectRect(
|
|
594
|
+
{
|
|
595
|
+
x: rect.left,
|
|
596
|
+
y: rect.top,
|
|
597
|
+
width: rect.width,
|
|
598
|
+
height: rect.height,
|
|
599
|
+
visible: true,
|
|
600
|
+
},
|
|
601
|
+
{
|
|
602
|
+
x: container.scrollLeft,
|
|
603
|
+
y: container.scrollTop,
|
|
604
|
+
width: container.clientWidth,
|
|
605
|
+
height: container.clientHeight,
|
|
606
|
+
visible: true,
|
|
607
|
+
},
|
|
608
|
+
);
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
private scrollElementIntoView(element: HTMLElement, webView: WebViewInfo): void {
|
|
612
|
+
element.style.scrollMarginBlock = '100px';
|
|
613
|
+
element.style.scrollMarginInline = '50px';
|
|
614
|
+
|
|
615
|
+
// Add scroll listener to redraw heatmap
|
|
616
|
+
const onScroll = () => {
|
|
617
|
+
setTimeout(() => {
|
|
618
|
+
this.heatmap?.redraw?.(null);
|
|
619
|
+
}, 200);
|
|
620
|
+
};
|
|
621
|
+
|
|
622
|
+
// Add scroll listeners to all parent elements
|
|
623
|
+
let parent: Node | null = element;
|
|
624
|
+
while (parent) {
|
|
625
|
+
parent =
|
|
626
|
+
parent.parentNode ||
|
|
627
|
+
(parent.nodeType === Node.DOCUMENT_FRAGMENT_NODE ? (parent as any).host : null);
|
|
628
|
+
|
|
629
|
+
if (parent) {
|
|
630
|
+
(parent as Element).addEventListener?.('scroll', onScroll, {
|
|
631
|
+
capture: true,
|
|
632
|
+
once: false,
|
|
633
|
+
passive: true,
|
|
634
|
+
});
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
element.scrollIntoView({
|
|
639
|
+
behavior: 'smooth',
|
|
640
|
+
block: 'start',
|
|
641
|
+
inline: 'nearest',
|
|
642
|
+
});
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
private overlay(div: HTMLElement, canvasId?: string): HTMLCanvasElement {
|
|
646
|
+
const id = `clarity-android-canvas${canvasId ? `-${canvasId}` : ''}`;
|
|
647
|
+
let canvas = document.getElementById(id) as HTMLCanvasElement;
|
|
648
|
+
|
|
649
|
+
if (canvas?.parentNode) {
|
|
650
|
+
canvas.parentNode.removeChild(canvas);
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
canvas = document.createElement('canvas');
|
|
654
|
+
canvas.id = id;
|
|
655
|
+
canvas.width = div.offsetWidth;
|
|
656
|
+
canvas.height = div.offsetHeight;
|
|
657
|
+
canvas.style.position = 'absolute';
|
|
658
|
+
canvas.style.top = '0';
|
|
659
|
+
canvas.style.left = '0';
|
|
660
|
+
|
|
661
|
+
div.prepend(canvas);
|
|
662
|
+
|
|
663
|
+
return canvas;
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
private drawKeyboard(heightRatio: number): void {
|
|
667
|
+
if (!this.state?.div) return;
|
|
668
|
+
|
|
669
|
+
const doc = document;
|
|
670
|
+
let keyboardLayer = doc.getElementById('clarity-keyboard-layer');
|
|
671
|
+
|
|
672
|
+
if (heightRatio > 0 && !keyboardLayer) {
|
|
673
|
+
keyboardLayer = this.createKeyboardLayer(doc, heightRatio);
|
|
674
|
+
this.state.div.appendChild(keyboardLayer);
|
|
675
|
+
} else if (heightRatio <= 0 && keyboardLayer) {
|
|
676
|
+
keyboardLayer.remove();
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
private createKeyboardLayer(doc: Document, heightRatio: number): HTMLDivElement {
|
|
681
|
+
const layer = doc.createElement('div');
|
|
682
|
+
layer.id = 'clarity-keyboard-layer';
|
|
683
|
+
|
|
684
|
+
const style = doc.createElement('style');
|
|
685
|
+
style.textContent = this.getKeyboardLayerStyle(heightRatio);
|
|
686
|
+
layer.appendChild(style);
|
|
687
|
+
layer.setAttribute('title', 'Keyboard Event');
|
|
688
|
+
|
|
689
|
+
return layer;
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
private getKeyboardLayerStyle(heightRatio: number): string {
|
|
693
|
+
return `
|
|
694
|
+
@keyframes fade-in {
|
|
695
|
+
0% { bottom: -100% }
|
|
696
|
+
100% { bottom: 0 }
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
#clarity-keyboard-layer {
|
|
700
|
+
position: absolute;
|
|
701
|
+
bottom: 0;
|
|
702
|
+
left: 0;
|
|
703
|
+
z-index: 2147483647;
|
|
704
|
+
width: 100%;
|
|
705
|
+
height: ${100 * heightRatio}%;
|
|
706
|
+
background-repeat: no-repeat;
|
|
707
|
+
background-color: #EDEBE9;
|
|
708
|
+
background-size: 100% 100%;
|
|
709
|
+
background-position: center center;
|
|
710
|
+
animation: fade-in 0.5s ease-in-out;
|
|
711
|
+
}
|
|
712
|
+
`;
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
private setSystemBackgroundColor(color: number | null): void {
|
|
716
|
+
if (!this.state?.div) return;
|
|
717
|
+
|
|
718
|
+
if (color !== null) {
|
|
719
|
+
const rgba = this.colorIntToRgba(color);
|
|
720
|
+
this.state.div.style.backgroundColor = `rgba(${rgba[0]}, ${rgba[1]}, ${rgba[2]}, ${rgba[3]})`;
|
|
721
|
+
} else {
|
|
722
|
+
this.state.div.style.backgroundColor = '';
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
private colorIntToRgba(color: number): [number, number, number, number] {
|
|
727
|
+
return [
|
|
728
|
+
(color >> 16) & 0xff, // R
|
|
729
|
+
(color >> 8) & 0xff, // G
|
|
730
|
+
color & 0xff, // B
|
|
731
|
+
((color >> 24) & 0xff) / 255, // A
|
|
732
|
+
];
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
private applyFrameUpdate(baseFrame: Frame, update: Partial<Frame>): void {
|
|
736
|
+
// TODO: Implement incremental frame update logic
|
|
737
|
+
// This should merge the update into the base frame
|
|
738
|
+
Object.assign(baseFrame, update);
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
public getWebViewRenderNodeIdsFromKeyFrame(event: MutationEvent): Set<number> {
|
|
742
|
+
const ids = new Set<number>();
|
|
743
|
+
|
|
744
|
+
if (!event.isKeyFrame) return ids;
|
|
745
|
+
|
|
746
|
+
try {
|
|
747
|
+
const frame = JSON.parse(event.serializedFrame);
|
|
748
|
+
|
|
749
|
+
if (frame?.viewHierarchy?.root) {
|
|
750
|
+
this.traverseViewHierarchy(frame.viewHierarchy, (node) => {
|
|
751
|
+
if (node.isWebView && node.renderNodeId) {
|
|
752
|
+
ids.add(node.renderNodeId);
|
|
753
|
+
}
|
|
754
|
+
});
|
|
755
|
+
}
|
|
756
|
+
} catch (error) {
|
|
757
|
+
console.error('Failed to parse keyframe:', error);
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
return ids;
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
export default ClarityVisualizer;
|