soiz1 commited on
Commit
fb3e427
·
verified ·
1 Parent(s): 1c6ec0a

Update dev-tools.js

Browse files
Files changed (1) hide show
  1. dev-tools.js +475 -25
dev-tools.js CHANGED
@@ -66,7 +66,7 @@
66
  }
67
 
68
  .devtools-panel.active {
69
- display: block;
70
  }
71
 
72
  /* Console スタイル */
@@ -74,6 +74,7 @@
74
  white-space: pre-wrap;
75
  margin: 0;
76
  line-height: 1.4;
 
77
  }
78
 
79
  .console-input {
@@ -86,12 +87,27 @@
86
  }
87
 
88
  /* Elements スタイル */
 
 
 
 
 
 
89
  .dom-tree {
90
  font-family: monospace;
 
 
 
 
91
  }
92
 
93
  .dom-node {
94
  margin-left: 15px;
 
 
 
 
 
95
  }
96
 
97
  .dom-tag {
@@ -102,6 +118,65 @@
102
  color: #ff7043;
103
  }
104
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
105
  /* Storage スタイル */
106
  .storage-table {
107
  width: 100%;
@@ -129,9 +204,33 @@
129
  padding: 2px 5px;
130
  cursor: pointer;
131
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
132
  `;
133
  document.head.appendChild(style);
134
 
 
 
 
 
 
135
  // 開発者ツールのUI構築
136
  function createDevTools() {
137
  const container = document.createElement('div');
@@ -179,6 +278,9 @@
179
 
180
  document.body.appendChild(container);
181
 
 
 
 
182
  // タブ切り替え機能
183
  function createTab(name, panelId) {
184
  const tab = document.createElement('div');
@@ -194,7 +296,112 @@
194
  }
195
 
196
  // 初期タブをアクティブに
197
- consoleTab.click();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
198
  }
199
 
200
  // Consoleパネルの作成
@@ -254,11 +461,20 @@
254
  panel.className = 'devtools-panel';
255
  panel.id = 'elements-panel';
256
 
 
 
 
257
  const tree = document.createElement('div');
258
  tree.className = 'dom-tree';
259
  tree.id = 'dom-tree';
260
 
261
- panel.appendChild(tree);
 
 
 
 
 
 
262
 
263
  // DOMツリーを構築
264
  function buildDOMTree(node, parentElement, depth = 0) {
@@ -266,6 +482,38 @@
266
  const element = document.createElement('div');
267
  element.className = 'dom-node';
268
  element.style.marginLeft = `${depth * 15}px`;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
269
 
270
  // タグ名
271
  const tag = document.createElement('span');
@@ -308,12 +556,142 @@
308
  }
309
  }
310
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
311
  // 初期DOMツリー構築
312
- buildDOMTree(document.documentElement, tree);
313
 
314
  return panel;
315
  }
316
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
317
  // Storageパネルの作成
318
  function createStoragePanel() {
319
  const panel = document.createElement('div');
@@ -329,6 +707,19 @@
329
  localStorageTable.className = 'storage-table';
330
  panel.appendChild(localStorageTable);
331
 
 
 
 
 
 
 
 
 
 
 
 
 
 
332
  // SessionStorage表示
333
  const sessionStorageTitle = document.createElement('h3');
334
  sessionStorageTitle.style.marginTop = '20px';
@@ -339,6 +730,19 @@
339
  sessionStorageTable.className = 'storage-table';
340
  panel.appendChild(sessionStorageTable);
341
 
 
 
 
 
 
 
 
 
 
 
 
 
 
342
  // Cookie表示
343
  const cookiesTitle = document.createElement('h3');
344
  cookiesTitle.style.marginTop = '20px';
@@ -349,14 +753,27 @@
349
  cookiesTable.className = 'storage-table';
350
  panel.appendChild(cookiesTable);
351
 
 
 
 
 
 
 
 
 
 
 
 
 
 
352
  // ストレージを表示する関数
353
  function renderStorage() {
354
- renderTable(localStorageTable, localStorage);
355
- renderTable(sessionStorageTable, sessionStorage);
356
  renderCookiesTable(cookiesTable);
357
  }
358
 
359
- function renderTable(tableElement, storage) {
360
  tableElement.innerHTML = `
361
  <thead>
362
  <tr>
@@ -377,24 +794,34 @@
377
  const row = document.createElement('tr');
378
 
379
  const keyCell = document.createElement('td');
380
- keyCell.textContent = key;
 
 
 
 
 
 
 
 
 
 
 
381
 
382
  const valueCell = document.createElement('td');
383
- valueCell.textContent = value;
384
-
385
- const actionsCell = document.createElement('td');
386
- actionsCell.className = 'storage-actions';
387
-
388
- const editBtn = document.createElement('button');
389
- editBtn.className = 'storage-btn';
390
- editBtn.textContent = 'Edit';
391
- editBtn.onclick = () => {
392
- const newValue = prompt('Enter new value:', value);
393
  if (newValue !== null) {
394
  storage.setItem(key, newValue);
395
  renderStorage();
396
  }
397
  };
 
 
 
 
398
 
399
  const deleteBtn = document.createElement('button');
400
  deleteBtn.className = 'storage-btn';
@@ -404,7 +831,6 @@
404
  renderStorage();
405
  };
406
 
407
- actionsCell.appendChild(editBtn);
408
  actionsCell.appendChild(deleteBtn);
409
 
410
  row.appendChild(keyCell);
@@ -433,15 +859,40 @@
433
  if (!cookie.trim()) return;
434
 
435
  const [name, ...valueParts] = cookie.split('=');
 
436
  const value = valueParts.join('=').trim();
437
 
438
  const row = document.createElement('tr');
439
 
440
  const nameCell = document.createElement('td');
441
- nameCell.textContent = name.trim();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
442
 
443
  const valueCell = document.createElement('td');
444
- valueCell.textContent = decodeURIComponent(value);
 
 
 
 
 
 
 
 
 
 
445
 
446
  const actionsCell = document.createElement('td');
447
  actionsCell.className = 'storage-actions';
@@ -499,13 +950,12 @@
499
 
500
  document.body.appendChild(button);
501
  }
 
 
502
  document.addEventListener('DOMContentLoaded', function() {
503
- // 初期化
504
  createDevTools();
505
  createOpenButton();
506
-
507
- // 初期メッセージ
508
  console.log('開発者ツールが初期化されました');
509
  console.log('このコンソールでJavaScriptを実行できます');
510
- })
511
  })();
 
66
  }
67
 
68
  .devtools-panel.active {
69
+ display: flex;
70
  }
71
 
72
  /* Console スタイル */
 
74
  white-space: pre-wrap;
75
  margin: 0;
76
  line-height: 1.4;
77
+ flex: 1;
78
  }
79
 
80
  .console-input {
 
87
  }
88
 
89
  /* Elements スタイル */
90
+ .elements-container {
91
+ display: flex;
92
+ flex: 1;
93
+ overflow: hidden;
94
+ }
95
+
96
  .dom-tree {
97
  font-family: monospace;
98
+ flex: 1;
99
+ overflow: auto;
100
+ border-right: 1px solid #4fc3f7;
101
+ padding-right: 10px;
102
  }
103
 
104
  .dom-node {
105
  margin-left: 15px;
106
+ position: relative;
107
+ }
108
+
109
+ .dom-node.selected {
110
+ background: rgba(79, 195, 247, 0.2);
111
  }
112
 
113
  .dom-tag {
 
118
  color: #ff7043;
119
  }
120
 
121
+ .css-panel {
122
+ flex: 1;
123
+ overflow: auto;
124
+ padding-left: 10px;
125
+ }
126
+
127
+ .css-rule {
128
+ margin-bottom: 15px;
129
+ border: 1px solid #4fc3f7;
130
+ padding: 8px;
131
+ }
132
+
133
+ .css-selector {
134
+ color: #4fc3f7;
135
+ margin-bottom: 5px;
136
+ }
137
+
138
+ .css-property {
139
+ display: flex;
140
+ margin-bottom: 3px;
141
+ }
142
+
143
+ .css-property-name {
144
+ color: #69f0ae;
145
+ min-width: 120px;
146
+ }
147
+
148
+ .css-property-value {
149
+ color: #e0e0e0;
150
+ flex: 1;
151
+ }
152
+
153
+ .css-toggle {
154
+ margin-left: 10px;
155
+ color: #ff7043;
156
+ cursor: pointer;
157
+ }
158
+
159
+ /* Context Menu */
160
+ .context-menu {
161
+ position: absolute;
162
+ background: #252a33;
163
+ border: 1px solid #4fc3f7;
164
+ z-index: 10000;
165
+ min-width: 200px;
166
+ box-shadow: 0 2px 10px rgba(0, 0, 0, 0.5);
167
+ display: none;
168
+ }
169
+
170
+ .context-menu-item {
171
+ padding: 8px 15px;
172
+ cursor: pointer;
173
+ }
174
+
175
+ .context-menu-item:hover {
176
+ background: #4fc3f7;
177
+ color: #000;
178
+ }
179
+
180
  /* Storage スタイル */
181
  .storage-table {
182
  width: 100%;
 
204
  padding: 2px 5px;
205
  cursor: pointer;
206
  }
207
+
208
+ .editable {
209
+ cursor: pointer;
210
+ padding: 2px 5px;
211
+ border: 1px dashed transparent;
212
+ }
213
+
214
+ .editable:hover {
215
+ border-color: #4fc3f7;
216
+ }
217
+
218
+ .add-btn {
219
+ background: #69f0ae;
220
+ color: #000;
221
+ border: none;
222
+ padding: 5px 10px;
223
+ margin-top: 10px;
224
+ cursor: pointer;
225
+ }
226
  `;
227
  document.head.appendChild(style);
228
 
229
+ // コンテキストメニュー用の変数
230
+ let contextMenu = null;
231
+ let selectedElement = null;
232
+ let selectedDOMNode = null;
233
+
234
  // 開発者ツールのUI構築
235
  function createDevTools() {
236
  const container = document.createElement('div');
 
278
 
279
  document.body.appendChild(container);
280
 
281
+ // コンテキストメニュー作成
282
+ createContextMenu();
283
+
284
  // タブ切り替え機能
285
  function createTab(name, panelId) {
286
  const tab = document.createElement('div');
 
296
  }
297
 
298
  // 初期タブをアクティブに
299
+ elementsTab.click();
300
+ }
301
+
302
+ // コンテキストメニュー作成
303
+ function createContextMenu() {
304
+ contextMenu = document.createElement('div');
305
+ contextMenu.className = 'context-menu';
306
+ contextMenu.innerHTML = `
307
+ <div class="context-menu-item" data-action="edit-html">HTMLとして編集</div>
308
+ <div class="context-menu-item" data-action="add-attribute">属性を追加</div>
309
+ <div class="context-menu-item" data-action="edit-element">要素を編集</div>
310
+ <div class="context-menu-item" data-action="duplicate">要素を複製</div>
311
+ <div class="context-menu-item" data-action="remove">要素を削除</div>
312
+ <div class="context-menu-item" data-action="toggle-visibility">要素を非表示</div>
313
+ <div class="context-menu-item" data-action="force-state">状態を強制</div>
314
+ `;
315
+ document.body.appendChild(contextMenu);
316
+
317
+ // メニューアイテムのクリック処理
318
+ contextMenu.querySelectorAll('.context-menu-item').forEach(item => {
319
+ item.addEventListener('click', (e) => {
320
+ const action = e.target.getAttribute('data-action');
321
+ handleContextMenuAction(action);
322
+ contextMenu.style.display = 'none';
323
+ });
324
+ });
325
+
326
+ // メニュー外をクリックで閉じる
327
+ document.addEventListener('click', (e) => {
328
+ if (e.target !== contextMenu && !contextMenu.contains(e.target)) {
329
+ contextMenu.style.display = 'none';
330
+ }
331
+ });
332
+ }
333
+
334
+ // コンテキストメニューのアクション処理
335
+ function handleContextMenuAction(action) {
336
+ if (!selectedElement) return;
337
+
338
+ switch (action) {
339
+ case 'edit-html':
340
+ const html = prompt('HTMLを編集', selectedElement.outerHTML);
341
+ if (html) {
342
+ selectedElement.outerHTML = html;
343
+ refreshElementsPanel();
344
+ }
345
+ break;
346
+
347
+ case 'add-attribute':
348
+ const attrName = prompt('属性名を入力');
349
+ if (attrName) {
350
+ const attrValue = prompt('属性値を入力');
351
+ selectedElement.setAttribute(attrName, attrValue || '');
352
+ refreshElementsPanel();
353
+ }
354
+ break;
355
+
356
+ case 'edit-element':
357
+ const tagName = prompt('タグ名を編集', selectedElement.tagName.toLowerCase());
358
+ if (tagName) {
359
+ const newElement = document.createElement(tagName);
360
+ Array.from(selectedElement.attributes).forEach(attr => {
361
+ newElement.setAttribute(attr.name, attr.value);
362
+ });
363
+ newElement.innerHTML = selectedElement.innerHTML;
364
+ selectedElement.parentNode.replaceChild(newElement, selectedElement);
365
+ selectedElement = newElement;
366
+ refreshElementsPanel();
367
+ }
368
+ break;
369
+
370
+ case 'duplicate':
371
+ const clone = selectedElement.cloneNode(true);
372
+ selectedElement.parentNode.insertBefore(clone, selectedElement.nextSibling);
373
+ refreshElementsPanel();
374
+ break;
375
+
376
+ case 'remove':
377
+ if (confirm('要素を削除しますか?')) {
378
+ selectedElement.parentNode.removeChild(selectedElement);
379
+ refreshElementsPanel();
380
+ }
381
+ break;
382
+
383
+ case 'toggle-visibility':
384
+ if (selectedElement.style.display === 'none') {
385
+ selectedElement.style.display = '';
386
+ } else {
387
+ selectedElement.style.display = 'none';
388
+ }
389
+ refreshElementsPanel();
390
+ break;
391
+
392
+ case 'force-state':
393
+ const state = prompt('強制する状態を入力 (例: hover, active, focus)', 'hover');
394
+ if (state) {
395
+ // 既存の状態クラスを削除
396
+ selectedElement.classList.remove('force-hover', 'force-active', 'force-focus',
397
+ 'force-focus-within', 'force-focus-visible', 'force-target');
398
+
399
+ // 新しい状態クラスを追加
400
+ selectedElement.classList.add(`force-${state}`);
401
+ refreshElementsPanel();
402
+ }
403
+ break;
404
+ }
405
  }
406
 
407
  // Consoleパネルの作成
 
461
  panel.className = 'devtools-panel';
462
  panel.id = 'elements-panel';
463
 
464
+ const container = document.createElement('div');
465
+ container.className = 'elements-container';
466
+
467
  const tree = document.createElement('div');
468
  tree.className = 'dom-tree';
469
  tree.id = 'dom-tree';
470
 
471
+ const cssPanel = document.createElement('div');
472
+ cssPanel.className = 'css-panel';
473
+ cssPanel.id = 'css-panel';
474
+
475
+ container.appendChild(tree);
476
+ container.appendChild(cssPanel);
477
+ panel.appendChild(container);
478
 
479
  // DOMツリーを構築
480
  function buildDOMTree(node, parentElement, depth = 0) {
 
482
  const element = document.createElement('div');
483
  element.className = 'dom-node';
484
  element.style.marginLeft = `${depth * 15}px`;
485
+ element.dataset.elementId = node.id || Math.random().toString(36).substr(2, 9);
486
+
487
+ // 右クリックイベント
488
+ element.oncontextmenu = (e) => {
489
+ e.preventDefault();
490
+ selectedElement = node;
491
+ selectedDOMNode = element;
492
+
493
+ // 選択状態を更新
494
+ document.querySelectorAll('.dom-node').forEach(el => el.classList.remove('selected'));
495
+ element.classList.add('selected');
496
+
497
+ // コンテキストメニュー表示
498
+ contextMenu.style.display = 'block';
499
+ contextMenu.style.left = `${e.pageX}px`;
500
+ contextMenu.style.top = `${e.pageY}px`;
501
+
502
+ // CSSパネルを更新
503
+ updateCSSPanel(node);
504
+ };
505
+
506
+ // クリックで選択
507
+ element.onclick = (e) => {
508
+ e.stopPropagation();
509
+ selectedElement = node;
510
+ selectedDOMNode = element;
511
+
512
+ document.querySelectorAll('.dom-node').forEach(el => el.classList.remove('selected'));
513
+ element.classList.add('selected');
514
+
515
+ updateCSSPanel(node);
516
+ };
517
 
518
  // タグ名
519
  const tag = document.createElement('span');
 
556
  }
557
  }
558
 
559
+ // CSSパネルを更新
560
+ function updateCSSPanel(element) {
561
+ const cssPanel = document.getElementById('css-panel');
562
+ cssPanel.innerHTML = '';
563
+
564
+ if (!element) return;
565
+
566
+ // インラインスタイル
567
+ if (element.style.length > 0) {
568
+ const inlineRule = document.createElement('div');
569
+ inlineRule.className = 'css-rule';
570
+
571
+ const selector = document.createElement('div');
572
+ selector.className = 'css-selector';
573
+ selector.textContent = 'インラインスタイル';
574
+ inlineRule.appendChild(selector);
575
+
576
+ for (let i = 0; i < element.style.length; i++) {
577
+ const propName = element.style[i];
578
+ const propValue = element.style[propName];
579
+
580
+ const propDiv = document.createElement('div');
581
+ propDiv.className = 'css-property';
582
+
583
+ const nameSpan = document.createElement('span');
584
+ nameSpan.className = 'css-property-name editable';
585
+ nameSpan.textContent = propName;
586
+ nameSpan.onclick = () => editCSSProperty(element, propName, 'style');
587
+
588
+ const valueSpan = document.createElement('span');
589
+ valueSpan.className = 'css-property-value editable';
590
+ valueSpan.textContent = propValue;
591
+ valueSpan.onclick = () => editCSSProperty(element, propName, 'style');
592
+
593
+ const toggleSpan = document.createElement('span');
594
+ toggleSpan.className = 'css-toggle';
595
+ toggleSpan.textContent = '×';
596
+ toggleSpan.title = 'プロパティを無効化';
597
+ toggleSpan.onclick = () => {
598
+ element.style[propName] = '';
599
+ updateCSSPanel(element);
600
+ };
601
+
602
+ propDiv.appendChild(nameSpan);
603
+ propDiv.appendChild(valueSpan);
604
+ propDiv.appendChild(toggleSpan);
605
+ inlineRule.appendChild(propDiv);
606
+ }
607
+
608
+ cssPanel.appendChild(inlineRule);
609
+ }
610
+
611
+ // 計算されたスタイル
612
+ const computedStyles = window.getComputedStyle(element);
613
+ const computedRule = document.createElement('div');
614
+ computedRule.className = 'css-rule';
615
+
616
+ const computedSelector = document.createElement('div');
617
+ computedSelector.className = 'css-selector';
618
+ computedSelector.textContent = '計算されたスタイル';
619
+ computedRule.appendChild(computedSelector);
620
+
621
+ // 重要なプロパティのみ表示
622
+ const importantProps = [
623
+ 'display', 'position', 'width', 'height', 'margin', 'padding',
624
+ 'color', 'background', 'border', 'font', 'flex', 'grid'
625
+ ];
626
+
627
+ importantProps.forEach(prop => {
628
+ const value = computedStyles[prop];
629
+
630
+ const propDiv = document.createElement('div');
631
+ propDiv.className = 'css-property';
632
+
633
+ const nameSpan = document.createElement('span');
634
+ nameSpan.className = 'css-property-name';
635
+ nameSpan.textContent = prop;
636
+
637
+ const valueSpan = document.createElement('span');
638
+ valueSpan.className = 'css-property-value';
639
+ valueSpan.textContent = value;
640
+
641
+ propDiv.appendChild(nameSpan);
642
+ propDiv.appendChild(valueSpan);
643
+ computedRule.appendChild(propDiv);
644
+ });
645
+
646
+ cssPanel.appendChild(computedRule);
647
+ }
648
+
649
+ // CSSプロパティを編集
650
+ function editCSSProperty(element, propName, styleType) {
651
+ let currentValue = '';
652
+
653
+ if (styleType === 'style') {
654
+ currentValue = element.style[propName];
655
+ }
656
+
657
+ const newValue = prompt(`${propName} の新しい値を入力`, currentValue);
658
+
659
+ if (newValue !== null) {
660
+ if (styleType === 'style') {
661
+ element.style[propName] = newValue;
662
+ }
663
+
664
+ updateCSSPanel(element);
665
+ refreshElementsPanel();
666
+ }
667
+ }
668
+
669
  // 初期DOMツリー構築
670
+ refreshElementsPanel();
671
 
672
  return panel;
673
  }
674
 
675
+ // Elementsパネルをリフレッシュ
676
+ function refreshElementsPanel() {
677
+ const tree = document.getElementById('dom-tree');
678
+ tree.innerHTML = '';
679
+ buildDOMTree(document.documentElement, tree);
680
+
681
+ if (selectedElement) {
682
+ const elementId = selectedElement.id || Array.from(selectedElement.attributes)
683
+ .find(attr => attr.name.startsWith('data-element-id'))?.value;
684
+
685
+ if (elementId) {
686
+ const node = document.querySelector(`[data-element-id="${elementId}"]`);
687
+ if (node) {
688
+ node.classList.add('selected');
689
+ updateCSSPanel(selectedElement);
690
+ }
691
+ }
692
+ }
693
+ }
694
+
695
  // Storageパネルの作成
696
  function createStoragePanel() {
697
  const panel = document.createElement('div');
 
707
  localStorageTable.className = 'storage-table';
708
  panel.appendChild(localStorageTable);
709
 
710
+ const addLocalStorageBtn = document.createElement('button');
711
+ addLocalStorageBtn.className = 'add-btn';
712
+ addLocalStorageBtn.textContent = '+ Local Storageに追加';
713
+ addLocalStorageBtn.onclick = () => {
714
+ const key = prompt('キー名を入力');
715
+ if (key) {
716
+ const value = prompt('値を入力');
717
+ localStorage.setItem(key, value);
718
+ renderStorage();
719
+ }
720
+ };
721
+ panel.appendChild(addLocalStorageBtn);
722
+
723
  // SessionStorage表示
724
  const sessionStorageTitle = document.createElement('h3');
725
  sessionStorageTitle.style.marginTop = '20px';
 
730
  sessionStorageTable.className = 'storage-table';
731
  panel.appendChild(sessionStorageTable);
732
 
733
+ const addSessionStorageBtn = document.createElement('button');
734
+ addSessionStorageBtn.className = 'add-btn';
735
+ addSessionStorageBtn.textContent = '+ Session Storageに追加';
736
+ addSessionStorageBtn.onclick = () => {
737
+ const key = prompt('キー名を入力');
738
+ if (key) {
739
+ const value = prompt('値を入力');
740
+ sessionStorage.setItem(key, value);
741
+ renderStorage();
742
+ }
743
+ };
744
+ panel.appendChild(addSessionStorageBtn);
745
+
746
  // Cookie表示
747
  const cookiesTitle = document.createElement('h3');
748
  cookiesTitle.style.marginTop = '20px';
 
753
  cookiesTable.className = 'storage-table';
754
  panel.appendChild(cookiesTable);
755
 
756
+ const addCookieBtn = document.createElement('button');
757
+ addCookieBtn.className = 'add-btn';
758
+ addCookieBtn.textContent = '+ Cookieに追加';
759
+ addCookieBtn.onclick = () => {
760
+ const name = prompt('Cookie名を入力');
761
+ if (name) {
762
+ const value = prompt('値を入力');
763
+ document.cookie = `${encodeURIComponent(name)}=${encodeURIComponent(value)}; path=/`;
764
+ renderStorage();
765
+ }
766
+ };
767
+ panel.appendChild(addCookieBtn);
768
+
769
  // ストレージを表示する関数
770
  function renderStorage() {
771
+ renderTable(localStorageTable, localStorage, 'local');
772
+ renderTable(sessionStorageTable, sessionStorage, 'session');
773
  renderCookiesTable(cookiesTable);
774
  }
775
 
776
+ function renderTable(tableElement, storage, type) {
777
  tableElement.innerHTML = `
778
  <thead>
779
  <tr>
 
794
  const row = document.createElement('tr');
795
 
796
  const keyCell = document.createElement('td');
797
+ const keySpan = document.createElement('span');
798
+ keySpan.className = 'editable';
799
+ keySpan.textContent = key;
800
+ keySpan.onclick = () => {
801
+ const newKey = prompt('新しいキー名を入力', key);
802
+ if (newKey && newKey !== key) {
803
+ storage.setItem(newKey, value);
804
+ storage.removeItem(key);
805
+ renderStorage();
806
+ }
807
+ };
808
+ keyCell.appendChild(keySpan);
809
 
810
  const valueCell = document.createElement('td');
811
+ const valueSpan = document.createElement('span');
812
+ valueSpan.className = 'editable';
813
+ valueSpan.textContent = value;
814
+ valueSpan.onclick = () => {
815
+ const newValue = prompt('新しい値を入力', value);
 
 
 
 
 
816
  if (newValue !== null) {
817
  storage.setItem(key, newValue);
818
  renderStorage();
819
  }
820
  };
821
+ valueCell.appendChild(valueSpan);
822
+
823
+ const actionsCell = document.createElement('td');
824
+ actionsCell.className = 'storage-actions';
825
 
826
  const deleteBtn = document.createElement('button');
827
  deleteBtn.className = 'storage-btn';
 
831
  renderStorage();
832
  };
833
 
 
834
  actionsCell.appendChild(deleteBtn);
835
 
836
  row.appendChild(keyCell);
 
859
  if (!cookie.trim()) return;
860
 
861
  const [name, ...valueParts] = cookie.split('=');
862
+ const decodedName = decodeURIComponent(name.trim());
863
  const value = valueParts.join('=').trim();
864
 
865
  const row = document.createElement('tr');
866
 
867
  const nameCell = document.createElement('td');
868
+ const nameSpan = document.createElement('span');
869
+ nameSpan.className = 'editable';
870
+ nameSpan.textContent = decodedName;
871
+ nameSpan.onclick = () => {
872
+ const newName = prompt('新しい名前を入力', decodedName);
873
+ if (newName && newName !== decodedName) {
874
+ const newValue = prompt('新しい値を入力', decodeURIComponent(value));
875
+ if (newValue !== null) {
876
+ document.cookie = `${encodeURIComponent(newName)}=${encodeURIComponent(newValue)}; expires=Fri, 31 Dec 9999 23:59:59 GMT; path=/`;
877
+ document.cookie = `${name.trim()}=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/`;
878
+ renderStorage();
879
+ }
880
+ }
881
+ };
882
+ nameCell.appendChild(nameSpan);
883
 
884
  const valueCell = document.createElement('td');
885
+ const valueSpan = document.createElement('span');
886
+ valueSpan.className = 'editable';
887
+ valueSpan.textContent = decodeURIComponent(value);
888
+ valueSpan.onclick = () => {
889
+ const newValue = prompt('新しい値を入力', decodeURIComponent(value));
890
+ if (newValue !== null) {
891
+ document.cookie = `${name.trim()}=${encodeURIComponent(newValue)}; expires=Fri, 31 Dec 9999 23:59:59 GMT; path=/`;
892
+ renderStorage();
893
+ }
894
+ };
895
+ valueCell.appendChild(valueSpan);
896
 
897
  const actionsCell = document.createElement('td');
898
  actionsCell.className = 'storage-actions';
 
950
 
951
  document.body.appendChild(button);
952
  }
953
+
954
+ // 初期化
955
  document.addEventListener('DOMContentLoaded', function() {
 
956
  createDevTools();
957
  createOpenButton();
 
 
958
  console.log('開発者ツールが初期化されました');
959
  console.log('このコンソールでJavaScriptを実行できます');
960
+ });
961
  })();