From c90c5c19eb3ca9c25836833b46410aeda8d5b253 Mon Sep 17 00:00:00 2001 From: Chris Date: Thu, 25 Jun 2026 09:14:44 +0200 Subject: [PATCH] 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) --- README.md | 7 ++++--- web/js/ideogram4_prompt_builder.js | 30 ++++++++++++++++++++++++++---- 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index cb1b0a3..46aa98c 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/web/js/ideogram4_prompt_builder.js b/web/js/ideogram4_prompt_builder.js index 2c55249..e02b914 100644 --- a/web/js/ideogram4_prompt_builder.js +++ b/web/js/ideogram4_prompt_builder.js @@ -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;