Add collapsible inspector sections with persisted state

This commit is contained in:
Rbanh 2026-02-18 20:36:01 -05:00
parent c02b14649e
commit bf64195c45
3 changed files with 84 additions and 8 deletions

View File

@ -1,6 +1,7 @@
const GRID = 20; const GRID = 20;
const SNAPSHOTS_KEY = "schemeta:snapshots:v2"; const SNAPSHOTS_KEY = "schemeta:snapshots:v2";
const SCHEMA_URL = "/schemeta.schema.json"; const SCHEMA_URL = "/schemeta.schema.json";
const INSPECTOR_SECTIONS_KEY = "schemeta:inspector-sections:v1";
const NET_CLASSES = ["power", "ground", "signal", "analog", "differential", "clock", "bus"]; const NET_CLASSES = ["power", "ground", "signal", "analog", "differential", "clock", "bus"];
const PIN_SIDES = ["left", "right", "top", "bottom"]; const PIN_SIDES = ["left", "right", "top", "bottom"];
const PIN_TYPES = ["power_in", "power_out", "input", "output", "bidirectional", "passive", "analog", "ground"]; const PIN_TYPES = ["power_in", "power_out", "input", "output", "bidirectional", "passive", "analog", "ground"];
@ -53,9 +54,13 @@ const el = {
compileStatus: document.getElementById("compileStatus"), compileStatus: document.getElementById("compileStatus"),
selectedSummary: document.getElementById("selectedSummary"), selectedSummary: document.getElementById("selectedSummary"),
componentEditor: document.getElementById("componentEditor"), componentEditor: document.getElementById("componentEditor"),
componentSection: document.getElementById("componentSection"),
symbolEditor: document.getElementById("symbolEditor"), symbolEditor: document.getElementById("symbolEditor"),
symbolSection: document.getElementById("symbolSection"),
pinEditor: document.getElementById("pinEditor"), pinEditor: document.getElementById("pinEditor"),
pinSection: document.getElementById("pinSection"),
netEditor: document.getElementById("netEditor"), netEditor: document.getElementById("netEditor"),
netSection: document.getElementById("netSection"),
instRefInput: document.getElementById("instRefInput"), instRefInput: document.getElementById("instRefInput"),
instValueInput: document.getElementById("instValueInput"), instValueInput: document.getElementById("instValueInput"),
instNotesInput: document.getElementById("instNotesInput"), instNotesInput: document.getElementById("instNotesInput"),
@ -1653,6 +1658,33 @@ function sortKeysDeep(value) {
return value; return value;
} }
function saveInspectorSectionState() {
const stateObj = {
component: Boolean(el.componentSection?.open),
symbol: Boolean(el.symbolSection?.open),
pin: Boolean(el.pinSection?.open),
net: Boolean(el.netSection?.open)
};
localStorage.setItem(INSPECTOR_SECTIONS_KEY, JSON.stringify(stateObj));
}
function loadInspectorSectionState() {
try {
const raw = localStorage.getItem(INSPECTOR_SECTIONS_KEY);
if (!raw) {
return;
}
const parsed = JSON.parse(raw);
if (typeof parsed !== "object" || !parsed) {
return;
}
if (el.componentSection) el.componentSection.open = parsed.component !== false;
if (el.symbolSection) el.symbolSection.open = parsed.symbol !== false;
if (el.pinSection) el.pinSection.open = parsed.pin !== false;
if (el.netSection) el.netSection.open = parsed.net !== false;
} catch {}
}
async function loadSchemaText() { async function loadSchemaText() {
if (state.schemaText) { if (state.schemaText) {
return state.schemaText; return state.schemaText;
@ -1890,6 +1922,12 @@ function setupEvents() {
}); });
el.instanceList.addEventListener("scroll", renderInstances, { passive: true }); el.instanceList.addEventListener("scroll", renderInstances, { passive: true });
el.netList.addEventListener("scroll", renderNets, { passive: true }); el.netList.addEventListener("scroll", renderNets, { passive: true });
[el.componentSection, el.symbolSection, el.pinSection, el.netSection].forEach((section) => {
if (!section) {
return;
}
section.addEventListener("toggle", saveInspectorSectionState);
});
el.instanceList.addEventListener("click", (evt) => { el.instanceList.addEventListener("click", (evt) => {
const item = evt.target.closest("[data-ref-item]"); const item = evt.target.closest("[data-ref-item]");
@ -2869,6 +2907,7 @@ function setupEvents() {
(async function init() { (async function init() {
setupEvents(); setupEvents();
loadInspectorSectionState();
updateTransform(); updateTransform();
const snapshots = JSON.parse(localStorage.getItem(SNAPSHOTS_KEY) ?? "[]"); const snapshots = JSON.parse(localStorage.getItem(SNAPSHOTS_KEY) ?? "[]");

View File

@ -72,7 +72,9 @@
<section> <section>
<h2>Selected</h2> <h2>Selected</h2>
<div id="selectedSummary" class="card">Click a component, net, or pin to inspect it.</div> <div id="selectedSummary" class="card">Click a component, net, or pin to inspect it.</div>
<div id="componentEditor" class="editorCard hidden"> <details id="componentSection" class="editorSection" open>
<summary>Component</summary>
<div id="componentEditor" class="editorCard hidden">
<div class="editorGrid"> <div class="editorGrid">
<label>Ref <input id="instRefInput" type="text" /></label> <label>Ref <input id="instRefInput" type="text" /></label>
<label>Value <input id="instValueInput" type="text" /></label> <label>Value <input id="instValueInput" type="text" /></label>
@ -96,8 +98,11 @@
<button id="deleteComponentBtn">Delete</button> <button id="deleteComponentBtn">Delete</button>
<button id="isolateSelectedComponentBtn">Isolate</button> <button id="isolateSelectedComponentBtn">Isolate</button>
</div> </div>
</div> </div>
<div id="symbolEditor" class="editorCard hidden"> </details>
<details id="symbolSection" class="editorSection" open>
<summary>Symbol</summary>
<div id="symbolEditor" class="editorCard hidden">
<div id="symbolMeta" class="hintText"></div> <div id="symbolMeta" class="hintText"></div>
<div class="editorGrid"> <div class="editorGrid">
<label>Category <input id="symbolCategoryInput" type="text" /></label> <label>Category <input id="symbolCategoryInput" type="text" /></label>
@ -110,8 +115,11 @@
</div> </div>
<div id="symbolValidation" class="hintText"></div> <div id="symbolValidation" class="hintText"></div>
<div id="symbolPinsList" class="miniList"></div> <div id="symbolPinsList" class="miniList"></div>
</div> </div>
<div id="pinEditor" class="editorCard hidden"> </details>
<details id="pinSection" class="editorSection" open>
<summary>Pin</summary>
<div id="pinEditor" class="editorCard hidden">
<div id="pinMeta" class="hintText"></div> <div id="pinMeta" class="hintText"></div>
<div class="editorGrid"> <div class="editorGrid">
<label>Pin Name <input id="pinNameInput" type="text" /></label> <label>Pin Name <input id="pinNameInput" type="text" /></label>
@ -166,8 +174,11 @@
<button id="createConnectNetBtn">Create + Connect</button> <button id="createConnectNetBtn">Create + Connect</button>
</div> </div>
<div id="pinConnections" class="miniList"></div> <div id="pinConnections" class="miniList"></div>
</div> </div>
<div id="netEditor" class="editorCard hidden"> </details>
<details id="netSection" class="editorSection" open>
<summary>Net</summary>
<div id="netEditor" class="editorCard hidden">
<div class="editorGrid"> <div class="editorGrid">
<label>Name <input id="netNameInput" type="text" /></label> <label>Name <input id="netNameInput" type="text" /></label>
<label>Class <label>Class
@ -194,7 +205,8 @@
<button id="addNetNodeBtn">Add Node</button> <button id="addNetNodeBtn">Add Node</button>
</div> </div>
<div id="netNodesList" class="miniList"></div> <div id="netNodesList" class="miniList"></div>
</div> </div>
</details>
</section> </section>
<section> <section>

View File

@ -218,6 +218,31 @@ textarea {
gap: 8px; gap: 8px;
} }
.editorSection {
border: 1px solid var(--line);
border-radius: 8px;
margin-top: 8px;
background: #fff;
}
.editorSection > summary {
cursor: pointer;
list-style: none;
padding: 8px 10px;
font-size: 0.8rem;
font-weight: 700;
color: var(--ink);
border-bottom: 1px solid transparent;
}
.editorSection[open] > summary {
border-bottom-color: var(--line);
}
.editorSection > summary::-webkit-details-marker {
display: none;
}
.editorGrid { .editorGrid {
display: grid; display: grid;
grid-template-columns: 1fr 1fr; grid-template-columns: 1fr 1fr;