Update dev-tools.js
Browse files- dev-tools.js +110 -48
dev-tools.js
CHANGED
@@ -15,6 +15,7 @@
|
|
15 |
};
|
16 |
let observer = null;
|
17 |
let refreshElementsPanel = null;
|
|
|
18 |
|
19 |
// Web Vitalsスクリプトを動的に読み込み
|
20 |
const loadWebVitals = () => {
|
@@ -31,7 +32,30 @@
|
|
31 |
});
|
32 |
};
|
33 |
|
34 |
-
//
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
35 |
const style = document.createElement('style');
|
36 |
style.textContent = `
|
37 |
:root {
|
@@ -578,32 +602,6 @@
|
|
578 |
}
|
579 |
`;
|
580 |
document.head.appendChild(style);
|
581 |
-
|
582 |
-
// DOM変更を監視するMutationObserver
|
583 |
-
const setupMutationObserver = () => {
|
584 |
-
observer = new MutationObserver((mutations) => {
|
585 |
-
mutations.forEach((mutation) => {
|
586 |
-
if (mutation.type === 'childList') {
|
587 |
-
mutation.addedNodes.forEach((node) => {
|
588 |
-
if (node.nodeType === Node.ELEMENT_NODE) {
|
589 |
-
highlightNode(node);
|
590 |
-
}
|
591 |
-
});
|
592 |
-
}
|
593 |
-
});
|
594 |
-
if (refreshElementsPanel) {
|
595 |
-
refreshElementsPanel();
|
596 |
-
}
|
597 |
-
});
|
598 |
-
|
599 |
-
observer.observe(document.documentElement, {
|
600 |
-
childList: true,
|
601 |
-
subtree: true,
|
602 |
-
attributes: true,
|
603 |
-
characterData: true
|
604 |
-
});
|
605 |
-
};
|
606 |
-
|
607 |
// ノードをハイライト表示
|
608 |
function highlightNode(node) {
|
609 |
const elementId = node.id || Math.random().toString(36).substr(2, 9);
|
@@ -709,6 +707,13 @@
|
|
709 |
container.className = 'vitals-container';
|
710 |
panel.appendChild(container);
|
711 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
712 |
// CLS (Cumulative Layout Shift)
|
713 |
const clsCard = document.createElement('div');
|
714 |
clsCard.className = 'vital-card';
|
@@ -779,29 +784,34 @@
|
|
779 |
`;
|
780 |
container.appendChild(ttfbCard);
|
781 |
|
782 |
-
|
783 |
-
|
784 |
-
|
785 |
-
|
786 |
-
|
787 |
-
|
788 |
-
|
789 |
-
vitals.
|
790 |
-
vitals.
|
791 |
-
|
792 |
-
|
793 |
-
|
794 |
-
|
795 |
-
|
796 |
-
vitals.
|
797 |
-
|
798 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
799 |
}
|
800 |
-
} catch (e) {
|
801 |
-
console.error('Web Vitals error:', e);
|
802 |
}
|
803 |
-
}
|
804 |
-
}
|
805 |
|
806 |
function updateCLS(metric) {
|
807 |
vitalsData.CLS = metric.value;
|
@@ -1216,6 +1226,7 @@
|
|
1216 |
contextMenu.className = 'context-menu';
|
1217 |
contextMenu.innerHTML = `
|
1218 |
<div class="context-menu-item" data-action="edit-html">HTMLとして編集</div>
|
|
|
1219 |
<div class="context-menu-item" data-action="add-attribute">属性を追加</div>
|
1220 |
<div class="context-menu-item" data-action="edit-element">要素を編集</div>
|
1221 |
<div class="context-menu-item" data-action="duplicate">要素を複製</div>
|
@@ -1488,6 +1499,57 @@
|
|
1488 |
}
|
1489 |
});
|
1490 |
break;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1491 |
case 'add-attribute':
|
1492 |
const attrName = prompt('属性名を入力');
|
1493 |
if (attrName) {
|
|
|
15 |
};
|
16 |
let observer = null;
|
17 |
let refreshElementsPanel = null;
|
18 |
+
let vitalsMeasurementStarted = false;
|
19 |
|
20 |
// Web Vitalsスクリプトを動的に読み込み
|
21 |
const loadWebVitals = () => {
|
|
|
32 |
});
|
33 |
};
|
34 |
|
35 |
+
// DOM変更を監視するMutationObserver
|
36 |
+
const setupMutationObserver = () => {
|
37 |
+
observer = new MutationObserver((mutations) => {
|
38 |
+
mutations.forEach((mutation) => {
|
39 |
+
if (mutation.type === 'childList') {
|
40 |
+
mutation.addedNodes.forEach((node) => {
|
41 |
+
if (node.nodeType === Node.ELEMENT_NODE) {
|
42 |
+
highlightNode(node);
|
43 |
+
}
|
44 |
+
});
|
45 |
+
}
|
46 |
+
});
|
47 |
+
if (refreshElementsPanel) {
|
48 |
+
refreshElementsPanel();
|
49 |
+
}
|
50 |
+
});
|
51 |
+
|
52 |
+
observer.observe(document.documentElement, {
|
53 |
+
childList: true,
|
54 |
+
subtree: true,
|
55 |
+
attributes: true,
|
56 |
+
characterData: true
|
57 |
+
});
|
58 |
+
};
|
59 |
const style = document.createElement('style');
|
60 |
style.textContent = `
|
61 |
:root {
|
|
|
602 |
}
|
603 |
`;
|
604 |
document.head.appendChild(style);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
605 |
// ノードをハイライト表示
|
606 |
function highlightNode(node) {
|
607 |
const elementId = node.id || Math.random().toString(36).substr(2, 9);
|
|
|
707 |
container.className = 'vitals-container';
|
708 |
panel.appendChild(container);
|
709 |
|
710 |
+
// 測定開始ボタン
|
711 |
+
const startButton = document.createElement('button');
|
712 |
+
startButton.className = 'add-btn';
|
713 |
+
startButton.textContent = '測定を開始';
|
714 |
+
startButton.onclick = startVitalsMeasurement;
|
715 |
+
container.appendChild(startButton);
|
716 |
+
|
717 |
// CLS (Cumulative Layout Shift)
|
718 |
const clsCard = document.createElement('div');
|
719 |
clsCard.className = 'vital-card';
|
|
|
784 |
`;
|
785 |
container.appendChild(ttfbCard);
|
786 |
|
787 |
+
function startVitalsMeasurement() {
|
788 |
+
if (vitalsMeasurementStarted) return;
|
789 |
+
vitalsMeasurementStarted = true;
|
790 |
+
|
791 |
+
loadWebVitals().then(() => {
|
792 |
+
if (window.webVitals) {
|
793 |
+
try {
|
794 |
+
const vitals = window.webVitals;
|
795 |
+
if (vitals.getCLS) {
|
796 |
+
vitals.getCLS(updateCLS);
|
797 |
+
vitals.getFCP(updateFCP);
|
798 |
+
vitals.getFID(updateFID);
|
799 |
+
vitals.getLCP(updateLCP);
|
800 |
+
vitals.getTTFB(updateTTFB);
|
801 |
+
} else if (vitals.onCLS) {
|
802 |
+
vitals.onCLS(updateCLS);
|
803 |
+
vitals.onFCP(updateFCP);
|
804 |
+
vitals.onFID(updateFID);
|
805 |
+
vitals.onLCP(updateLCP);
|
806 |
+
vitals.onTTFB(updateTTFB);
|
807 |
+
}
|
808 |
+
alert('Web Vitalsの測定を開始しました');
|
809 |
+
} catch (e) {
|
810 |
+
console.error('Web Vitals error:', e);
|
811 |
}
|
|
|
|
|
812 |
}
|
813 |
+
});
|
814 |
+
}
|
815 |
|
816 |
function updateCLS(metric) {
|
817 |
vitalsData.CLS = metric.value;
|
|
|
1226 |
contextMenu.className = 'context-menu';
|
1227 |
contextMenu.innerHTML = `
|
1228 |
<div class="context-menu-item" data-action="edit-html">HTMLとして編集</div>
|
1229 |
+
<div class="context-menu-item" data-action="edit-whole-html">HTML全体を編集</div>
|
1230 |
<div class="context-menu-item" data-action="add-attribute">属性を追加</div>
|
1231 |
<div class="context-menu-item" data-action="edit-element">要素を編集</div>
|
1232 |
<div class="context-menu-item" data-action="duplicate">要素を複製</div>
|
|
|
1499 |
}
|
1500 |
});
|
1501 |
break;
|
1502 |
+
case 'edit-whole-html':
|
1503 |
+
const htmlContent = document.documentElement.outerHTML;
|
1504 |
+
const textarea = document.createElement('textarea');
|
1505 |
+
textarea.style.width = '100%';
|
1506 |
+
textarea.style.height = '300px';
|
1507 |
+
textarea.value = htmlContent;
|
1508 |
+
|
1509 |
+
const modal = document.createElement('div');
|
1510 |
+
modal.style.position = 'fixed';
|
1511 |
+
modal.style.top = '0';
|
1512 |
+
modal.style.left = '0';
|
1513 |
+
modal.style.width = '100%';
|
1514 |
+
modal.style.height = '100%';
|
1515 |
+
modal.style.backgroundColor = 'rgba(0,0,0,0.8)';
|
1516 |
+
modal.style.zIndex = '10000';
|
1517 |
+
modal.style.display = 'flex';
|
1518 |
+
modal.style.flexDirection = 'column';
|
1519 |
+
modal.style.padding = '20px';
|
1520 |
+
modal.style.boxSizing = 'border-box';
|
1521 |
+
|
1522 |
+
const buttonContainer = document.createElement('div');
|
1523 |
+
buttonContainer.style.marginTop = '10px';
|
1524 |
+
buttonContainer.style.display = 'flex';
|
1525 |
+
buttonContainer.style.gap = '10px';
|
1526 |
+
|
1527 |
+
const saveButton = document.createElement('button');
|
1528 |
+
saveButton.textContent = '保存';
|
1529 |
+
saveButton.onclick = () => {
|
1530 |
+
try {
|
1531 |
+
document.documentElement.innerHTML = textarea.value;
|
1532 |
+
modal.remove();
|
1533 |
+
refreshElementsPanel();
|
1534 |
+
} catch (e) {
|
1535 |
+
alert('HTMLの解析に失敗しました: ' + e.message);
|
1536 |
+
}
|
1537 |
+
};
|
1538 |
+
|
1539 |
+
const cancelButton = document.createElement('button');
|
1540 |
+
cancelButton.textContent = 'キャンセル';
|
1541 |
+
cancelButton.onclick = () => {
|
1542 |
+
modal.remove();
|
1543 |
+
};
|
1544 |
+
|
1545 |
+
buttonContainer.appendChild(saveButton);
|
1546 |
+
buttonContainer.appendChild(cancelButton);
|
1547 |
+
|
1548 |
+
modal.appendChild(textarea);
|
1549 |
+
modal.appendChild(buttonContainer);
|
1550 |
+
|
1551 |
+
document.body.appendChild(modal);
|
1552 |
+
break;
|
1553 |
case 'add-attribute':
|
1554 |
const attrName = prompt('属性名を入力');
|
1555 |
if (attrName) {
|