mirror of
https://github.com/nesquena/hermes-webui.git
synced 2026-05-25 03:00:23 +00:00
b5e8e67d71
Closes #1707 — single-click on a workspace tree filename did nothing. #1698 was a regression where the filename's dblclick rename handler was unreachable because the row's el.onclick (openFile) fired synchronously on the first click. The fix in #1702 stopped click propagation on nameEl — but that broke single-click activation entirely (#1707): clicking the filename now did nothing, you had to click the icon or row whitespace to open the file. Restored fix preserves both intents via a 300ms debounced delegator: let _nameClickTimer = null; nameEl.onclick = (e) => { e.stopPropagation(); if (_nameClickTimer) { clearTimeout(_nameClickTimer); _nameClickTimer = null; } _nameClickTimer = setTimeout(() => { _nameClickTimer = null; if (typeof el.onclick === 'function') el.onclick(e); }, 300); }; nameEl.ondblclick = (e) => { e.stopPropagation(); if (_nameClickTimer) { clearTimeout(_nameClickTimer); _nameClickTimer = null; } // ... existing rename body }; Single-click on nameEl schedules a setTimeout that calls el.onclick(e) after the dblclick threshold passes (300ms — matches the OS dblclick threshold on most platforms). Double-click cancels the pending timer and triggers the existing rename input. Cost: 300ms latency on file-open clicks. Acceptable trade for keeping rename reachable on single-click. Also updated tests/test_workspace_tree_rename.py to accept both the pre-#1707 (pure stopPropagation) and post-#1707 (debounced delegator) shapes — the original assertion was too narrow and would have rejected the correct fix. 9 new regression tests in tests/test_1707_workspace_filename_click.py: - 6 source-level static-analysis checks on the patched handler shape - 3 behavioral tests via Node VM (synthesize click → 300ms delay, click → dblclick within tick → assert rename mounts + openFile is not called). 7 of 9 tests fail on master pre-fix (verified); all 9 pass after.
38 lines
1.8 KiB
Python
38 lines
1.8 KiB
Python
from pathlib import Path
|
|
|
|
|
|
REPO_ROOT = Path(__file__).resolve().parents[1]
|
|
UI_JS = (REPO_ROOT / "static" / "ui.js").read_text(encoding="utf-8")
|
|
|
|
|
|
def test_workspace_file_name_click_does_not_immediately_bubble():
|
|
"""Clicking a file name must not synchronously bubble to the row open handler
|
|
before dblclick can fire. The fix originally landed as pure stopPropagation
|
|
(#1698), then evolved to a 300ms debounce that delegates to el.onclick (#1707
|
|
— the pure-stopPropagation form broke single-click activation entirely).
|
|
|
|
Either shape satisfies the #1698 invariant. Accept both:
|
|
- pre-#1707 shape: `nameEl.onclick=(e)=>e.stopPropagation();`
|
|
- post-#1707 shape: any `nameEl.onclick=(e)=>{...stopPropagation()...setTimeout...}`
|
|
"""
|
|
name_start = UI_JS.index("const nameEl=document.createElement('span');")
|
|
dblclick_idx = UI_JS.index("nameEl.ondblclick=(e)=>", name_start)
|
|
block = UI_JS[name_start:dblclick_idx]
|
|
|
|
assert "nameEl.onclick" in block, (
|
|
"workspace file-tree name span must bind nameEl.onclick to prevent the "
|
|
"first click of a dblclick from triggering the row's openFile (#1698)"
|
|
)
|
|
# The bound handler must call stopPropagation (either the original simple form
|
|
# or the post-#1707 debounce form that contains stopPropagation in its body).
|
|
assert "stopPropagation" in block, (
|
|
"nameEl.onclick must call stopPropagation so the row's el.onclick does not "
|
|
"fire on the first click of a dblclick (#1698)"
|
|
)
|
|
|
|
|
|
def test_workspace_file_row_click_still_opens_file_preview():
|
|
"""The row-level openFile binding must still exist — the nameEl handler delegates
|
|
to it (post-#1707) or sits beneath it as a pure barrier (pre-#1707)."""
|
|
assert "el.onclick=async()=>openFile(item.path);" in UI_JS
|