diff --git a/plugin-designs/my-upg-100-list/data/visual-gate.mjs b/plugin-designs/my-upg-100-list/data/visual-gate.mjs index 722bbaf99..e17fe7edf 100644 --- a/plugin-designs/my-upg-100-list/data/visual-gate.mjs +++ b/plugin-designs/my-upg-100-list/data/visual-gate.mjs @@ -1046,36 +1046,15 @@ async function main() { && colorState.fill.toLowerCase().includes('0072b2') && colorState.rendered > 0, colorState.err || `swatchClicked=${swatchClicked} handleColor=${colorState.handleColor} fill=${colorState.fill} opacity=${colorState.opacity} renderedSelFeatures=${colorState.rendered}`); - // ── (15) toolA §1(d) + R1.1 (h): Copy → names + total + trailing "· #hex" ── - // The Copy button click is a REAL Playwright click (user gesture → the live - // navigator.clipboard path runs; its success/fallback is reported via the - // flash label). The payload assertion reads __UPG_LAST_CLIPBOARD__ — the - // exact string the handler wrote (headless clipboard READ permission is not - // grantable in Firefox, so the write-side record is the assertable surface). - let copied = null; - const copyBtn = page.locator(`${EL} .upg-hud-btn.copy`); - if (await copyBtn.count()) { - await copyBtn.first().click().catch(() => {}); - await page.waitForTimeout(600); - copied = await page.evaluate((elName) => { - const host = document.querySelector(elName); - const root = host && host.shadowRoot ? host.shadowRoot : document; - const f = root.querySelector('.upg-hud-flash'); - return { text: window.__UPG_LAST_CLIPBOARD__ || '', flash: f ? f.textContent.trim() : '' }; - }, EL); - } - let copyOk = false, copyDetail = 'no .upg-hud-btn.copy in HUD'; - if (copied) { - const line1 = (copied.text.split('\n')[0]) || ''; - const names = shiftSel.err ? [] : shiftSel.list.map((x) => x.name); - const allNames = names.length === 3 && names.every((n) => line1.includes(n)); - const hasTotal = line1.includes('— ' + expectSum + ' UUPG'); - const hasColor = line1.endsWith('· #0072b2'); - copyOk = allNames && hasTotal && hasColor; - copyDetail = `line1=${JSON.stringify(line1)} allNames=${allNames} hasTotal(Σ=${expectSum})=${hasTotal} endsWith"· #0072b2"=${hasColor} clipboardApi=${JSON.stringify(copied.flash)}`; - } - check('(15) toolA: Copy → payload carries all 3 names + the independent total + trailing "· #0072b2"', - copyOk, copyDetail); + // ── (15) REMOVED 2026-09-28: the Copy payload assertion. It guarded on + // .upg-hud-btn.copy existing, but that button went with fc93847bb (batch + // selection builds a packet now). The guard skipped only the MEASUREMENT — + // copyOk was initialised false and check() ran unconditionally — so this + // reported FAIL ("no .upg-hud-btn.copy in HUD") on every run since. A + // permanently red assertion for a deleted feature teaches readers to ignore + // red, which is worse than no check. Its handler (copySelection/legacyCopy) + // and the window.__UPG_LAST_CLIPBOARD__ it read are deleted in the same + // commit. formatClipboard survives: __UPG_SELECT__.clipboardText still uses it. // ── (16) toolA R1.4 (a‴/f′ + L4): batch mode OFF → a PLAIN tap is NOT a // multi-select AND not a clear. Tap the RAIL button again ("Done") to LEAVE diff --git a/plugin-designs/my-upg-100-list/upg-100-list.vue b/plugin-designs/my-upg-100-list/upg-100-list.vue index 2074dff70..a00d526ed 100644 --- a/plugin-designs/my-upg-100-list/upg-100-list.vue +++ b/plugin-designs/my-upg-100-list/upg-100-list.vue @@ -889,54 +889,14 @@ function clearSelection() { refreshSelectionPaint() } -// ── 09b §1(d): copy the selection to the clipboard (D-1 format) ────────────── -// "Name1, Name2, … — {total} UUPG" + a 2nd line per-territory breakdown. -// navigator.clipboard on the click gesture; textarea+execCommand fallback for a -// non-secure context (where navigator.clipboard is undefined). Exposed on window -// for the firefox gate to read back the exact string the handler built. -async function copySelection() { - if (!selectedTerritories.value.length) return - // R1.1 (h): the payload ends with " · #hex" when a color is picked — the exact - // string toolB parses to regroup+recolor in one command. - const text = formatClipboard(selectedTerritories.value, { color: selectedColor.value }) - if (typeof window !== 'undefined') window.__UPG_LAST_CLIPBOARD__ = text - let ok = false - try { - if (navigator.clipboard && navigator.clipboard.writeText) { - await navigator.clipboard.writeText(text) - ok = true - } else { - ok = legacyCopy(text) - } - } catch (err) { - ok = legacyCopy(text) // secure-context / permission failure → fallback - } - // ⚠ THE OLD GUARD ONLY MATCHED THE SUCCESS BRANCH — `startsWith('Copied')` — so "Copy failed" NEVER - // CLEARED, in English, before any of this was translated. A pre-existing bug that the prose fix would - // have widened to both branches in ten languages. Found by exp-shepherd sweeping the whole tree for the - // PATTERN (a branch on a user-visible string's content) after I had only grepped for the exact shape I - // already knew, `status.value.startsWith` — which is why I missed the ternary form in my own file. - flashRef(copyFlash, ok ? t('messages.copied') : t('messages.copyFailed'), 1800) -} -// textarea + execCommand('copy') fallback (non-secure context, no clipboard API). -function legacyCopy(text) { - try { - const ta = document.createElement('textarea') - ta.value = text - ta.style.position = 'fixed'; ta.style.opacity = '0'; ta.style.top = '0' - document.body.appendChild(ta) - ta.focus(); ta.select() - const ok = document.execCommand && document.execCommand('copy') - document.body.removeChild(ta) - return !!ok - } catch (_) { return false } -} - -// ── R1.2 PIVOT (D-8): the Ctrl/Cmd-C + Escape keyboard handler is REMOVED. The -// the brief explicitly asked to drop modifier-key gestures for mobile parity — Copy -// + Clear are the on-screen HUD buttons (≥40px tap targets) and are the ONLY -// selection-action surface now. No code path depends on a keyboard modifier. +// ── R1.2 PIVOT (D-8): the Ctrl/Cmd-C + Escape keyboard handler is REMOVED — the +// brief asked to drop modifier-key gestures for mobile parity. The on-screen HUD +// buttons (≥40px tap targets) are the ONLY selection-action surface, and no code +// path depends on a keyboard modifier. +// Those buttons are Prayer List / Poster Map / Clear (:2856-2860). This comment +// used to name "Copy + Clear"; Copy went with fc93847bb, which is the same commit +// that orphaned the copySelection/legacyCopy pair deleted just above. // ── 09b-search-bar §1(a/c): build the unified index once records are loaded ── // PG rows come from the LIVE records (slug+name+lat/lng) — the same record set