diff --git a/assets/visualizer/index.html b/assets/visualizer/index.html new file mode 100644 index 0000000..90dc36f --- /dev/null +++ b/assets/visualizer/index.html @@ -0,0 +1,28 @@ + + + + + UI Dump Viewer + + + +
+ + +
+
+
+ screenshot +
+
+
+ + + diff --git a/assets/visualizer/script.js b/assets/visualizer/script.js new file mode 100644 index 0000000..c27511f --- /dev/null +++ b/assets/visualizer/script.js @@ -0,0 +1,70 @@ +let dumpWidth = 1080; +let dumpHeight = 1920; + +document.getElementById('xmlInput').addEventListener('change', handleXml); +document.getElementById('imgInput').addEventListener('change', handleImage); + +function handleImage(event) { + const file = event.target.files[0]; + if (!file) return; + + const url = URL.createObjectURL(file); + document.getElementById('screenshot').src = url; +} + +async function handleXml(event) { + const file = event.target.files[0]; + if (!file) return; + + const text = await file.text(); + const parser = new DOMParser(); + const xml = parser.parseFromString(text, "text/xml"); + + const nodes = Array.from(xml.querySelectorAll("node")); + const canvas = document.getElementById("canvas"); + canvas.innerHTML = ""; + + // Автоопределение размеров дампа + let maxX = 0, maxY = 0; + for (const node of nodes) { + const bounds = node.getAttribute("bounds"); + const match = bounds && bounds.match(/\[(\d+),(\d+)\]\[(\d+),(\d+)\]/); + if (!match) continue; + + const [, , , x2, y2] = match.map(Number); + if (x2 > maxX) maxX = x2; + if (y2 > maxY) maxY = y2; + } + dumpWidth = maxX; + dumpHeight = maxY; + + // Масштабирование по высоте экрана с отступами + const padding = 100; + const displayHeight = window.innerHeight - padding; + const scale = displayHeight / dumpHeight; + const displayWidth = dumpWidth * scale; + + // Установка размеров canvas + canvas.style.width = `${displayWidth}px`; + canvas.style.height = `${displayHeight}px`; + + for (const node of nodes) { + const bounds = node.getAttribute("bounds"); + const match = bounds && bounds.match(/\[(\d+),(\d+)\]\[(\d+),(\d+)\]/); + if (!match) continue; + + const [, x1, y1, x2, y2] = match.map(Number); + + const div = document.createElement("div"); + div.className = "rect"; + div.style.left = `${x1 * scale}px`; + div.style.top = `${y1 * scale}px`; + div.style.width = `${(x2 - x1) * scale}px`; + div.style.height = `${(y2 - y1) * scale}px`; + + const text = (node.getAttribute("text") || '').trim(); + if (text) div.innerText = text; + + canvas.appendChild(div); + } +} diff --git a/assets/visualizer/style.css b/assets/visualizer/style.css new file mode 100644 index 0000000..492c800 --- /dev/null +++ b/assets/visualizer/style.css @@ -0,0 +1,72 @@ +body, html { + margin: 0; + height: 100%; + font-family: sans-serif; + background-color: #f4f4f4; /* вернули светлый фон */ + color: #333; +} + +#layout { + display: flex; + height: 100vh; +} + +#sidebar { + width: 250px; + padding: 20px; + background: #ffffff; /* светлая боковая панель */ + border-right: 1px solid #ccc; + box-sizing: border-box; +} + +#sidebar h3, label { + color: #333; +} + +#sidebar input[type="file"] { + background: #fff; + color: #000; + border: 1px solid #ccc; + padding: 6px; + margin-top: 4px; + width: 100%; +} + +#content { + display: flex; + flex: 1; + align-items: center; + justify-content: space-around; + overflow: auto; + padding: 50px 20px; + box-sizing: border-box; + gap: 20px; + background-color: #f4f4f4; +} + +#canvas { + position: relative; + background: #2a2a2a; /* серый фон области дампа */ + border: 1px solid #444; + flex-shrink: 0; +} + +#screenshot-container { + flex-shrink: 0; +} + +#screenshot { + max-height: calc(100vh - 100px); + border: 1px solid #ccc; +} + +.rect { + position: absolute; + border: 1px dashed #ff9800; + background: transparent; + color: #ff9800; + font-size: 11px; + font-weight: bold; +} + +