Rename the Explorer to "Overview" + add Ctrl/Shift multi-select

- The region panel is now titled "Overview" (with a tooltip describing the gestures); README updated.
- Overview rows: Ctrl/Cmd-click toggles a row in/out of the selection; Shift-click selects the
  range from the anchor to the clicked row in displayed (tree) order. Plain click still single-selects
  and sets the range anchor. Selection is shared with the canvas, so multi-selects show + move there.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Chris
2026-06-25 09:14:44 +02:00
co-authored by Claude Opus 4.8
parent 28fba64f87
commit c90c5c19eb
2 changed files with 30 additions and 7 deletions
+4 -3
View File
@@ -27,10 +27,11 @@ A visual prompt builder for Ideogram 4's structured JSON caption format. Draw bo
regions on a canvas, set each region's type / description / text / colors, and the node assembles
the caption JSON.
Includes an **Object Explorer** — a docked, collapsible list of every region. Click a row to
Includes an **Overview panel** — a docked, collapsible tree of every region. Click a row to
select it (two-way with the canvas), so you can pick and edit regions without accidentally
nudging them on the canvas. Reorder, lock, duplicate, and delete from the list. (Region
parenting / hierarchy is planned.)
nudging them on the canvas. **Ctrl/Cmd-click** to multi-select, **Shift-click** for a range; lock,
duplicate, delete, and **drag a row onto another to nest it** (parenting — move a parent on the
canvas and its children follow). The hierarchy is editor-only; the exported caption stays flat.
## License & Credits
+26 -4
View File
@@ -804,7 +804,8 @@ app.registerExtension({
stopProp(exToggle);
const exTitle = document.createElement("span");
exTitle.className = "ai2go-ideo-extitle";
exTitle.textContent = "Regions";
exTitle.textContent = "Overview";
exTitle.title = "Click a region to select · Ctrl/Cmd-click to multi-select · Shift-click for a range · drag a row onto another to nest";
const exList = document.createElement("div");
exList.className = "ai2go-ideo-exlist";
stopProp(exList);
@@ -2700,10 +2701,31 @@ app.registerExtension({
}
commit();
});
row.addEventListener("click", () => {
row.addEventListener("click", (ev) => {
if (node._exSuppressClick) { node._exSuppressClick = false; return; } // swallow the click that trails a reparent drag
canvasEl.focus(); // show the selection on canvas + route Del/Ctrl+C here
selectOnly(node._boxes.indexOf(b));
const idx = node._boxes.indexOf(b);
if (ev.shiftKey && node._selAnchor != null && node._selAnchor !== idx) {
// Shift = range-select from the anchor to here, in the CURRENT displayed (tree) order.
const order = Array.from(exList.querySelectorAll(".ai2go-ideo-lrow")).map((r) => node._boxes.indexOf(r._box));
const a = order.indexOf(node._selAnchor), c = order.indexOf(idx);
if (a >= 0 && c >= 0) {
const [lo, hi] = a < c ? [a, c] : [c, a];
node._selection = new Set(order.slice(lo, hi + 1).filter((k) => k >= 0));
node._activeIdx = idx;
} else { selectOnly(idx); node._selAnchor = idx; }
} else if (ev.ctrlKey || ev.metaKey) {
// Ctrl/Cmd = toggle this row in/out of the multi-selection.
if (node._selection.has(idx) && node._selection.size > 1) {
node._selection.delete(idx);
if (node._activeIdx === idx) node._activeIdx = node._selection.values().next().value ?? -1;
} else {
node._selection.add(idx); node._activeIdx = idx;
}
node._selAnchor = idx;
} else {
selectOnly(idx); node._selAnchor = idx; // plain click → single select + set the range anchor
}
commit();
});
dup.addEventListener("click", (e) => { e.stopPropagation(); startPlacing(node._boxes.indexOf(b)); });
@@ -2713,7 +2735,7 @@ app.registerExtension({
if (idx < 0) return;
removeBox(idx); commit(); fitCanvas();
});
// Drag a row to RE-PARENT it: drop ONTO another row → become its child; drop on the "Regions"
// Drag a row to RE-PARENT it: drop ONTO another row → become its child; drop on the "Overview"
// header → back to root. Cycle-guarded (can't drop onto self or a descendant).
row.addEventListener("pointerdown", (e) => {
if (e.button !== 0 || e.target === lock || e.target === dup || e.target === del || e.target === tri) return;