Add inspector quick actions for duplicate/delete/isolate
This commit is contained in:
parent
f938c6dcbc
commit
b7ba158880
@ -63,6 +63,9 @@ const el = {
|
||||
lockedInput: document.getElementById("lockedInput"),
|
||||
rotateSelectedBtn: document.getElementById("rotateSelectedBtn"),
|
||||
updatePlacementBtn: document.getElementById("updatePlacementBtn"),
|
||||
duplicateComponentBtn: document.getElementById("duplicateComponentBtn"),
|
||||
deleteComponentBtn: document.getElementById("deleteComponentBtn"),
|
||||
isolateSelectedComponentBtn: document.getElementById("isolateSelectedComponentBtn"),
|
||||
symbolMeta: document.getElementById("symbolMeta"),
|
||||
symbolCategoryInput: document.getElementById("symbolCategoryInput"),
|
||||
symbolWidthInput: document.getElementById("symbolWidthInput"),
|
||||
@ -87,6 +90,7 @@ const el = {
|
||||
netNameInput: document.getElementById("netNameInput"),
|
||||
netClassInput: document.getElementById("netClassInput"),
|
||||
updateNetBtn: document.getElementById("updateNetBtn"),
|
||||
isolateSelectedNetBtn: document.getElementById("isolateSelectedNetBtn"),
|
||||
netNodeRefInput: document.getElementById("netNodeRefInput"),
|
||||
netNodePinInput: document.getElementById("netNodePinInput"),
|
||||
addNetNodeBtn: document.getElementById("addNetNodeBtn"),
|
||||
@ -261,6 +265,19 @@ function normalizeNetName(raw) {
|
||||
.replace(/\s+/g, "_");
|
||||
}
|
||||
|
||||
function nextRefLike(baseRef) {
|
||||
const base = normalizeRef(baseRef || "X1");
|
||||
const m = /^([A-Za-z_]+)(\d+)?$/i.exec(base);
|
||||
const prefix = m?.[1] ?? `${base}_`;
|
||||
let n = Number(m?.[2] ?? 1);
|
||||
let candidate = `${prefix}${n}`;
|
||||
while (instanceByRef(candidate)) {
|
||||
n += 1;
|
||||
candidate = `${prefix}${n}`;
|
||||
}
|
||||
return candidate;
|
||||
}
|
||||
|
||||
function escHtml(text) {
|
||||
return String(text ?? "")
|
||||
.replaceAll("&", "&")
|
||||
@ -890,6 +907,11 @@ function renderNetEditor() {
|
||||
}
|
||||
|
||||
function renderSelected() {
|
||||
el.duplicateComponentBtn.disabled = true;
|
||||
el.deleteComponentBtn.disabled = true;
|
||||
el.isolateSelectedComponentBtn.disabled = true;
|
||||
el.isolateSelectedNetBtn.disabled = true;
|
||||
|
||||
if (!state.model) {
|
||||
el.selectedSummary.textContent = "Click a component, net, or pin to inspect it.";
|
||||
el.componentEditor.classList.add("hidden");
|
||||
@ -931,6 +953,9 @@ function renderSelected() {
|
||||
el.instRefInput.value = inst.ref;
|
||||
el.instValueInput.value = String(inst.properties?.value ?? "");
|
||||
el.instNotesInput.value = String(inst.properties?.notes ?? "");
|
||||
el.duplicateComponentBtn.disabled = false;
|
||||
el.deleteComponentBtn.disabled = false;
|
||||
el.isolateSelectedComponentBtn.disabled = false;
|
||||
renderSymbolEditorForRef(inst.ref);
|
||||
el.pinEditor.classList.add("hidden");
|
||||
el.netEditor.classList.add("hidden");
|
||||
@ -941,6 +966,7 @@ function renderSelected() {
|
||||
const net = state.model.nets.find((n) => n.name === state.selectedNet);
|
||||
if (net) {
|
||||
el.selectedSummary.textContent = `${net.name} (${net.class})\nNodes: ${net.nodes.map((n) => `${n.ref}.${n.pin}`).join(", ")}`;
|
||||
el.isolateSelectedNetBtn.disabled = false;
|
||||
el.componentEditor.classList.add("hidden");
|
||||
el.symbolEditor.classList.add("hidden");
|
||||
el.pinEditor.classList.add("hidden");
|
||||
@ -1915,6 +1941,63 @@ function setupEvents() {
|
||||
queueCompile(true, "rotate");
|
||||
});
|
||||
|
||||
el.duplicateComponentBtn.addEventListener("click", () => {
|
||||
if (state.selectedRefs.length !== 1 || !state.selectedRef || !state.model) {
|
||||
return;
|
||||
}
|
||||
const inst = instanceByRef(state.selectedRef);
|
||||
if (!inst) {
|
||||
return;
|
||||
}
|
||||
pushHistory("duplicate-component");
|
||||
const nextRef = nextRefLike(inst.ref);
|
||||
const next = clone(inst);
|
||||
next.ref = nextRef;
|
||||
next.placement = {
|
||||
...next.placement,
|
||||
x: toGrid(Number(next.placement.x ?? 0) + GRID * 2),
|
||||
y: toGrid(Number(next.placement.y ?? 0) + GRID * 2),
|
||||
locked: false
|
||||
};
|
||||
state.model.instances.push(next);
|
||||
selectSingleRef(nextRef);
|
||||
state.selectedNet = null;
|
||||
state.selectedPin = null;
|
||||
state.isolateNet = false;
|
||||
el.jsonFeedback.textContent = `Duplicated ${inst.ref} as ${nextRef}.`;
|
||||
queueCompile(true, "duplicate-component");
|
||||
});
|
||||
|
||||
el.deleteComponentBtn.addEventListener("click", () => {
|
||||
if (state.selectedRefs.length !== 1 || !state.selectedRef || !state.model) {
|
||||
return;
|
||||
}
|
||||
const ref = state.selectedRef;
|
||||
pushHistory("delete-component");
|
||||
state.model.instances = state.model.instances.filter((i) => i.ref !== ref);
|
||||
for (const net of state.model.nets ?? []) {
|
||||
net.nodes = (net.nodes ?? []).filter((n) => n.ref !== ref);
|
||||
}
|
||||
state.model.nets = (state.model.nets ?? []).filter((n) => (n.nodes ?? []).length >= 2);
|
||||
setSelectedRefs([]);
|
||||
state.selectedNet = null;
|
||||
state.selectedPin = null;
|
||||
state.isolateComponent = false;
|
||||
state.isolateNet = false;
|
||||
el.jsonFeedback.textContent = `Deleted component ${ref}.`;
|
||||
queueCompile(true, "delete-component");
|
||||
});
|
||||
|
||||
el.isolateSelectedComponentBtn.addEventListener("click", () => {
|
||||
if (!state.selectedRef) {
|
||||
return;
|
||||
}
|
||||
state.isolateComponent = true;
|
||||
state.isolateNet = false;
|
||||
state.selectedNet = null;
|
||||
renderAll();
|
||||
});
|
||||
|
||||
el.showPinNetLabelInput.addEventListener("change", () => {
|
||||
if (!state.selectedPin) {
|
||||
return;
|
||||
@ -2054,6 +2137,17 @@ function setupEvents() {
|
||||
queueCompile(true, "net-edit");
|
||||
});
|
||||
|
||||
el.isolateSelectedNetBtn.addEventListener("click", () => {
|
||||
if (!state.selectedNet) {
|
||||
return;
|
||||
}
|
||||
state.isolateNet = true;
|
||||
state.isolateComponent = false;
|
||||
setSelectedRefs([]);
|
||||
state.selectedPin = null;
|
||||
renderAll();
|
||||
});
|
||||
|
||||
el.addNetNodeBtn.addEventListener("click", () => {
|
||||
if (!state.selectedNet) {
|
||||
return;
|
||||
|
||||
@ -92,6 +92,9 @@
|
||||
<div class="editorActions">
|
||||
<button id="rotateSelectedBtn">Rotate +90</button>
|
||||
<button id="updatePlacementBtn">Apply Component</button>
|
||||
<button id="duplicateComponentBtn">Duplicate</button>
|
||||
<button id="deleteComponentBtn">Delete</button>
|
||||
<button id="isolateSelectedComponentBtn">Isolate</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="symbolEditor" class="editorCard hidden">
|
||||
@ -180,6 +183,7 @@
|
||||
</div>
|
||||
<div class="editorActions">
|
||||
<button id="updateNetBtn">Apply Net</button>
|
||||
<button id="isolateSelectedNetBtn">Isolate</button>
|
||||
</div>
|
||||
<div class="editorGrid">
|
||||
<label>Node Ref <input id="netNodeRefInput" type="text" placeholder="U1" /></label>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user