DevTools Sidebar — HTML

HTML template builders for DevTools Sidebar

このスクリプトは単体で利用できません。右のようなメタデータを含むスクリプトから、ライブラリとして読み込まれます: // @require https://update.greasyfork.org/scripts/580257/1839659/DevTools%20Sidebar%20%E2%80%94%20HTML.js

スクリプトをインストールするには、Tampermonkey, GreasemonkeyViolentmonkey のような拡張機能のインストールが必要です。

You will need to install an extension such as Tampermonkey to install this script.

スクリプトをインストールするには、TampermonkeyViolentmonkey のような拡張機能のインストールが必要です。

スクリプトをインストールするには、TampermonkeyUserscripts のような拡張機能のインストールが必要です。

このスクリプトをインストールするには、Tampermonkeyなどの拡張機能をインストールする必要があります。

このスクリプトをインストールするには、ユーザースクリプト管理ツールの拡張機能をインストールする必要があります。

(ユーザースクリプト管理ツールは設定済みなのでインストール!)

このスタイルをインストールするには、Stylusなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus などの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus tなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

(ユーザースタイル管理ツールは設定済みなのでインストール!)

このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください
// ==UserScript==
// @name         DevTools Sidebar — HTML
// @namespace    http://tampermonkey.net/
// @version      7.2.3
// @description  HTML template builders for DevTools Sidebar
// @author       MrNosferatu
// ==/UserScript==

// ─── Build merged Network panel ───────────────────────────────────────────────
function buildNetworkPanel() {
  function tip(text) {
    return `<span class="dt-tip"><span class="dt-tip-icon">?</span><span class="dt-tip-text">${text}</span></span>`;
  }
  return `
    <div class="dt-section">
      <div class="dt-slabel">Filter</div>
      <div class="dt-row" style="margin-bottom:8px">
        <div class="dt-row-label" style="display:flex;align-items:center;gap:5px">Methods ${tip('Applies to both request and response interceptors, and benchmark capture.')}</div>
      </div>
      <div class="dt-method-grid">${ALL_METHODS.map(m=>`<input type="checkbox" class="dt-method-check" id="dt-net-m-${m}" data-m="${m}"><label class="dt-method-pill" for="dt-net-m-${m}">${m}</label>`).join('')}</div>
      <div style="margin-top:14px">
        <div class="dt-row" style="margin-bottom:6px">
          <div class="dt-row-label" style="display:flex;align-items:center;gap:5px">URL Mode ${tip('Auto intercepts every URL. Manual filters by the regex pattern below.')}</div>
          <div class="dt-chip" id="dt-net-chip">Auto</div>
        </div>
        <div class="dt-mode-group">
          <button class="dt-mode-btn active" data-mode="auto" data-ns="net">Auto</button>
          <button class="dt-mode-btn" data-mode="manual" data-ns="net">Regex</button>
        </div>
        <div class="dt-regex-wrap" id="dt-net-rwrap">
          <div class="dt-regex-field" style="margin-top:8px">
            <span class="dt-regex-delim">/</span>
            <input class="dt-regex-input" id="dt-net-regex" type="text" placeholder="api\\/v\\d+\\/.*" spellcheck="false">
            <span class="dt-regex-delim">/</span>
            <div class="dt-regex-dot" id="dt-net-rdot"></div>
          </div>
        </div>
      </div>
    </div>

    <div class="dt-section">
      <div class="dt-slabel">Request</div>
      <div class="dt-row" style="margin-bottom:10px">
        <div class="dt-row-label" style="display:flex;align-items:center;gap:5px">Intercept ${tip('Halt outgoing requests before they are sent. GET requests show URL params editor; others show body editor.')}</div>
        <label class="dt-toggle"><input type="checkbox" id="dt-req-enabled"><div class="dt-toggle-track"><div class="dt-toggle-thumb"></div></div></label>
      </div>
      <div class="dt-queue"><div class="dt-queue-dot" id="dt-req-qdot"></div><span class="dt-queue-text" id="dt-req-qtext">No requests queued</span></div>
      <div class="dt-row" id="dt-req-persist-row" style="margin-top:10px;margin-bottom:0">
        <div class="dt-row-label" style="display:flex;align-items:center;gap:5px">Persist ${tip('Keep intercept enabled across page reloads.')}</div>
        <label class="dt-toggle"><input type="checkbox" id="dt-req-persist"><div class="dt-toggle-track"><div class="dt-toggle-thumb"></div></div></label>
      </div>
    </div>

    <div class="dt-section">
      <div class="dt-slabel">Response</div>
      <div class="dt-row" style="margin-bottom:10px">
        <div class="dt-row-label" style="display:flex;align-items:center;gap:5px">Intercept ${tip('Hold API responses and allow editing the body and headers before the page receives them.')}</div>
        <label class="dt-toggle"><input type="checkbox" id="dt-res-enabled"><div class="dt-toggle-track"><div class="dt-toggle-thumb"></div></div></label>
      </div>
      <div class="dt-queue"><div class="dt-queue-dot" id="dt-res-qdot"></div><span class="dt-queue-text" id="dt-res-qtext">No responses queued</span></div>
      <div class="dt-row" id="dt-res-persist-row" style="margin-top:10px;margin-bottom:0">
        <div class="dt-row-label" style="display:flex;align-items:center;gap:5px">Persist ${tip('Keep intercept enabled across page reloads.')}</div>
        <label class="dt-toggle"><input type="checkbox" id="dt-res-persist"><div class="dt-toggle-track"><div class="dt-toggle-thumb"></div></div></label>
      </div>
    </div>

    <div class="dt-section">
      <div class="dt-slabel">Transforms</div>
      <div class="dt-row" style="margin-bottom:10px">
        <div class="dt-row-label" style="display:flex;align-items:center;gap:5px">Auto-apply ${tip('Automatically apply a matching enabled preset to responses — no modal shown.')}</div>
        <label class="dt-toggle"><input type="checkbox" id="dt-res-auto-transform"><div class="dt-toggle-track"><div class="dt-toggle-thumb"></div></div></label>
      </div>
      <div class="dt-at-hint" id="dt-res-auto-transform-hint" style="font-size:11px;color:var(--mu);margin-bottom:10px"></div>
      <button class="dt-btn-presets" id="dt-res-presets-btn">
        <svg width="13" height="13" viewBox="0 0 13 13" fill="none" stroke="currentColor" stroke-width="1.5"><circle cx="6.5" cy="6.5" r="2"/><path d="M6.5 1v1.5M6.5 10.5V12M1 6.5h1.5M10.5 6.5H12M2.6 2.6l1.1 1.1M9.3 9.3l1.1 1.1M9.3 2.6l-1.1 1.1M3.7 9.3l-1.1 1.1"/></svg>
        Manage Presets
      </button>
    </div>
  `;
}

// ─── Keep buildInterceptorPanel as alias for nothing (no longer used directly)
function buildInterceptorPanel(ns) { return ''; }


// ─── Sidebar settings panel HTML ──────────────────────────────────────────────

// ─── Sidebar settings panel HTML ──────────────────────────────────────────────
function buildSidebarSettingsPanel() {
  return `
    <div class="dt-section">
      <div class="dt-slabel">Layout</div>
      <div class="dt-row" style="margin-bottom:10px">
        <div class="dt-row-label">Position</div>
        <div class="dt-side-toggle" id="dt-sb-side-toggle">
          <button class="dt-side-btn" data-side="left">Left</button>
          <button class="dt-side-btn active" data-side="right">Right</button>
        </div>
      </div>
      <div class="dt-row" style="margin-bottom:6px"><div class="dt-row-label">Width</div></div>
      <div class="dt-slider-row">
        <button class="dt-slider-step" id="dt-sb-width-dec">−</button>
        <input type="range" class="dt-size-slider" id="dt-sb-width-slider" min="280" max="720" step="4" style="flex:1">
        <button class="dt-slider-step" id="dt-sb-width-inc">+</button>
        <input type="text" class="dt-slider-val-input" id="dt-sb-width-val" value="360">
      </div>
    </div>

    <div class="dt-section">
      <div class="dt-slabel">Appearance</div>
      <div class="dt-appearance-tabs">
        <button class="dt-appearance-tab" data-sbmode="light">Light</button>
        <button class="dt-appearance-tab" data-sbmode="dark">Dark</button>
        <button class="dt-appearance-tab" data-sbmode="auto">Auto</button>
        <button class="dt-appearance-tab" data-sbmode="custom">Custom</button>
      </div>
      <div id="dt-sb-custom-colors" style="display:none">
        <div class="dt-custom-colors">
          <div class="dt-color-group"><div class="dt-color-label">Background</div><div class="dt-color-input-row"><input type="color" class="dt-color-picker" id="dt-sb-sbg-picker"><input type="text" class="dt-color-hex" id="dt-sb-sbg-hex" placeholder="#1a1b1e" maxlength="9"><button class="dt-color-clear" id="dt-sb-sbg-clear">✕</button></div></div>
          <div class="dt-color-group"><div class="dt-color-label">Surface</div><div class="dt-color-input-row"><input type="color" class="dt-color-picker" id="dt-sb-ssf-picker"><input type="text" class="dt-color-hex" id="dt-sb-ssf-hex" placeholder="#222327" maxlength="9"><button class="dt-color-clear" id="dt-sb-ssf-clear">✕</button></div></div>
          <div class="dt-color-group"><div class="dt-color-label">Text</div><div class="dt-color-input-row"><input type="color" class="dt-color-picker" id="dt-sb-stx-picker"><input type="text" class="dt-color-hex" id="dt-sb-stx-hex" placeholder="#e4e6f0" maxlength="9"><button class="dt-color-clear" id="dt-sb-stx-clear">✕</button></div></div>
          <div class="dt-color-group"><div class="dt-color-label">Border</div><div class="dt-color-input-row"><input type="color" class="dt-color-picker" id="dt-sb-sbd-picker"><input type="text" class="dt-color-hex" id="dt-sb-sbd-hex" placeholder="#333540" maxlength="9"><button class="dt-color-clear" id="dt-sb-sbd-clear">✕</button></div></div>
        </div>
      </div>
    </div>

    <div class="dt-section">
      <div class="dt-slabel">Editor Theme</div>
      <div class="dt-theme-grid" id="dt-sb-theme-grid"></div>
      <div id="dt-sb-editor-custom-colors" style="display:none;margin-top:12px">
        <div class="dt-custom-colors">
          <div class="dt-color-group"><div class="dt-color-label">Background</div><div class="dt-color-input-row"><input type="color" class="dt-color-picker" id="dt-sb-bg-picker"><input type="text" class="dt-color-hex" id="dt-sb-bg-hex" placeholder="#1e1e2e" maxlength="9"><button class="dt-color-clear" id="dt-sb-bg-clear">✕</button></div></div>
          <div class="dt-color-group"><div class="dt-color-label">Text</div><div class="dt-color-input-row"><input type="color" class="dt-color-picker" id="dt-sb-text-picker"><input type="text" class="dt-color-hex" id="dt-sb-text-hex" placeholder="#cdd6f4" maxlength="9"><button class="dt-color-clear" id="dt-sb-text-clear">✕</button></div></div>
        </div>
      </div>
    </div>

    <div class="dt-section">
      <div class="dt-slabel">Editor Font</div>
      <div class="dt-font-list" id="dt-sb-font-list"></div>
    </div>

    <div class="dt-section">
      <div class="dt-row" style="margin-bottom:6px"><div class="dt-row-label">Font Size</div></div>
      <div class="dt-slider-row">
        <button class="dt-slider-step" id="dt-sb-size-dec">−</button>
        <input type="range" class="dt-size-slider" id="dt-sb-size-slider" min="9" max="18" step="1" style="flex:1">
        <button class="dt-slider-step" id="dt-sb-size-inc">+</button>
        <input type="text" class="dt-slider-val-input" id="dt-sb-size-val" value="12">
      </div>
      <div style="margin-top:12px;border-radius:8px;overflow:hidden" id="dt-sb-preview">
        <div class="dt-editor-wrap" style="border-radius:6px;margin:0">
          <div style="padding:10px 12px" id="dt-sb-preview-inner">const greeting = "Hello, DevTools!";</div>
        </div>
      </div>
    </div>

    <div class="dt-section" style="display:flex;gap:8px">
      <button class="dt-btn-reset" id="dt-sb-settings-reset">Reset</button>
      <button class="dt-btn-apply" id="dt-sb-settings-apply">Apply</button>
    </div>
  `;
}

// ─── Editor HTML ──────────────────────────────────────────────────────────────
// The cURL copy button lives here in the editor bar — a secondary utility
// action, consistent with Format/Minify. Only rendered for the request editor.
function buildEditorHTML(id) {
  const isCurlEditor = id === 'dt-req-ed';
  return `
    <div class="dt-editor-wrap" id="${id}-wrap">
      <div class="dt-editor-outer">
        <div class="dt-hl-overlay" id="${id}-hl" aria-hidden="true"></div>
        <textarea class="dt-editor" id="${id}" spellcheck="false"></textarea>
      </div>
      <div class="dt-editor-bar" id="${id}-bar">
        <button class="dt-editor-btn" id="${id}-fmt">Format</button>
        <button class="dt-editor-btn" id="${id}-min">Minify</button>
        <span class="dt-json-badge" id="${id}-badge">—</span>
        ${isCurlEditor ? `<button class="dt-editor-btn" id="dt-req-copy-curl">Copy as cURL</button>` : ''}
        <button class="dt-editor-btn dt-search-toggle-btn" id="${id}-stoggle" title="Find (Ctrl+F)">⌕ Find</button>
      </div>
      <div class="dt-search-bar hidden" id="${id}-sbar">
        <div class="dt-search-wrap" id="${id}-swrap">
          <span class="dt-search-icon">⌕</span>
          <input class="dt-search-input" id="${id}-sinput" type="text" placeholder="Search…" spellcheck="false">
        </div>
        <span class="dt-search-count" id="${id}-scount">—</span>
        <button class="dt-snav" id="${id}-sprev" title="Prev (Shift+Enter)">↑</button>
        <button class="dt-snav" id="${id}-snext" title="Next (Enter)">↓</button>
        <button class="dt-sclose" id="${id}-sclose">✕</button>
      </div>
    </div>
  `;
}

// ─── Benchmark panel HTML ─────────────────────────────────────────────────────
function buildBenchPanel() {
  return `
    <!-- Mode selector: Off / Manual / Capture -->
    <div class="dt-section">
      <div class="dt-slabel">Mode</div>
      <div class="dt-mode-group" style="margin-top:0">
        <button class="dt-mode-btn active" id="dt-bench-mode-off">
          <svg width="11" height="11" viewBox="0 0 11 11" fill="none" stroke="currentColor" stroke-width="1.6" style="vertical-align:middle;margin-right:4px"><circle cx="5.5" cy="5.5" r="4.5"/><line x1="3.3" y1="3.3" x2="7.7" y2="7.7"/></svg>Off
        </button>
        <button class="dt-mode-btn" id="dt-bench-mode-manual">
          <svg width="11" height="11" viewBox="0 0 11 11" fill="none" stroke="currentColor" stroke-width="1.6" style="vertical-align:middle;margin-right:4px"><path d="M2 2h7M2 5.5h5M2 9h6"/></svg>Manual
        </button>
        <button class="dt-mode-btn" id="dt-bench-mode-capture">
          <svg width="11" height="11" viewBox="0 0 11 11" fill="none" stroke="currentColor" stroke-width="1.6" style="vertical-align:middle;margin-right:4px"><circle cx="5.5" cy="5.5" r="4"/><circle cx="5.5" cy="5.5" r="1.5" fill="currentColor" stroke="none"/></svg>Capture
        </button>
      </div>
    </div>

    <!-- Manual URL input -->
    <div class="dt-section" id="dt-bench-manual-section" style="display:none">
      <div class="dt-slabel">Request</div>

      <!-- cURL paste area -->
      <div id="dt-bench-curl-wrap" style="margin-bottom:10px">
        <div class="dt-flabel" style="margin-bottom:5px">
          Paste cURL
          <span style="font-weight:400;color:var(--mu);margin-left:4px">— or fill the fields below</span>
        </div>
        <textarea class="dt-bench-body-ed" id="dt-bench-curl-input" spellcheck="false" rows="2"
          placeholder="curl 'https://api.example.com/data' -H 'Authorization: Bearer token' -d '{}'"></textarea>
        <button class="dt-pe-add-pattern" id="dt-bench-curl-parse" style="margin-top:6px">
          <svg width="11" height="11" viewBox="0 0 11 11" fill="none" stroke="currentColor" stroke-width="1.8"><polyline points="2,3 5.5,6.5 9,3"/><line x1="5.5" y1="6.5" x2="5.5" y2="1"/><line x1="1" y1="9" x2="10" y2="9"/></svg>
          Parse cURL into fields
        </button>
      </div>

      <div style="display:flex;gap:6px;margin-bottom:10px">
        <select class="dt-bench-method-sel" id="dt-bench-method">
          ${['GET','POST','PUT','PATCH','DELETE'].map(m=>`<option value="${m}">${m}</option>`).join('')}
        </select>
        <input class="dt-bench-url-input" id="dt-bench-url" type="text" placeholder="https://api.example.com/endpoint" spellcheck="false">
      </div>
      <div class="dt-flabel" style="margin-bottom:6px">Headers <span style="font-weight:400;color:var(--mu)">(optional)</span></div>
      <div class="dt-pe-patterns-list" id="dt-bench-headers-list"></div>
      <button class="dt-pe-add-pattern" id="dt-bench-add-header">
        <svg width="11" height="11" viewBox="0 0 11 11" fill="none" stroke="currentColor" stroke-width="1.8"><line x1="5.5" y1="1" x2="5.5" y2="10"/><line x1="1" y1="5.5" x2="10" y2="5.5"/></svg>
        Add header
      </button>
      <div id="dt-bench-body-wrap" style="display:none;margin-top:12px">
        <div class="dt-flabel" style="margin-bottom:6px">Body</div>
        <textarea class="dt-bench-body-ed" id="dt-bench-body" spellcheck="false" placeholder='{"key": "value"}'></textarea>
      </div>
    </div>

    <!-- Capture mode -->
    <div class="dt-section" id="dt-bench-capture-section" style="display:none">
      <div id="dt-bench-cap-persist-row" class="dt-row" style="margin-bottom:12px">
        <div class="dt-row-info">
          <div class="dt-row-label">Persist</div>
          <div class="dt-row-sub">Keep capture mode enabled across reloads</div>
        </div>
        <label class="dt-toggle"><input type="checkbox" id="dt-bench-cap-persist"><div class="dt-toggle-track"><div class="dt-toggle-thumb"></div></div></label>
      </div>
      <div class="dt-flabel" style="margin-bottom:6px">Capture Methods</div>
      <div class="dt-method-grid" style="margin-bottom:12px">${ALL_METHODS.map(m=>`<input type="checkbox" class="dt-method-check" id="dt-bench-cap-m-${m}" data-m="${m}" checked><label class="dt-method-pill" for="dt-bench-cap-m-${m}">${m}</label>`).join('')}</div>
      <div class="dt-flabel" style="margin-bottom:4px">URL Filter</div>
      <div class="dt-mode-group" style="margin-bottom:8px">
        <button class="dt-mode-btn active" data-mode="auto" data-ns="bench-cap">Auto — All</button>
        <button class="dt-mode-btn" data-mode="manual" data-ns="bench-cap">Manual — Regex</button>
      </div>
      <div class="dt-regex-wrap" id="dt-bench-cap-rwrap">
        <div class="dt-regex-field">
          <span class="dt-regex-delim">/</span>
          <input class="dt-regex-input" id="dt-bench-cap-regex" type="text" placeholder="api\\/v\\d+\\/.*" spellcheck="false">
          <span class="dt-regex-delim">/</span>
          <div class="dt-regex-dot" id="dt-bench-cap-rdot"></div>
        </div>
      </div>
      <div class="dt-bench-capture-list" id="dt-bench-capture-list" style="margin-top:10px">
        <div class="dt-bench-capture-empty" id="dt-bench-capture-empty">Enable capture, then browse the page — requests will appear here.</div>
      </div>
    </div>

    <!-- Selected request preview (shared between modes) -->
    <div class="dt-section" id="dt-bench-selected-section" style="display:none">
      <div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:12px">
        <div class="dt-slabel" style="margin-bottom:0">Selected Request</div>
        <button class="dt-bench-clear-sel" id="dt-bench-clear-sel">Clear</button>
      </div>
      <div class="dt-bench-sel-pill" id="dt-bench-sel-pill"></div>
    </div>

    <!-- Run config -->
    <div class="dt-section" id="dt-bench-config-section">
      <div class="dt-slabel">Run Config</div>
      <div class="dt-bench-config-grid">
        <div class="dt-bench-config-item">
          <div class="dt-flabel">Iterations</div>
          <input class="dt-bench-num-input" id="dt-bench-iters" type="number" min="1" max="500" value="10">
        </div>
        <div class="dt-bench-config-item">
          <div class="dt-flabel">Concurrency</div>
          <input class="dt-bench-num-input" id="dt-bench-concurrency" type="number" min="1" max="20" value="1">
        </div>
        <div class="dt-bench-config-item">
          <div class="dt-flabel">Delay (ms)</div>
          <input class="dt-bench-num-input" id="dt-bench-delay" type="number" min="0" max="5000" value="0">
        </div>
        <div class="dt-bench-config-item">
          <div class="dt-flabel">Warmup</div>
          <label class="dt-toggle" style="margin-top:4px"><input type="checkbox" id="dt-bench-warmup" checked><div class="dt-toggle-track"><div class="dt-toggle-thumb"></div></div></label>
        </div>
      </div>
      <button class="dt-bench-run-btn" id="dt-bench-run">
        <svg width="12" height="12" viewBox="0 0 12 12" fill="currentColor"><polygon points="2,1 11,6 2,11"/></svg>
        Run Benchmark
      </button>
    </div>

    <!-- Results -->
    <div class="dt-section" id="dt-bench-results-section" style="display:none">
      <div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:12px">
        <div class="dt-slabel" style="margin-bottom:0">Results</div>
        <div style="display:flex;gap:6px">
          <button class="dt-bench-copy-btn" id="dt-bench-copy-results">Copy</button>
          <button class="dt-bench-copy-btn dt-bench-clear-results-btn" id="dt-bench-clear-results">Clear</button>
        </div>
      </div>

      <!-- Last result accordion — sits at top, expands downward -->
      <div class="dt-bench-accordion" id="dt-bench-last-result" style="display:none;margin-bottom:12px">
        <button class="dt-bench-accordion-hd" id="dt-bench-last-result-toggle">
          <span class="dt-bench-accordion-label">Last Request</span>
          <span class="dt-bench-accordion-pill" id="dt-bench-last-result-pill"></span>
          <svg class="dt-bench-accordion-arrow" width="10" height="10" viewBox="0 0 10 10" fill="none" stroke="currentColor" stroke-width="1.8"><polyline points="2,3.5 5,6.5 8,3.5"/></svg>
        </button>
        <div class="dt-bench-accordion-body" id="dt-bench-last-result-body" style="display:none">
          <div class="dt-bench-accordion-row" id="dt-bench-last-url-row">
            <div class="dt-bench-accordion-key">URL</div>
            <div class="dt-bench-accordion-val" id="dt-bench-last-url"></div>
          </div>
          <div class="dt-bench-accordion-row" id="dt-bench-last-params-row" style="display:none">
            <div class="dt-bench-accordion-key">Params</div>
            <div class="dt-bench-accordion-val" id="dt-bench-last-params"></div>
          </div>
          <div class="dt-bench-accordion-row" id="dt-bench-last-headers-row" style="display:none">
            <div class="dt-bench-accordion-key">Headers</div>
            <div class="dt-bench-accordion-val" id="dt-bench-last-headers"></div>
          </div>
          <div class="dt-bench-accordion-row" id="dt-bench-last-body-row" style="display:none">
            <div class="dt-bench-accordion-key">Body</div>
            <div class="dt-bench-accordion-val" id="dt-bench-last-body"></div>
          </div>
        </div>
      </div>

      <div class="dt-bench-progress" id="dt-bench-progress" style="display:none">
        <div class="dt-bench-progress-bar"><div class="dt-bench-progress-fill" id="dt-bench-progress-fill"></div></div>
        <div class="dt-bench-progress-label" id="dt-bench-progress-label">Running…</div>
      </div>
      <div class="dt-bench-stats" id="dt-bench-stats"></div>
      <canvas class="dt-bench-chart" id="dt-bench-chart" height="54"></canvas>
      <div class="dt-bench-run-list" id="dt-bench-run-list"></div>
    </div>
  `;
}

// ─── Full HTML ────────────────────────────────────────────────────────────────
const HTML = `
  <div id="dt-tab" title="DevTools Sidebar">
    <div id="dt-tab-inner">
      <div id="dt-tab-label">DevTools</div><div id="dt-tab-chevron">‹</div>
    </div>
  </div>

  <div id="dt-sidebar">
    <div id="dt-sb-drag-handle"></div>
    <div class="dt-head">
      <div class="dt-head-icon">🛠</div>
      <div class="dt-head-text"><div class="dt-head-title">DevTools</div><div class="dt-head-sub">Developer Utilities</div></div>
      <button class="dt-head-close" id="dt-close-btn">✕</button>
    </div>
    <div class="dt-nav">
      <button class="dt-nav-btn active" data-panel="network">Network</button>
      <button class="dt-nav-btn" data-panel="bench">Bench</button>
      <button class="dt-nav-btn" data-panel="settings">Settings</button>
    </div>
    <div class="dt-scroll">
      <div class="dt-panel active" id="dt-panel-network">${buildNetworkPanel()}</div>
      <div class="dt-panel" id="dt-panel-bench">${buildBenchPanel()}</div>
      <div class="dt-panel" id="dt-panel-settings">${buildSidebarSettingsPanel()}</div>
      <div class="dt-panel" id="dt-panel-about">
        <div class="dt-about-hero"><div class="dt-about-icon">🛠</div><div class="dt-about-title">DevTools Sidebar</div><div class="dt-about-version">v7.0.0 · Tampermonkey</div></div>
        <div class="dt-feature-list">
          <div class="dt-feature-item"><div class="dt-feature-ico"></div><div><div class="dt-feature-name">Request Interceptor</div><div class="dt-feature-desc">Edit request body or URL params before sending. Supports GET/POST/PUT/PATCH/DELETE.</div></div></div>
          <div class="dt-feature-item"><div class="dt-feature-ico"></div><div><div class="dt-feature-name">Response Interceptor</div><div class="dt-feature-desc">Capture & transform responses. Manual edit, GUI path extractor, or custom JS transform.</div></div></div>
          <div class="dt-feature-item"><div class="dt-feature-ico"></div><div><div class="dt-feature-name">Editor Theming</div><div class="dt-feature-desc">Catppuccin, Monokai, Nord, Dracula, VS Light presets + custom background & text colors, font, and size. Edit live from the Settings tab.</div></div></div>
        </div>
      </div>
    </div>
  </div>

  <!-- Request Modal -->
  <div id="dt-req-overlay" class="dt-overlay">
    <div id="dt-req-modal" class="dt-modal">
      <div class="dt-modal-head">
        <div class="dt-modal-icon req">⚡</div>
        <div class="dt-modal-meta"><div class="dt-modal-title">Request Intercepted</div><div class="dt-modal-url" id="dt-req-url"></div></div>
        <div class="dt-method-tag POST" id="dt-req-method">POST</div>
      </div>
      <div class="dt-modal-body" id="dt-req-body">
        <div class="dt-modal-inner">
          <div id="dt-req-editor-section" class="dt-payload-section">
            <div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:4px">
              <div class="dt-flabel" style="margin-bottom:0">Request Payload</div>
              <button class="dt-revert-btn" id="dt-req-ed-revert" title="Revert to original">
                <svg width="11" height="11" viewBox="0 0 11 11" fill="none" stroke="currentColor" stroke-width="1.6"><path d="M1.5 5.5A4 4 0 1 1 3 8.5"/><polyline points="1,3 1.5,5.5 4,5"/></svg>
                Revert
              </button>
            </div>
            ${buildEditorHTML('dt-req-ed')}
          </div>
          <div id="dt-req-params-section" class="dt-params-section" style="display:none">
            <div class="dt-flabel">URL Parameters</div>
            <div class="dt-params-list" id="dt-req-params-list"></div>
            <button class="dt-add-param" id="dt-req-add-param">+ Add Parameter</button>
          </div>
          <div class="dt-headers-section">
            <div class="dt-headers-toggle" id="dt-req-htoggle">
              <span class="dt-headers-arrow">›</span>
              <span class="dt-headers-label">Request Headers</span>
              <span class="dt-headers-count" id="dt-req-hcount"></span>
              <button class="dt-hrevert-btn" id="dt-req-hrevert" title="Revert headers to original" style="display:none">
                <svg width="10" height="10" viewBox="0 0 10 10" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M1 4.5A3.5 3.5 0 1 1 2.5 7.8"/><polyline points="1,2.5 1,4.5 3,4.5"/></svg>
                Revert
              </button>
            </div>
            <div class="dt-headers-body" id="dt-req-hbody"><div class="dt-headers-inner" id="dt-req-hinner"></div></div>
          </div>
        </div>
      </div>
      <div class="dt-modal-foot">
        <button class="dt-foot-btn dt-foot-btn-abort" id="dt-req-abort">Abort</button>
        <span class="dt-modal-count" id="dt-req-count"></span>
        <button class="dt-foot-btn dt-foot-btn-send" id="dt-req-send">Send Request →</button>
      </div>
    </div>
  </div>

  <!-- Response Modal -->
  <div id="dt-res-overlay" class="dt-overlay">
    <div id="dt-res-modal" class="dt-modal">
      <div class="dt-modal-head">
        <div class="dt-modal-icon res">↩</div>
        <div class="dt-modal-meta"><div class="dt-modal-title">Response Intercepted</div><div class="dt-modal-url" id="dt-res-url"></div></div>
        <div class="dt-method-tag GET" id="dt-res-method">GET</div>
      </div>
      <div class="dt-modal-body" id="dt-res-body">
        <div class="dt-modal-inner">
          <div id="dt-res-status-bar"></div>
          <div class="dt-res-tabs">
            <button class="dt-res-tab active" data-restab="manual"><svg width="12" height="12" viewBox="0 0 12 12" fill="none" stroke="currentColor" stroke-width="1.5" style="vertical-align:middle;margin-right:4px"><path d="M2 3h8M2 6h5M2 9h6"/></svg>Manual Edit</button>
            <button class="dt-res-tab" data-restab="gui"><svg width="12" height="12" viewBox="0 0 12 12" fill="none" stroke="currentColor" stroke-width="1.5" style="vertical-align:middle;margin-right:4px"><rect x="1" y="1" width="4" height="4" rx="0.5"/><rect x="7" y="1" width="4" height="4" rx="0.5"/><rect x="1" y="7" width="4" height="4" rx="0.5"/><rect x="7" y="7" width="4" height="4" rx="0.5"/></svg>GUI Extract</button>
            <button class="dt-res-tab" data-restab="code"><svg width="12" height="12" viewBox="0 0 12 12" fill="none" stroke="currentColor" stroke-width="1.5" style="vertical-align:middle;margin-right:4px"><polyline points="3,4 1,6 3,8"/><polyline points="9,4 11,6 9,8"/><line x1="7" y1="2" x2="5" y2="10"/></svg>JS Transform</button>
          </div>
          <div id="dt-res-manual" class="dt-payload-section">
            <div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:4px">
              <div class="dt-flabel" style="margin-bottom:0">Response Body</div>
              <button class="dt-revert-btn" id="dt-res-ed-revert" title="Revert to original">
                <svg width="11" height="11" viewBox="0 0 11 11" fill="none" stroke="currentColor" stroke-width="1.6"><path d="M1.5 5.5A4 4 0 1 1 3 8.5"/><polyline points="1,3 1.5,5.5 4,5"/></svg>
                Revert
              </button>
            </div>
            ${buildEditorHTML('dt-res-ed')}
          </div>
          <div id="dt-res-gui" class="dt-transform-section" style="display:none">
            <div class="dt-transform-note">Click a key in the tree to select a path. Then choose an action to apply to the response.</div>
            <div class="dt-transform-cols">
              <div class="dt-transform-pane">
                <div class="dt-transform-pane-label">Response Tree</div>
                <div class="dt-tree" id="dt-res-tree"></div>
              </div>
              <div class="dt-transform-pane" style="max-width:200px;flex-shrink:0">
                <div class="dt-transform-pane-label">Actions</div>
                <div class="dt-path-builder">
                  <div class="dt-transform-pane-label" style="color:var(--vi);margin-bottom:4px">Selected path:</div>
                  <div class="dt-path-display" id="dt-res-path-display">—</div>
                  <div class="dt-path-actions">
                    <button class="dt-path-btn dt-path-btn-extract" id="dt-res-extract-btn">Extract value</button>
                    <button class="dt-path-btn dt-path-btn-wrap" id="dt-res-wrap-btn">Wrap in key</button>
                  </div>
                  <input class="dt-custom-wrap-input" id="dt-res-wrap-key" type="text" placeholder='wrap key name e.g. "data"' style="display:none">
                </div>
              </div>
            </div>
          </div>
          <div id="dt-res-code" class="dt-code-section" style="display:none">
            <div class="dt-code-note">
              Write a JS function body. You receive <code>data</code> (parsed JSON or raw string) and <code>res</code> (metadata).<br>
              Return the value that should replace the response. Example:<br>
              <code>return data.res.data;</code> or <code>return { items: data.items, total: data.meta.total };</code>
            </div>
            <textarea class="dt-transform-editor" id="dt-res-code-editor" spellcheck="false" placeholder="// data = parsed response JSON&#10;// res = { url, method, status }&#10;return data;"></textarea>
            <div style="display:flex;gap:8px;align-items:center;flex-shrink:0">
              <button class="dt-transform-run-btn" id="dt-res-run-btn">
                <svg width="12" height="12" viewBox="0 0 12 12" fill="currentColor"><polygon points="2,1 11,6 2,11"/></svg>
                Run Transform
              </button>
              <button class="dt-transform-save-btn" id="dt-res-save-preset" title="Save this transform as a preset">
                <svg width="12" height="12" viewBox="0 0 12 12" fill="none" stroke="currentColor" stroke-width="1.5"><rect x="1" y="1" width="10" height="10" rx="1"/><rect x="3.5" y="1" width="5" height="3.5" rx="0.5"/><rect x="3" y="6.5" width="6" height="4" rx="0.5"/></svg>
                Save Preset
              </button>
              <div class="dt-transform-err" id="dt-res-transform-err"></div>
            </div>
            <div id="dt-res-preview" class="dt-res-preview-section">
              <div class="dt-flabel" style="margin-bottom:8px">Preview (Before → After)</div>
              <div class="dt-preview-container">
                <div class="dt-preview-pane">
                  <div class="dt-preview-label">Original</div>
                  <div class="dt-preview-content dt-editor-themed" id="dt-res-preview-original"></div>
                </div>
                <div class="dt-preview-arrow">→</div>
                <div class="dt-preview-pane">
                  <div class="dt-preview-label">Transformed</div>
                  <div class="dt-preview-content dt-editor-themed" id="dt-res-preview-transformed"></div>
                </div>
              </div>
            </div>
          </div>
          <div class="dt-headers-section">
            <div class="dt-headers-toggle" id="dt-res-htoggle">
              <span class="dt-headers-arrow">›</span>
              <span class="dt-headers-label">Response Headers</span>
              <span class="dt-headers-count" id="dt-res-hcount"></span>
              <button class="dt-hrevert-btn" id="dt-res-hrevert" title="Revert headers to original" style="display:none">
                <svg width="10" height="10" viewBox="0 0 10 10" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M1 4.5A3.5 3.5 0 1 1 2.5 7.8"/><polyline points="1,2.5 1,4.5 3,4.5"/></svg>
                Revert
              </button>
            </div>
            <div class="dt-headers-body" id="dt-res-hbody"><div class="dt-headers-inner" id="dt-res-hinner"></div></div>
          </div>
        </div>
      </div>
      <div class="dt-modal-foot">
        <button class="dt-foot-btn dt-foot-btn-abort" id="dt-res-abort">Passthrough (Original)</button>
        <span class="dt-modal-count" id="dt-res-count"></span>
        <button class="dt-foot-btn dt-foot-btn-send res-send" id="dt-res-send">Apply Response →</button>
      </div>
    </div>
  </div>

  <!-- Presets Modal — must be after dt-res-overlay in DOM so it stacks on top -->
  <div id="dt-presets-overlay" class="dt-overlay">
    <div id="dt-presets-modal" class="dt-modal" style="max-width:560px;width:90%;height:auto;min-height:300px;max-height:80vh">
      <div class="dt-modal-head">
        <div class="dt-modal-icon res">
          <svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6"><rect x="2" y="2" width="5" height="5" rx="1"/><rect x="9" y="2" width="5" height="5" rx="1"/><rect x="2" y="9" width="5" height="5" rx="1"/><rect x="9" y="9" width="5" height="5" rx="1"/></svg>
        </div>
        <div class="dt-modal-meta">
          <div class="dt-modal-title" id="dt-presets-modal-title">Response Transform Presets</div>
          <div class="dt-modal-url">Save &amp; manage transform presets</div>
        </div>
      </div>
      <div class="dt-modal-body" id="dt-presets-body">
        <!-- List view -->
        <div id="dt-presets-list-view" class="dt-modal-inner" style="gap:10px">
          <div class="dt-presets-list" id="dt-presets-list"></div>
          <div class="dt-presets-empty" id="dt-presets-empty" style="text-align:center;color:var(--mu);padding:32px 20px;display:none">No presets saved yet.</div>
          <div class="dt-preset-load-hint" id="dt-preset-load-hint" style="display:none;font-size:11px;color:var(--mu);background:var(--am-bg);border:1px solid var(--am-bd);border-radius:6px;padding:8px 12px;line-height:1.5">
            <strong style="color:var(--am)">Load</strong> puts the preset's JS code into the transform editor — it only takes effect once you're in the response intercept modal and click Run or Apply.
          </div>
        </div>
        <!-- Editor view (save / edit) -->
        <div id="dt-preset-editor-view" class="dt-modal-inner" style="display:none;gap:14px">
          <div>
            <div class="dt-flabel" style="margin-bottom:6px">Preset Name</div>
            <input class="dt-preset-name-input" id="dt-pe-name" type="text" placeholder="My Transform" spellcheck="false">
          </div>
          <div>
            <div class="dt-flabel" style="margin-bottom:4px">URL Patterns <span style="font-weight:400;color:var(--mu)">(regex — leave empty to match all)</span></div>
            <div class="dt-pe-patterns-list" id="dt-pe-patterns"></div>
            <button class="dt-pe-add-pattern" id="dt-pe-add-pattern">
              <svg width="11" height="11" viewBox="0 0 11 11" fill="none" stroke="currentColor" stroke-width="1.8"><line x1="5.5" y1="1" x2="5.5" y2="10"/><line x1="1" y1="5.5" x2="10" y2="5.5"/></svg>
              Add pattern
            </button>
          </div>
        </div>
      </div>
      <div class="dt-modal-foot" id="dt-presets-foot-list">
        <button class="dt-foot-btn dt-foot-btn-abort" id="dt-presets-close" style="flex:1">Close</button>
      </div>
      <div class="dt-modal-foot" id="dt-presets-foot-editor" style="display:none">
        <button class="dt-foot-btn dt-foot-btn-abort" id="dt-pe-cancel">Cancel</button>
        <button class="dt-foot-btn dt-foot-btn-send" id="dt-pe-save">Save Preset</button>
      </div>
    </div>
  </div>

  <!-- Mini Save Preset — last in DOM, always on top -->
  <div id="dt-save-preset-overlay" class="dt-overlay dt-mini-overlay">
    <div class="dt-mini-modal">
      <div class="dt-mini-modal-head">
        <svg width="14" height="14" viewBox="0 0 14 14" fill="none" stroke="currentColor" stroke-width="1.6"><rect x="1" y="1" width="12" height="12" rx="1.5"/><rect x="4" y="1" width="6" height="4" rx="0.5"/><rect x="3.5" y="7.5" width="7" height="4.5" rx="0.5"/></svg>
        Save Preset
      </div>
      <div class="dt-mini-modal-body">
        <div class="dt-flabel" style="margin-bottom:5px">Preset Name</div>
        <input class="dt-preset-name-input" id="dt-spe-name" type="text" placeholder="My Transform" spellcheck="false" style="margin-bottom:12px">
        <div class="dt-flabel" style="margin-bottom:4px">URL Patterns <span style="font-weight:400;color:var(--mu)">(regex — empty = match all)</span></div>
        <div class="dt-pe-patterns-list" id="dt-spe-patterns"></div>
        <button class="dt-pe-add-pattern" id="dt-spe-add-pattern">
          <svg width="11" height="11" viewBox="0 0 11 11" fill="none" stroke="currentColor" stroke-width="1.8"><line x1="5.5" y1="1" x2="5.5" y2="10"/><line x1="1" y1="5.5" x2="10" y2="5.5"/></svg>
          Add pattern
        </button>
      </div>
      <div class="dt-modal-foot" style="padding:12px 16px;gap:8px">
        <button class="dt-foot-btn dt-foot-btn-abort" id="dt-spe-cancel">Cancel</button>
        <button class="dt-foot-btn dt-foot-btn-send" id="dt-spe-save">Save</button>
      </div>
    </div>
  </div>
`;