Update dev-tools.js
Browse files- dev-tools.js +160 -76
dev-tools.js
CHANGED
@@ -9,7 +9,7 @@
|
|
9 |
width: 100%;
|
10 |
height: 300px; /* 適切な高さに調整 */
|
11 |
background-color: #fff;
|
12 |
-
border-top: 1px solid #
|
13 |
box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.1);
|
14 |
z-index: 9999;
|
15 |
display: flex;
|
@@ -78,40 +78,44 @@
|
|
78 |
}
|
79 |
|
80 |
/* Console スタイル */
|
81 |
-
|
82 |
white-space: pre-wrap;
|
83 |
-
|
84 |
-
|
85 |
-
|
|
|
86 |
}
|
87 |
|
88 |
.console-input {
|
89 |
width: 100%;
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
|
|
95 |
}
|
96 |
|
97 |
/* Elements スタイル */
|
98 |
-
|
99 |
display: flex;
|
100 |
-
|
101 |
-
|
102 |
}
|
103 |
|
104 |
.dom-tree {
|
105 |
font-family: monospace;
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
|
|
110 |
}
|
111 |
|
112 |
.dom-node {
|
113 |
margin-left: 15px;
|
114 |
-
|
|
|
115 |
}
|
116 |
|
117 |
.dom-node.selected {
|
@@ -119,104 +123,119 @@
|
|
119 |
}
|
120 |
|
121 |
.dom-tag {
|
122 |
-
color: #
|
|
|
123 |
}
|
124 |
|
125 |
.dom-attr {
|
126 |
-
color: #
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
127 |
}
|
128 |
|
129 |
.css-panel {
|
130 |
flex: 1;
|
131 |
-
|
132 |
-
|
133 |
}
|
134 |
|
135 |
.css-rule {
|
136 |
margin-bottom: 15px;
|
137 |
-
|
138 |
-
|
|
|
139 |
}
|
140 |
|
141 |
.css-selector {
|
142 |
-
color: #
|
143 |
-
|
|
|
144 |
}
|
145 |
|
146 |
.css-property {
|
147 |
display: flex;
|
148 |
-
|
149 |
}
|
150 |
|
151 |
.css-property-name {
|
152 |
-
color: #
|
153 |
-
|
154 |
}
|
155 |
|
156 |
.css-property-value {
|
157 |
-
color: #
|
158 |
-
|
159 |
}
|
160 |
|
161 |
.css-toggle {
|
162 |
margin-left: 10px;
|
163 |
-
|
164 |
-
|
165 |
}
|
166 |
|
167 |
/* Context Menu */
|
168 |
-
|
169 |
position: absolute;
|
170 |
-
|
171 |
-
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
}
|
177 |
|
178 |
.context-menu-item {
|
179 |
padding: 8px 15px;
|
180 |
-
|
|
|
181 |
}
|
182 |
|
183 |
.context-menu-item:hover {
|
184 |
background: #4fc3f7;
|
185 |
-
|
186 |
}
|
187 |
|
188 |
/* Storage スタイル */
|
189 |
-
|
190 |
width: 100%;
|
191 |
-
|
|
|
192 |
}
|
193 |
|
194 |
.storage-table th, .storage-table td {
|
195 |
-
border: 1px solid #
|
196 |
-
|
197 |
-
|
198 |
}
|
199 |
|
200 |
.storage-table th {
|
201 |
-
background: #
|
202 |
}
|
203 |
|
204 |
.storage-actions {
|
205 |
display: flex;
|
206 |
-
|
207 |
}
|
208 |
|
209 |
.storage-btn {
|
210 |
background: #4fc3f7;
|
211 |
-
|
212 |
-
|
213 |
-
|
|
|
214 |
}
|
215 |
|
216 |
.editable {
|
217 |
cursor: pointer;
|
218 |
-
|
219 |
-
|
220 |
}
|
221 |
|
222 |
.editable:hover {
|
@@ -224,25 +243,33 @@
|
|
224 |
}
|
225 |
|
226 |
.add-btn {
|
227 |
-
background: #
|
228 |
-
|
229 |
-
|
230 |
-
|
231 |
-
|
232 |
-
|
|
|
233 |
}
|
234 |
|
235 |
-
|
236 |
-
#
|
237 |
-
background-color: #fff;
|
238 |
}
|
239 |
|
240 |
-
|
241 |
-
|
242 |
}
|
243 |
|
244 |
-
|
245 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
246 |
}
|
247 |
`;
|
248 |
document.head.appendChild(style);
|
@@ -252,7 +279,6 @@
|
|
252 |
let selectedElement = null;
|
253 |
let selectedDOMNode = null;
|
254 |
|
255 |
-
// 開発者ツールのUI構築
|
256 |
// 開発者ツールのUI構築
|
257 |
function createDevTools() {
|
258 |
const container = document.createElement('div');
|
@@ -443,11 +469,11 @@
|
|
443 |
try {
|
444 |
const result = eval(e.target.value);
|
445 |
if (result !== undefined) {
|
446 |
-
logMessage('> ' + e.target.value, '#
|
447 |
-
logMessage('← ' + result, '#
|
448 |
}
|
449 |
} catch (err) {
|
450 |
-
logMessage(err.message, '#
|
451 |
}
|
452 |
e.target.value = '';
|
453 |
}
|
@@ -461,21 +487,61 @@
|
|
461 |
const original = console[method];
|
462 |
console[method] = (...args) => {
|
463 |
original.apply(console, args);
|
464 |
-
const color = method === 'error' ? '#
|
465 |
-
logMessage(args.join(' '), color);
|
466 |
};
|
467 |
});
|
468 |
|
469 |
function logMessage(message, color) {
|
470 |
const line = document.createElement('div');
|
471 |
line.style.color = color;
|
472 |
-
line.
|
473 |
log.appendChild(line);
|
474 |
log.scrollTop = log.scrollHeight;
|
475 |
}
|
476 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
477 |
return panel;
|
478 |
}
|
|
|
|
|
479 |
function buildDOMTree(node, parentElement, depth = 0) {
|
480 |
if (node.nodeType === Node.ELEMENT_NODE) {
|
481 |
const element = document.createElement('div');
|
@@ -520,8 +586,16 @@
|
|
520 |
// 属性
|
521 |
Array.from(node.attributes).forEach(attr => {
|
522 |
const attrSpan = document.createElement('span');
|
523 |
-
attrSpan.className = 'dom-attr';
|
524 |
attrSpan.textContent = ` ${attr.name}="${attr.value}"`;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
525 |
element.appendChild(attrSpan);
|
526 |
});
|
527 |
|
@@ -546,11 +620,20 @@
|
|
546 |
} else if (node.nodeType === Node.TEXT_NODE && node.textContent.trim()) {
|
547 |
const text = document.createElement('div');
|
548 |
text.style.marginLeft = `${depth * 15}px`;
|
549 |
-
text.
|
550 |
text.textContent = `"${node.textContent.trim()}"`;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
551 |
parentElement.appendChild(text);
|
552 |
}
|
553 |
}
|
|
|
554 |
// Elementsパネルの作成
|
555 |
function createElementsPanel() {
|
556 |
const panel = document.createElement('div');
|
@@ -724,6 +807,7 @@
|
|
724 |
}
|
725 |
}
|
726 |
}
|
|
|
727 |
// Storageパネルの作成
|
728 |
function createStoragePanel() {
|
729 |
const panel = document.createElement('div');
|
|
|
9 |
width: 100%;
|
10 |
height: 300px; /* 適切な高さに調整 */
|
11 |
background-color: #fff;
|
12 |
+
border-top: 1px solid #ddd;
|
13 |
box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.1);
|
14 |
z-index: 9999;
|
15 |
display: flex;
|
|
|
78 |
}
|
79 |
|
80 |
/* Console スタイル */
|
81 |
+
#console-log {
|
82 |
white-space: pre-wrap;
|
83 |
+
margin: 0;
|
84 |
+
line-height: 1.4;
|
85 |
+
flex: 1;
|
86 |
+
color: #333;
|
87 |
}
|
88 |
|
89 |
.console-input {
|
90 |
width: 100%;
|
91 |
+
background: #f5f5f5;
|
92 |
+
border: 1px solid #ddd;
|
93 |
+
color: #333;
|
94 |
+
padding: 8px;
|
95 |
+
margin-top: 10px;
|
96 |
+
font-family: monospace;
|
97 |
}
|
98 |
|
99 |
/* Elements スタイル */
|
100 |
+
.elements-container {
|
101 |
display: flex;
|
102 |
+
flex: 1;
|
103 |
+
overflow: hidden;
|
104 |
}
|
105 |
|
106 |
.dom-tree {
|
107 |
font-family: monospace;
|
108 |
+
flex: 1;
|
109 |
+
overflow: auto;
|
110 |
+
border-right: 1px solid #ddd;
|
111 |
+
padding-right: 10px;
|
112 |
+
color: #333;
|
113 |
}
|
114 |
|
115 |
.dom-node {
|
116 |
margin-left: 15px;
|
117 |
+
position: relative;
|
118 |
+
line-height: 1.4;
|
119 |
}
|
120 |
|
121 |
.dom-node.selected {
|
|
|
123 |
}
|
124 |
|
125 |
.dom-tag {
|
126 |
+
color: #0066cc;
|
127 |
+
font-weight: bold;
|
128 |
}
|
129 |
|
130 |
.dom-attr {
|
131 |
+
color: #d33682;
|
132 |
+
}
|
133 |
+
|
134 |
+
.dom-attr.editable:hover {
|
135 |
+
text-decoration: underline;
|
136 |
+
cursor: pointer;
|
137 |
+
}
|
138 |
+
|
139 |
+
.dom-text {
|
140 |
+
color: #333;
|
141 |
}
|
142 |
|
143 |
.css-panel {
|
144 |
flex: 1;
|
145 |
+
overflow: auto;
|
146 |
+
padding-left: 10px;
|
147 |
}
|
148 |
|
149 |
.css-rule {
|
150 |
margin-bottom: 15px;
|
151 |
+
border: 1px solid #ddd;
|
152 |
+
padding: 8px;
|
153 |
+
background-color: #f9f9f9;
|
154 |
}
|
155 |
|
156 |
.css-selector {
|
157 |
+
color: #0066cc;
|
158 |
+
margin-bottom: 5px;
|
159 |
+
font-weight: bold;
|
160 |
}
|
161 |
|
162 |
.css-property {
|
163 |
display: flex;
|
164 |
+
margin-bottom: 3px;
|
165 |
}
|
166 |
|
167 |
.css-property-name {
|
168 |
+
color: #d33682;
|
169 |
+
min-width: 120px;
|
170 |
}
|
171 |
|
172 |
.css-property-value {
|
173 |
+
color: #333;
|
174 |
+
flex: 1;
|
175 |
}
|
176 |
|
177 |
.css-toggle {
|
178 |
margin-left: 10px;
|
179 |
+
color: #dc322f;
|
180 |
+
cursor: pointer;
|
181 |
}
|
182 |
|
183 |
/* Context Menu */
|
184 |
+
.context-menu {
|
185 |
position: absolute;
|
186 |
+
background: #fff;
|
187 |
+
border: 1px solid #ddd;
|
188 |
+
z-index: 10000;
|
189 |
+
min-width: 200px;
|
190 |
+
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
191 |
+
display: none;
|
192 |
}
|
193 |
|
194 |
.context-menu-item {
|
195 |
padding: 8px 15px;
|
196 |
+
cursor: pointer;
|
197 |
+
color: #333;
|
198 |
}
|
199 |
|
200 |
.context-menu-item:hover {
|
201 |
background: #4fc3f7;
|
202 |
+
color: #000;
|
203 |
}
|
204 |
|
205 |
/* Storage スタイル */
|
206 |
+
.storage-table {
|
207 |
width: 100%;
|
208 |
+
border-collapse: collapse;
|
209 |
+
margin-bottom: 10px;
|
210 |
}
|
211 |
|
212 |
.storage-table th, .storage-table td {
|
213 |
+
border: 1px solid #ddd;
|
214 |
+
padding: 5px;
|
215 |
+
text-align: left;
|
216 |
}
|
217 |
|
218 |
.storage-table th {
|
219 |
+
background: #f5f5f5;
|
220 |
}
|
221 |
|
222 |
.storage-actions {
|
223 |
display: flex;
|
224 |
+
gap: 5px;
|
225 |
}
|
226 |
|
227 |
.storage-btn {
|
228 |
background: #4fc3f7;
|
229 |
+
border: none;
|
230 |
+
padding: 2px 5px;
|
231 |
+
cursor: pointer;
|
232 |
+
border-radius: 3px;
|
233 |
}
|
234 |
|
235 |
.editable {
|
236 |
cursor: pointer;
|
237 |
+
padding: 2px 5px;
|
238 |
+
border: 1px dashed transparent;
|
239 |
}
|
240 |
|
241 |
.editable:hover {
|
|
|
243 |
}
|
244 |
|
245 |
.add-btn {
|
246 |
+
background: #4fc3f7;
|
247 |
+
color: #000;
|
248 |
+
border: none;
|
249 |
+
padding: 5px 10px;
|
250 |
+
margin-top: 10px;
|
251 |
+
cursor: pointer;
|
252 |
+
border-radius: 3px;
|
253 |
}
|
254 |
|
255 |
+
.json-object {
|
256 |
+
color: #268bd2;
|
|
|
257 |
}
|
258 |
|
259 |
+
.json-string {
|
260 |
+
color: #2aa198;
|
261 |
}
|
262 |
|
263 |
+
.json-number {
|
264 |
+
color: #d33682;
|
265 |
+
}
|
266 |
+
|
267 |
+
.json-boolean {
|
268 |
+
color: #b58900;
|
269 |
+
}
|
270 |
+
|
271 |
+
.json-null {
|
272 |
+
color: #6c71c4;
|
273 |
}
|
274 |
`;
|
275 |
document.head.appendChild(style);
|
|
|
279 |
let selectedElement = null;
|
280 |
let selectedDOMNode = null;
|
281 |
|
|
|
282 |
// 開発者ツールのUI構築
|
283 |
function createDevTools() {
|
284 |
const container = document.createElement('div');
|
|
|
469 |
try {
|
470 |
const result = eval(e.target.value);
|
471 |
if (result !== undefined) {
|
472 |
+
logMessage('> ' + e.target.value, '#0066cc');
|
473 |
+
logMessage('← ' + formatOutput(result), '#2aa198');
|
474 |
}
|
475 |
} catch (err) {
|
476 |
+
logMessage(err.message, '#dc322f');
|
477 |
}
|
478 |
e.target.value = '';
|
479 |
}
|
|
|
487 |
const original = console[method];
|
488 |
console[method] = (...args) => {
|
489 |
original.apply(console, args);
|
490 |
+
const color = method === 'error' ? '#dc322f' : method === 'warn' ? '#b58900' : '#586e75';
|
491 |
+
logMessage(args.map(arg => formatOutput(arg)).join(' '), color);
|
492 |
};
|
493 |
});
|
494 |
|
495 |
function logMessage(message, color) {
|
496 |
const line = document.createElement('div');
|
497 |
line.style.color = color;
|
498 |
+
line.innerHTML = message;
|
499 |
log.appendChild(line);
|
500 |
log.scrollTop = log.scrollHeight;
|
501 |
}
|
502 |
|
503 |
+
// 出力をフォーマットする関数
|
504 |
+
function formatOutput(output) {
|
505 |
+
if (output === null) {
|
506 |
+
return '<span class="json-null">null</span>';
|
507 |
+
}
|
508 |
+
if (output === undefined) {
|
509 |
+
return '<span class="json-null">undefined</span>';
|
510 |
+
}
|
511 |
+
if (typeof output === 'boolean') {
|
512 |
+
return `<span class="json-boolean">${output}</span>`;
|
513 |
+
}
|
514 |
+
if (typeof output === 'number') {
|
515 |
+
return `<span class="json-number">${output}</span>`;
|
516 |
+
}
|
517 |
+
if (typeof output === 'string') {
|
518 |
+
return `<span class="json-string">"${output}"</span>`;
|
519 |
+
}
|
520 |
+
if (typeof output === 'function') {
|
521 |
+
return `<span class="json-object">function ${output.name}() { ... }</span>`;
|
522 |
+
}
|
523 |
+
if (Array.isArray(output)) {
|
524 |
+
return `<span class="json-object">[${output.map(formatOutput).join(', ')}]</span>`;
|
525 |
+
}
|
526 |
+
if (typeof output === 'object') {
|
527 |
+
try {
|
528 |
+
return `<span class="json-object">${JSON.stringify(output, null, 2)
|
529 |
+
.replace(/"([^"]+)":/g, '<span class="json-string">"$1"</span>:')
|
530 |
+
.replace(/"([^"]+)"/g, '<span class="json-string">"$1"</span>')
|
531 |
+
.replace(/\b(true|false)\b/g, '<span class="json-boolean">$1</span>')
|
532 |
+
.replace(/\b(null)\b/g, '<span class="json-null">$1</span>')
|
533 |
+
.replace(/\b(\d+)\b/g, '<span class="json-number">$1</span>')}</span>`;
|
534 |
+
} catch (e) {
|
535 |
+
return `<span class="json-object">${output.toString()}</span>`;
|
536 |
+
}
|
537 |
+
}
|
538 |
+
return output;
|
539 |
+
}
|
540 |
+
|
541 |
return panel;
|
542 |
}
|
543 |
+
|
544 |
+
// DOMツリーを構築する関数
|
545 |
function buildDOMTree(node, parentElement, depth = 0) {
|
546 |
if (node.nodeType === Node.ELEMENT_NODE) {
|
547 |
const element = document.createElement('div');
|
|
|
586 |
// 属性
|
587 |
Array.from(node.attributes).forEach(attr => {
|
588 |
const attrSpan = document.createElement('span');
|
589 |
+
attrSpan.className = 'dom-attr editable';
|
590 |
attrSpan.textContent = ` ${attr.name}="${attr.value}"`;
|
591 |
+
attrSpan.onclick = (e) => {
|
592 |
+
e.stopPropagation();
|
593 |
+
const newValue = prompt(`属性 ${attr.name} の値を編集`, attr.value);
|
594 |
+
if (newValue !== null) {
|
595 |
+
node.setAttribute(attr.name, newValue);
|
596 |
+
refreshElementsPanel();
|
597 |
+
}
|
598 |
+
};
|
599 |
element.appendChild(attrSpan);
|
600 |
});
|
601 |
|
|
|
620 |
} else if (node.nodeType === Node.TEXT_NODE && node.textContent.trim()) {
|
621 |
const text = document.createElement('div');
|
622 |
text.style.marginLeft = `${depth * 15}px`;
|
623 |
+
text.className = 'dom-text';
|
624 |
text.textContent = `"${node.textContent.trim()}"`;
|
625 |
+
text.onclick = (e) => {
|
626 |
+
e.stopPropagation();
|
627 |
+
const newText = prompt('テキストを編集', node.textContent.trim());
|
628 |
+
if (newText !== null) {
|
629 |
+
node.textContent = newText;
|
630 |
+
refreshElementsPanel();
|
631 |
+
}
|
632 |
+
};
|
633 |
parentElement.appendChild(text);
|
634 |
}
|
635 |
}
|
636 |
+
|
637 |
// Elementsパネルの作成
|
638 |
function createElementsPanel() {
|
639 |
const panel = document.createElement('div');
|
|
|
807 |
}
|
808 |
}
|
809 |
}
|
810 |
+
|
811 |
// Storageパネルの作成
|
812 |
function createStoragePanel() {
|
813 |
const panel = document.createElement('div');
|