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"),
|
lockedInput: document.getElementById("lockedInput"),
|
||||||
rotateSelectedBtn: document.getElementById("rotateSelectedBtn"),
|
rotateSelectedBtn: document.getElementById("rotateSelectedBtn"),
|
||||||
updatePlacementBtn: document.getElementById("updatePlacementBtn"),
|
updatePlacementBtn: document.getElementById("updatePlacementBtn"),
|
||||||
|
duplicateComponentBtn: document.getElementById("duplicateComponentBtn"),
|
||||||
|
deleteComponentBtn: document.getElementById("deleteComponentBtn"),
|
||||||
|
isolateSelectedComponentBtn: document.getElementById("isolateSelectedComponentBtn"),
|
||||||
symbolMeta: document.getElementById("symbolMeta"),
|
symbolMeta: document.getElementById("symbolMeta"),
|
||||||
symbolCategoryInput: document.getElementById("symbolCategoryInput"),
|
symbolCategoryInput: document.getElementById("symbolCategoryInput"),
|
||||||
symbolWidthInput: document.getElementById("symbolWidthInput"),
|
symbolWidthInput: document.getElementById("symbolWidthInput"),
|
||||||
@ -87,6 +90,7 @@ const el = {
|
|||||||
netNameInput: document.getElementById("netNameInput"),
|
netNameInput: document.getElementById("netNameInput"),
|
||||||
netClassInput: document.getElementById("netClassInput"),
|
netClassInput: document.getElementById("netClassInput"),
|
||||||
updateNetBtn: document.getElementById("updateNetBtn"),
|
updateNetBtn: document.getElementById("updateNetBtn"),
|
||||||
|
isolateSelectedNetBtn: document.getElementById("isolateSelectedNetBtn"),
|
||||||
netNodeRefInput: document.getElementById("netNodeRefInput"),
|
netNodeRefInput: document.getElementById("netNodeRefInput"),
|
||||||
netNodePinInput: document.getElementById("netNodePinInput"),
|
netNodePinInput: document.getElementById("netNodePinInput"),
|
||||||
addNetNodeBtn: document.getElementById("addNetNodeBtn"),
|
addNetNodeBtn: document.getElementById("addNetNodeBtn"),
|
||||||
@ -261,6 +265,19 @@ function normalizeNetName(raw) {
|
|||||||
.replace(/\s+/g, "_");
|
.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) {
|
function escHtml(text) {
|
||||||
return String(text ?? "")
|
return String(text ?? "")
|
||||||
.replaceAll("&", "&")
|
.replaceAll("&", "&")
|
||||||
@ -890,6 +907,11 @@ function renderNetEditor() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function renderSelected() {
|
function renderSelected() {
|
||||||
|
el.duplicateComponentBtn.disabled = true;
|
||||||
|
el.deleteComponentBtn.disabled = true;
|
||||||
|
el.isolateSelectedComponentBtn.disabled = true;
|
||||||
|
el.isolateSelectedNetBtn.disabled = true;
|
||||||
|
|
||||||
if (!state.model) {
|
if (!state.model) {
|
||||||
el.selectedSummary.textContent = "Click a component, net, or pin to inspect it.";
|
el.selectedSummary.textContent = "Click a component, net, or pin to inspect it.";
|
||||||
el.componentEditor.classList.add("hidden");
|
el.componentEditor.classList.add("hidden");
|
||||||
@ -931,6 +953,9 @@ function renderSelected() {
|
|||||||
el.instRefInput.value = inst.ref;
|
el.instRefInput.value = inst.ref;
|
||||||
el.instValueInput.value = String(inst.properties?.value ?? "");
|
el.instValueInput.value = String(inst.properties?.value ?? "");
|
||||||
el.instNotesInput.value = String(inst.properties?.notes ?? "");
|
el.instNotesInput.value = String(inst.properties?.notes ?? "");
|
||||||
|
el.duplicateComponentBtn.disabled = false;
|
||||||
|
el.deleteComponentBtn.disabled = false;
|
||||||
|
el.isolateSelectedComponentBtn.disabled = false;
|
||||||
renderSymbolEditorForRef(inst.ref);
|
renderSymbolEditorForRef(inst.ref);
|
||||||
el.pinEditor.classList.add("hidden");
|
el.pinEditor.classList.add("hidden");
|
||||||
el.netEditor.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);
|
const net = state.model.nets.find((n) => n.name === state.selectedNet);
|
||||||
if (net) {
|
if (net) {
|
||||||
el.selectedSummary.textContent = `${net.name} (${net.class})\nNodes: ${net.nodes.map((n) => `${n.ref}.${n.pin}`).join(", ")}`;
|
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.componentEditor.classList.add("hidden");
|
||||||
el.symbolEditor.classList.add("hidden");
|
el.symbolEditor.classList.add("hidden");
|
||||||
el.pinEditor.classList.add("hidden");
|
el.pinEditor.classList.add("hidden");
|
||||||
@ -1915,6 +1941,63 @@ function setupEvents() {
|
|||||||
queueCompile(true, "rotate");
|
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", () => {
|
el.showPinNetLabelInput.addEventListener("change", () => {
|
||||||
if (!state.selectedPin) {
|
if (!state.selectedPin) {
|
||||||
return;
|
return;
|
||||||
@ -2054,6 +2137,17 @@ function setupEvents() {
|
|||||||
queueCompile(true, "net-edit");
|
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", () => {
|
el.addNetNodeBtn.addEventListener("click", () => {
|
||||||
if (!state.selectedNet) {
|
if (!state.selectedNet) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@ -92,6 +92,9 @@
|
|||||||
<div class="editorActions">
|
<div class="editorActions">
|
||||||
<button id="rotateSelectedBtn">Rotate +90</button>
|
<button id="rotateSelectedBtn">Rotate +90</button>
|
||||||
<button id="updatePlacementBtn">Apply Component</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>
|
</div>
|
||||||
<div id="symbolEditor" class="editorCard hidden">
|
<div id="symbolEditor" class="editorCard hidden">
|
||||||
@ -180,6 +183,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="editorActions">
|
<div class="editorActions">
|
||||||
<button id="updateNetBtn">Apply Net</button>
|
<button id="updateNetBtn">Apply Net</button>
|
||||||
|
<button id="isolateSelectedNetBtn">Isolate</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="editorGrid">
|
<div class="editorGrid">
|
||||||
<label>Node Ref <input id="netNodeRefInput" type="text" placeholder="U1" /></label>
|
<label>Node Ref <input id="netNodeRefInput" type="text" placeholder="U1" /></label>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user