From 0ba4fef55eec53fab7ea798c41a7dfdc0dbdf43a Mon Sep 17 00:00:00 2001 From: yuanzhen869 Date: Tue, 14 Apr 2026 09:31:08 +0800 Subject: [PATCH] Refine device query guidance layout --- web/device_query.html | 1076 +++++++++++++++++++++++++++++++---------- 1 file changed, 824 insertions(+), 252 deletions(-) diff --git a/web/device_query.html b/web/device_query.html index 7971e1c..2104c99 100644 --- a/web/device_query.html +++ b/web/device_query.html @@ -14,23 +14,41 @@ --brand: #0f6fff; --ok: #0a7f3f; --warn: #b16a00; + --nav-height: 52px; } * { box-sizing: border-box; } body { margin: 0; + padding-top: var(--nav-height); font-family: "PingFang SC", "Noto Sans SC", "Microsoft YaHei", sans-serif; background: radial-gradient(circle at 0 0, #eef4ff 0, var(--bg) 40%), var(--bg); color: var(--text); } + body.docs-mode { + padding-top: var(--nav-height); + } .top-nav { + position: fixed; + top: 0; + left: 0; + right: 0; + z-index: 1000; background: linear-gradient(180deg, #1f2a3a, #1a2431); border-bottom: 1px solid rgba(255, 255, 255, 0.08); + box-shadow: 0 10px 30px rgba(14, 25, 42, 0.16); + } + body.docs-mode .top-nav { + position: fixed !important; + top: 0; + left: 0; + right: 0; + z-index: 1000; } .top-nav-inner { max-width: 1200px; margin: 0 auto; padding: 0 16px; - height: 52px; + height: var(--nav-height); display: flex; align-items: center; gap: 8px; @@ -66,9 +84,48 @@ margin: 24px auto; padding: 0 16px 32px; display: grid; - grid-template-columns: 360px 1fr; + grid-template-columns: 320px 1fr; gap: 16px; } + .query-wrap { + align-items: start; + } + .query-sidebar { + position: sticky; + top: calc(var(--nav-height) + 24px); + display: grid; + gap: 12px; + } + .query-main { + min-width: 0; + } + .sidebar-card { + padding: 16px; + border-color: #d4def1; + box-shadow: + 0 16px 32px rgba(26, 42, 78, 0.07), + inset 0 1px 0 rgba(255, 255, 255, 0.8); + } + .sidebar-card .title { + font-size: 15px; + color: #173053; + } + .sidebar-card .sub { + margin-bottom: 0; + } + .sidebar-card .helper-box { + margin-top: 0; + } + .compact-note-list { + margin: 0; + padding-left: 18px; + color: var(--sub); + font-size: 13px; + line-height: 1.65; + } + .compact-note-list li + li { + margin-top: 4px; + } .page-tabs-wrap { max-width: 1200px; margin: 18px auto 0; @@ -105,6 +162,156 @@ padding: 14px; box-shadow: 0 6px 18px rgba(36, 56, 89, 0.06); } + .search-card { + position: relative; + overflow: hidden; + padding: 16px; + border-color: #cfdaf0; + background: + linear-gradient(180deg, rgba(255, 255, 255, 0.98), rgba(247, 250, 255, 0.98)), + #fff; + box-shadow: + 0 20px 40px rgba(26, 42, 78, 0.08), + inset 0 1px 0 rgba(255, 255, 255, 0.75); + } + .search-card .title { + font-size: 18px; + color: #16263f; + } + .search-card .sub { + margin-bottom: 4px; + color: #53627a; + } + .search-layout { + display: grid; + grid-template-columns: minmax(420px, 1.08fr) minmax(0, 0.92fr); + gap: 8px 18px; + align-items: stretch; + } + .search-layout-single { + display: grid; + gap: 8px; + } + .search-layout-single .search-copy { + grid-column: auto; + } + .search-copy { + grid-column: 1 / -1; + } + .search-copy .sub:last-child { + margin-bottom: 0; + } + .search-header-row { + grid-column: 1 / -1; + display: grid; + grid-template-columns: inherit; + gap: 18px; + align-items: end; + } + .search-field-label { + margin: 0; + font-size: 13px; + color: #324056; + font-weight: 600; + line-height: 1.2; + } + .search-main-fields, + .search-side-fields { + min-width: 0; + display: grid; + gap: 6px; + align-content: stretch; + align-self: start; + } + .search-inline-row { + display: grid; + grid-template-columns: minmax(220px, 1fr) 76px; + gap: 10px; + align-items: end; + justify-content: stretch; + min-height: 44px; + } + .search-layout-single .search-inline-row { + grid-template-columns: minmax(0, 1fr) 76px; + } + .search-input-stack { + display: grid; + gap: 4px; + } + .search-input-stack label, + .search-input-stack .search-field-label { + margin: 0; + } + .search-inline-row .btns { + justify-content: flex-end; + } + .search-card .helper-box { + background: linear-gradient(180deg, #fbfdff, #f4f8ff); + border-color: #d9e4f8; + } + .search-card input { + min-height: 44px; + padding: 10px 12px; + border-radius: 14px; + border-color: #cbd8ee; + background: rgba(255, 255, 255, 0.96); + box-shadow: inset 0 1px 2px rgba(25, 36, 61, 0.04); + } + .search-card input:focus { + outline: none; + border-color: #0f6fff; + box-shadow: 0 0 0 4px rgba(15, 111, 255, 0.12); + } + .search-card .platform-picker { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 8px; + margin-top: 0; + padding: 3px; + height: 44px; + border: 1px solid #dbe5f5; + border-radius: 12px; + background: #fdfefe; + align-items: stretch; + } + .search-card .platform-card { + min-height: 34px; + height: 34px; + border-radius: 9px; + border-color: transparent; + background: transparent; + box-shadow: none; + gap: 8px; + padding: 0 9px; + flex-direction: row; + justify-content: center; + } + .search-card .platform-card.active { + border-color: #c6d8fb; + background: #edf4ff; + box-shadow: + 0 4px 10px rgba(15, 111, 255, 0.1), + inset 0 0 0 1px rgba(15, 111, 255, 0.1); + } + .search-card .btns { + margin-top: 0; + display: flex; + align-items: stretch; + height: 44px; + } + .search-card .btns .primary { + min-height: 44px; + height: 44px; + width: 76px; + min-width: 76px; + padding: 0 12px; + border-radius: 14px; + font-size: 14px; + line-height: 44px; + letter-spacing: 0.01em; + box-shadow: 0 10px 20px rgba(15, 111, 255, 0.18); + white-space: nowrap; + } .title { margin: 0 0 8px; font-size: 16px; @@ -208,6 +415,12 @@ } @media (max-width: 640px) { .credential-grid { grid-template-columns: 1fr; } + .search-card { + padding: 16px; + } + .search-card .platform-picker { + grid-template-columns: 1fr; + } } code { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; @@ -255,20 +468,22 @@ color: #15437d; } .platform-icon { - width: 58px; - height: 58px; + width: 22px; + height: 22px; display: flex; align-items: center; justify-content: center; + flex: 0 0 auto; } .platform-icon svg { - width: 34px; - height: 34px; + width: 20px; + height: 20px; display: block; } .platform-name { - font-size: 13px; + font-size: 12px; font-weight: 700; + white-space: nowrap; } .pill { display: inline-block; @@ -330,6 +545,64 @@ } .mono { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; } details summary { cursor: pointer; color: #294f88; } + .collapse-card { + padding: 0; + overflow: hidden; + } + .collapse-card summary { + list-style: none; + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + padding: 14px 16px; + font-weight: 700; + color: #1f355a; + } + .collapse-card summary::-webkit-details-marker { + display: none; + } + .collapse-card summary::after { + content: "展开"; + flex: 0 0 auto; + border: 1px solid #c8d6ee; + border-radius: 999px; + padding: 3px 10px; + font-size: 12px; + font-weight: 600; + color: #47648f; + background: #f6f9ff; + } + .collapse-card[open] summary::after { + content: "收起"; + } + .collapse-card[open] summary { + border-bottom: 1px solid var(--line); + background: #fbfcff; + } + .collapse-body { + padding: 14px; + display: grid; + gap: 14px; + } + .collapse-body .credential-grid { + margin-top: 0; + } + .collapse-section { + display: grid; + gap: 8px; + } + .section-label { + margin: 0; + color: #203554; + font-size: 13px; + font-weight: 700; + line-height: 1.4; + } + .collapse-section .title { + margin-bottom: 0; + font-size: 14px; + } .tag { display: inline-block; margin: 2px 6px 2px 0; @@ -424,8 +697,21 @@ } @media (max-width: 1020px) { .wrap { grid-template-columns: 1fr; } + .query-sidebar { + position: static; + } + .search-layout { + grid-template-columns: 1fr; + } + .search-header-row { + grid-template-columns: 1fr; + gap: 8px; + } } @media (max-width: 640px) { + .search-inline-row { + grid-template-columns: 1fr; + } .platform-picker { grid-template-columns: 1fr; } } @@ -442,93 +728,104 @@
- - + + +
-
-
-

SQL 查询

-

当前模式直接查询 MySQL 主表 `mobilemodels.mm_device_catalog`,结果与线上 SQL 接入保持一致。

+
+ - - -
- - + - -
- - - - -
- -
-
- -
+ + + iOS + + +
+
+
+
+
+

设备标识 model_raw

+ +
+
+ +
+
+
+ +
-

SQL 查询结果

-
输入设备标识后开始查询 MySQL。
+

设备标识结果

+
输入设备标识 model_raw 后开始查询。
@@ -674,15 +1038,103 @@
+
+ 调试信息 +
+
+

执行 SQL

+
-- 暂无 SQL
+
+
+

返回 JSON

+
{}
+
+
+
+ + + + + @@ -691,7 +1143,7 @@

相关文档

-

这里统一整理页面调试、MySQL 接入和兼容查询相关说明,直接在当前页面查看,不再跳转到单独页面。

+

这里保留完整文档阅读;查询约定已经前移到各个查询页的使用提示里。

-
-
-

主推查询表

-
mobilemodels.mm_device_catalog
-
-
-

兼容视图

-
mobilemodels.mm_device_lookup
-mobilemodels.mm_device_record
-mobilemodels.models
-python_services_test.models
-
-
-

归一化规则

-
全部转小写
-只保留 [0-9a-z中文]
-去掉空格、横线、下划线和其他标点
-
-SM-G9980  -> smg9980
-iPhone14,2 -> iphone142
-NOH-AL00  -> nohal00
-
-
- -
-
-

主推 SQL

-
SELECT
-  model,
-  record_id,
-  alias_norm,
-  device_name,
-  brand,
-  manufacturer_brand,
-  parent_brand,
-  market_brand,
-  device_type,
-  source_file,
-  section,
-  source_rank,
-  source_weight,
-  code,
-  code_alias,
-  ver_name
-FROM mobilemodels.mm_device_catalog
-WHERE alias_norm = ?
-ORDER BY source_rank ASC, record_id ASC
-LIMIT 20;
-
-
-

兼容旧结构 SQL

-
SELECT
-  model,
-  dtype,
-  brand,
-  brand_title,
-  code,
-  code_alias,
-  model_name,
-  ver_name
-FROM python_services_test.models
-WHERE model = ?
-LIMIT 20;
-
-
@@ -804,11 +1191,17 @@ LIMIT 20; const sqlStatementEl = document.getElementById("sqlStatement"); const sqlJsonOutputEl = document.getElementById("sqlJsonOutput"); const sqlReadonlyInfoEl = document.getElementById("sqlReadonlyInfo"); + const deviceNameSqlSummaryEl = document.getElementById("deviceNameSqlSummary"); + const deviceNameSqlResultBodyEl = document.getElementById("deviceNameSqlResultBody"); + const deviceNameSqlStatementEl = document.getElementById("deviceNameSqlStatement"); + const deviceNameSqlJsonOutputEl = document.getElementById("deviceNameSqlJsonOutput"); + const deviceNameSqlReadonlyInfoEl = document.getElementById("deviceNameSqlReadonlyInfo"); const docPanelTitleEl = document.getElementById("docPanelTitle"); const docPanelPathEl = document.getElementById("docPanelPath"); const docPanelContentEl = document.getElementById("docPanelContent"); const indexQueryPanelEl = document.getElementById("indexQueryPanel"); const sqlQueryPanelEl = document.getElementById("sqlQueryPanel"); + const deviceNameSqlQueryPanelEl = document.getElementById("deviceNameSqlQueryPanel"); const docsPanelEl = document.getElementById("docsPanel"); const topNavQueryEl = document.getElementById("topNavQuery"); const topNavDocsEl = document.getElementById("topNavDocs"); @@ -844,6 +1237,102 @@ LIMIT 20; return out; } + function parseAppleSeriesGeneration(name) { + const text = String(name || "").trim().replace(/\s+/g, " "); + if (!text) { + return null; + } + + const ordinalMatch = text.match(/^(.*?)(?:\s*\((\d+)(?:st|nd|rd|th)\s+generation\)|\s+(\d+))$/i); + if (ordinalMatch) { + const baseLabel = String(ordinalMatch[1] || "").trim().replace(/\s+/g, " "); + const generation = Number(ordinalMatch[2] || ordinalMatch[3] || 0); + if (baseLabel && generation > 0) { + return { + baseLabel, + baseNorm: normalizeText(baseLabel), + generation, + chipLike: false, + }; + } + } + + const chipLike = /\((?:[^)]*\b(?:a\d{1,2}|m\d{1,2})\b[^)]*)\)$/i.test(text); + const baseLabel = text.replace(/\s*\([^)]*\)\s*$/g, "").trim().replace(/\s+/g, " "); + if (!baseLabel) { + return null; + } + + return { + baseLabel, + baseNorm: normalizeText(baseLabel), + generation: null, + chipLike, + }; + } + + function buildSyntheticAppleGenerationAliases(records) { + const groups = new Map(); + const aliasesByRecordId = new Map(); + + for (const record of records || []) { + const brand = record.market_brand || record.manufacturer_brand || record.brand || ""; + if (brand !== "Apple") { + continue; + } + + const parsed = parseAppleSeriesGeneration(record.device_name); + if (!parsed || !parsed.baseNorm) { + continue; + } + + if (!groups.has(parsed.baseNorm)) { + groups.set(parsed.baseNorm, { + baseLabel: parsed.baseLabel, + items: [], + }); + } + + groups.get(parsed.baseNorm).items.push({ + id: record.id, + deviceName: record.device_name, + baseLabel: parsed.baseLabel, + generation: parsed.generation, + chipLike: parsed.chipLike, + }); + } + + for (const group of groups.values()) { + const explicitGenerations = [...new Set( + group.items + .map((item) => item.generation) + .filter((value) => Number.isInteger(value) && value > 0) + )].sort((a, b) => a - b); + const maxExplicitGeneration = explicitGenerations.length + ? explicitGenerations[explicitGenerations.length - 1] + : 0; + + for (const item of group.items) { + let generation = item.generation; + if (!generation && item.deviceName === group.baseLabel && maxExplicitGeneration >= 2) { + generation = 1; + } else if (!generation && item.chipLike && maxExplicitGeneration >= 1) { + generation = maxExplicitGeneration + 1; + } + + if (!generation || generation <= 0) { + continue; + } + + const syntheticAliases = aliasesByRecordId.get(item.id) || []; + syntheticAliases.push(`${group.baseLabel} ${generation}`); + aliasesByRecordId.set(item.id, syntheticAliases); + } + } + + return aliasesByRecordId; + } + function sanitizeManagedBrandConfig(rawConfig) { const manufacturersMap = new Map(); const brandsMap = new Map(); @@ -1139,7 +1628,23 @@ LIMIT 20; function applyManagedBrandConfigToRecords() { recordById = new Map(); + if (!indexData.lookup || typeof indexData.lookup !== "object") { + indexData.lookup = {}; + } + const syntheticAliasesByRecordId = buildSyntheticAppleGenerationAliases(indexData.records || []); for (const r of indexData.records || []) { + const extraAliases = syntheticAliasesByRecordId.get(r.id) || []; + r.aliases = normalizeAliasList(r.device_name, [...(Array.isArray(r.aliases) ? r.aliases : []), ...extraAliases]); + for (const alias of extraAliases) { + const key = normalizeText(alias); + if (!key) continue; + const ids = Array.isArray(indexData.lookup[key]) ? indexData.lookup[key] : []; + if (!ids.includes(r.id)) { + ids.push(r.id); + } + indexData.lookup[key] = ids; + } + const mappedBrand = r.market_brand || r.manufacturer_brand || r.brand; const mappedManufacturer = managedBrandToManufacturer.get(mappedBrand) @@ -1523,6 +2028,7 @@ LIMIT 20; }); indexQueryPanelEl.classList.toggle("hidden", tabName !== "index-query"); sqlQueryPanelEl.classList.toggle("hidden", tabName !== "sql-query"); + deviceNameSqlQueryPanelEl.classList.toggle("hidden", tabName !== "device-name-sql-query"); try { localStorage.setItem(QUERY_PAGE_TAB_STORAGE_KEY, tabName); } catch { @@ -1534,11 +2040,13 @@ LIMIT 20; const params = new URLSearchParams(window.location.search); const view = params.get("view") || ""; const docsMode = view === "docs"; + document.body.classList.toggle("docs-mode", docsMode); docsPanelEl.classList.toggle("hidden", !docsMode); document.querySelector(".page-tabs-wrap").classList.toggle("hidden", docsMode); if (docsMode) { indexQueryPanelEl.classList.add("hidden"); sqlQueryPanelEl.classList.add("hidden"); + deviceNameSqlQueryPanelEl.classList.add("hidden"); topNavQueryEl.classList.remove("active"); topNavDocsEl.classList.add("active"); return; @@ -1551,6 +2059,7 @@ LIMIT 20; } catch { topNavQueryEl.classList.add("active"); topNavDocsEl.classList.remove("active"); + document.body.classList.remove("docs-mode"); docsPanelEl.classList.add("hidden"); activateTopTab("sql-query"); } @@ -1618,12 +2127,14 @@ LIMIT 20; ["User", data.mysql_reader_user || "-"], ["Password", data.mysql_reader_password || "-"], ]; - sqlReadonlyInfoEl.innerHTML = items.map(([label, value]) => ` + const html = items.map(([label, value]) => `
${escapeHtml(label)}

${escapeHtml(value)}

`).join(""); + sqlReadonlyInfoEl.innerHTML = html; + deviceNameSqlReadonlyInfoEl.innerHTML = html; } async function loadReadonlyInfo() { @@ -1631,12 +2142,14 @@ LIMIT 20; const data = await fetchJson("/api/status", { cache: "no-store" }); renderReadonlyInfo(data); } catch (err) { - sqlReadonlyInfoEl.innerHTML = ` + const html = `
只读连接参数

暂时无法读取连接参数

`; + sqlReadonlyInfoEl.innerHTML = html; + deviceNameSqlReadonlyInfoEl.innerHTML = html; } } @@ -1675,6 +2188,7 @@ LIMIT 20; reportModelRaw: document.getElementById("reportModelRaw").value, sqlPlatform: document.getElementById("sqlPlatform").value, sqlModelRaw: document.getElementById("sqlModelRaw").value, + deviceNameSqlInput: document.getElementById("deviceNameSqlInput").value, }; } @@ -1689,6 +2203,7 @@ LIMIT 20; setValue("reportModelRaw", state.reportModelRaw); setValue("sqlPlatform", state.sqlPlatform); setValue("sqlModelRaw", state.sqlModelRaw); + setValue("deviceNameSqlInput", state.deviceNameSqlInput); } function saveQueryFormState() { @@ -1714,6 +2229,7 @@ LIMIT 20; "reportModelRaw", "sqlPlatform", "sqlModelRaw", + "deviceNameSqlInput", ]; for (const id of ids) { const el = document.getElementById(id); @@ -1762,30 +2278,50 @@ LIMIT 20; e.preventDefault(); document.getElementById("sqlQueryBtn").click(); }); + document.getElementById("deviceNameSqlInput").addEventListener("keydown", (e) => { + if (e.key !== "Enter") return; + e.preventDefault(); + document.getElementById("deviceNameSqlQueryBtn").click(); + }); + + function renderSqlResult(output, options = {}) { + const summaryEl = options.summaryEl || sqlSummaryEl; + const statementEl = options.statementEl || sqlStatementEl; + const resultBodyEl = options.resultBodyEl || sqlResultBodyEl; + const jsonOutputEl = options.jsonOutputEl || sqlJsonOutputEl; + const strategyLabel = options.defaultStrategyLabel || "alias_norm 精确匹配"; + const description = options.summaryDescription || "当前模式直接查询 MySQL 主表 mm_device_catalog。"; - function renderSqlResult(output) { if (output.error) { - sqlSummaryEl.textContent = output.error; - sqlStatementEl.textContent = "-- SQL 执行失败"; - sqlResultBodyEl.innerHTML = `${escapeHtml(output.error)}`; - sqlJsonOutputEl.textContent = JSON.stringify(output, null, 2); + summaryEl.textContent = output.error; + statementEl.textContent = "-- SQL 执行失败"; + resultBodyEl.innerHTML = `${escapeHtml(output.error)}`; + jsonOutputEl.textContent = JSON.stringify(output, null, 2); return; } - sqlSummaryEl.innerHTML = ` + summaryEl.innerHTML = `
查询键: ${escapeHtml(output.alias_norm || "-")} 命中: ${Number(output.row_count || 0)} limit: ${Number(output.limit || 0)} + 命中方式: ${escapeHtml(output.match_strategy_label || strategyLabel)}
-

当前模式直接查询 MySQL 主表 mm_device_catalog

+

${description}

+
+

登录设备管理字段建议

+

推荐取 device_name / market_brand(为空退回 brand)/ parent_brand(为空退回 manufacturer_brand)/ device_type

+
+ ${Array.isArray(output.resolved_device_names) && output.resolved_device_names.length + ? `
设备名别名映射: ${escapeHtml(output.resolved_device_names.join(" | "))}
` + : ""} `; - sqlStatementEl.textContent = output.sql || "-- 暂无 SQL"; + statementEl.textContent = output.sql || "-- 暂无 SQL"; if (!Array.isArray(output.rows) || !output.rows.length) { - sqlResultBodyEl.innerHTML = `没有查到结果。`; + resultBodyEl.innerHTML = `没有查到结果。`; } else { - sqlResultBodyEl.innerHTML = output.rows.map((row, idx) => ` + resultBodyEl.innerHTML = output.rows.map((row, idx) => ` ${idx + 1} @@ -1795,7 +2331,7 @@ LIMIT 20; ${escapeHtml(row.device_name || "-")}
品牌: ${escapeHtml(row.market_brand || row.brand || "-")}
-
厂商: ${escapeHtml(row.manufacturer_brand || "-")}
+
厂商: ${escapeHtml(row.parent_brand || row.manufacturer_brand || "-")}
${escapeHtml(row.device_type || "-")} @@ -1806,7 +2342,7 @@ LIMIT 20; `).join(""); } - sqlJsonOutputEl.textContent = JSON.stringify(output, null, 2); + jsonOutputEl.textContent = JSON.stringify(output, null, 2); } document.getElementById("queryBtn").addEventListener("click", () => { @@ -1854,11 +2390,47 @@ LIMIT 20; } }); + document.getElementById("deviceNameSqlQueryBtn").addEventListener("click", async () => { + const deviceName = document.getElementById("deviceNameSqlInput").value.trim(); + + deviceNameSqlSummaryEl.textContent = "正在查询 MySQL,请稍候。"; + deviceNameSqlStatementEl.textContent = "-- 查询进行中..."; + deviceNameSqlResultBodyEl.innerHTML = `查询进行中...`; + + try { + const output = await fetchJson("/api/query-sql-device-name", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + device_name: deviceName, + limit: 20, + }), + }); + renderSqlResult(output, { + summaryEl: deviceNameSqlSummaryEl, + statementEl: deviceNameSqlStatementEl, + resultBodyEl: deviceNameSqlResultBodyEl, + jsonOutputEl: deviceNameSqlJsonOutputEl, + defaultStrategyLabel: "device_name 模糊匹配", + summaryDescription: "当前模式仅支持设备名称搜索,直接查询 MySQL 主表 mm_device_catalog。", + }); + saveQueryFormState(); + } catch (err) { + renderSqlResult({ error: err.message || String(err) }, { + summaryEl: deviceNameSqlSummaryEl, + statementEl: deviceNameSqlStatementEl, + resultBodyEl: deviceNameSqlResultBodyEl, + jsonOutputEl: deviceNameSqlJsonOutputEl, + }); + } + }); + document.querySelectorAll(".page-tab").forEach((el) => { el.addEventListener("click", () => { const url = new URL(window.location.href); url.searchParams.delete("view"); window.history.replaceState({}, "", url.toString()); + document.body.classList.remove("docs-mode"); docsPanelEl.classList.add("hidden"); document.querySelector(".page-tabs-wrap").classList.remove("hidden"); topNavQueryEl.classList.add("active");