mirror of
https://github.com/PR0M3TH3AN/Artstr-Studio.git
synced 2026-10-27 08:40:39 +00:00
fix(book): text-frame ports were eating resize handles + drag/select events
Three reported bugs, one root cause: the in/out ports sat at the top-left and bottom-right CORNERS of every text-frame layer — exactly where the lh-nw and lh-se resize handles live — AND their pointerdown bubbled to the freeLayer's startLayerDrag handler. Symptoms: - Couldn't resize a text frame (corner handles hidden under the larger-z-index ports). - Couldn't click an in/out port to thread (the port's pointerdown started a layer drag, the click never fired). - Couldn't 'Place chapter into selected frame' (the chapter button needs a selected frame, and selection requires a click that wasn't being shadowed by drag). Fixes: - Move ports OUTSIDE the layer's bounding box on the top edge (in port) and bottom edge (out port), centered. New tab shape with a dashed connector to the frame edge so the affordance reads. Both well clear of the 8 corner/edge/rotation handles. - Bump the frameId badge to sit just above the layer (top-right, -16px) so it doesn't visually conflict with the in port. - Add pointerdown + mousedown handlers on both ports AND on the source-chapter chip + its X clear button that stopPropagation. Drag handler no longer kidnaps the click. Resize, select, link, and place-chapter all flow correctly now. Co-Authored-By: Claude Opus 4.7 <[email protected]>
This commit is contained in:
1 parent
b6b00c09f1
commit
81dd6bf6b6
1 file changed
+43
-12
+43
-12
@@ -2050,24 +2050,38 @@
|
||||
.bookStarterCardDesc { font-size: .76rem; color: var(--ink); margin-top: 8px; line-height: 1.35; }
|
||||
|
||||
/* Phase J3: in / out ports for threaded text frames. The ports
|
||||
are small square handles at the corners of every text-frame
|
||||
layer; clicking out → in links the frames so chapter text
|
||||
flows from one to the next. Colour signals state:
|
||||
sit OUTSIDE the layer's bounding box on the top and bottom
|
||||
edges (centered) so they don't collide with the 8 corner /
|
||||
edge / rotation resize handles or the per-target controls
|
||||
layered on the frame. Click out → click another in to thread.
|
||||
Colour signals state:
|
||||
empty (slate) → unlinked
|
||||
linked (green) → part of a thread
|
||||
overset (red) → end of thread with overflow text */
|
||||
.bookFramePort {
|
||||
position: absolute;
|
||||
width: 12px; height: 12px;
|
||||
width: 14px; height: 14px;
|
||||
background: #fff;
|
||||
border: 1.5px solid #475569;
|
||||
box-sizing: border-box;
|
||||
cursor: crosshair;
|
||||
z-index: 50;
|
||||
z-index: 60;
|
||||
pointer-events: auto;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
.bookFramePort.in { top: -7px; left: -7px; border-radius: 0; }
|
||||
.bookFramePort.out { bottom: -7px; right: -7px; border-radius: 0; }
|
||||
.bookFramePort.in { top: -22px; border-radius: 0 0 7px 7px; }
|
||||
.bookFramePort.out { bottom: -22px; border-radius: 7px 7px 0 0; }
|
||||
.bookFramePort::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
width: 0; height: 8px;
|
||||
border-left: 1.5px dashed #475569;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
.bookFramePort.in::before { top: 100%; }
|
||||
.bookFramePort.out::before { bottom: 100%; }
|
||||
.bookFramePort.linked { background: #16a34a; border-color: #15803d; }
|
||||
.bookFramePort.overset { background: #dc2626; border-color: #991b1b; }
|
||||
.bookFramePort:hover { box-shadow: 0 0 0 2px rgba(96,165,250,.6); }
|
||||
@@ -2103,13 +2117,14 @@
|
||||
outline-color: rgba(59,130,246,.85);
|
||||
}
|
||||
.bookFrameIdBadge {
|
||||
position: absolute; bottom: 4px; left: 4px;
|
||||
font-size: .58rem; font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
||||
color: rgba(71,85,105,.65);
|
||||
background: rgba(255,255,255,.7);
|
||||
padding: 1px 4px; border-radius: 2px;
|
||||
position: absolute; top: -16px; right: -2px;
|
||||
font-size: .56rem; font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
||||
color: rgba(71,85,105,.55);
|
||||
background: rgba(255,255,255,.85);
|
||||
padding: 0 4px; border-radius: 2px;
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
z-index: 7;
|
||||
}
|
||||
|
||||
/* J5: source-chapter chip on threaded text frames. Shows which
|
||||
@@ -23076,6 +23091,11 @@
|
||||
const linkedIn = !!incoming;
|
||||
const isLoadingSource = _bookPortLoading?.layerId === layer.id;
|
||||
|
||||
// J3.b fix: stop pointerdown on the port from bubbling to the
|
||||
// freeLayer's startLayerDrag handler — otherwise the drag
|
||||
// starts and the click never fires.
|
||||
const stopPointerDown = (ev) => { ev.stopPropagation(); };
|
||||
|
||||
const inPort = document.createElement('div');
|
||||
inPort.className = 'bookFramePort in' + (linkedIn ? ' linked' : '');
|
||||
inPort.title = linkedIn
|
||||
@@ -23085,6 +23105,8 @@
|
||||
: 'In port — empty. Click an "out" port elsewhere first to load a thread, then click this.');
|
||||
inPort.dataset.framePort = 'in';
|
||||
inPort.dataset.frameId = myFrameId;
|
||||
inPort.addEventListener('pointerdown', stopPointerDown);
|
||||
inPort.addEventListener('mousedown', stopPointerDown);
|
||||
inPort.addEventListener('click', (ev) => _bookPortOnClick(layer, 'in', ev));
|
||||
if (linkedIn || linkedOut) {
|
||||
inPort.addEventListener('mouseenter', () => _bookHighlightThread(layer));
|
||||
@@ -23103,6 +23125,8 @@
|
||||
: 'Out port — text ends here. Click to load and then click another frame\'s in port to link.');
|
||||
outPort.dataset.framePort = 'out';
|
||||
outPort.dataset.frameId = myFrameId;
|
||||
outPort.addEventListener('pointerdown', stopPointerDown);
|
||||
outPort.addEventListener('mousedown', stopPointerDown);
|
||||
outPort.addEventListener('click', (ev) => _bookPortOnClick(layer, 'out', ev));
|
||||
if (linkedIn || linkedOut) {
|
||||
outPort.addEventListener('mouseenter', () => _bookHighlightThread(layer));
|
||||
@@ -23140,6 +23164,11 @@
|
||||
lab.textContent = `📄 ${title}${positionTag}`;
|
||||
lab.title = `Chapter placed on this thread's head frame. ${chain.length > 1 ? `This is frame ${myIndex + 1} of ${chain.length} in the chain.` : 'Solo frame — link more frames with the out port to extend the thread.'}`;
|
||||
chip.appendChild(lab);
|
||||
// J3.b fix: same pointerdown shield as ports — the chip
|
||||
// sits on top of the freeLayer, so its events would
|
||||
// otherwise start a drag.
|
||||
chip.addEventListener('pointerdown', (ev) => ev.stopPropagation());
|
||||
chip.addEventListener('mousedown', (ev) => ev.stopPropagation());
|
||||
// Only the head shows the clear (X) — clearing on a
|
||||
// mid-chain frame would be ambiguous.
|
||||
if (layer === head) {
|
||||
@@ -23147,6 +23176,8 @@
|
||||
clearBtn.className = 'bookFrameSourceClear';
|
||||
clearBtn.textContent = '×';
|
||||
clearBtn.title = 'Unplace this chapter from the thread.';
|
||||
clearBtn.addEventListener('pointerdown', (ev) => ev.stopPropagation());
|
||||
clearBtn.addEventListener('mousedown', (ev) => ev.stopPropagation());
|
||||
clearBtn.addEventListener('click', (ev) => {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
|
||||
Reference in new issue
Block a user