refine swipe action reveal timing
This commit is contained in:
@@ -113,12 +113,28 @@ def _run_git(args, cwd, timeout=10):
|
||||
|
||||
def _dirty_suffix(path: Path, timeout=1) -> str:
|
||||
"""Return a best-effort ``-dirty`` suffix without blocking version display."""
|
||||
out, ok = _run_git(['diff-index', '--quiet', 'HEAD', '--'], path, timeout=timeout)
|
||||
if ok:
|
||||
try:
|
||||
r = subprocess.run(
|
||||
['git', 'diff-index', '--quiet', 'HEAD', '--'],
|
||||
cwd=str(path),
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=timeout,
|
||||
)
|
||||
except (subprocess.TimeoutExpired, FileNotFoundError, OSError):
|
||||
return ""
|
||||
if r.returncode == 0:
|
||||
return ""
|
||||
# diff-index exits 1 with no output for a dirty tree. Timeouts and real git
|
||||
# failures include a diagnostic; skip the suffix so the base version remains.
|
||||
return "-dirty" if not out else ""
|
||||
diagnostic = (r.stderr or r.stdout or "").strip()
|
||||
if r.returncode != 1 or diagnostic:
|
||||
return ""
|
||||
diff, ok = _run_git(['diff', '--binary', 'HEAD', '--'], path, timeout=timeout)
|
||||
if not (ok and diff):
|
||||
return "-dirty"
|
||||
digest = hashlib.sha1(diff.encode('utf-8', errors='replace')).hexdigest()[:8]
|
||||
return f"-dirty-{digest}"
|
||||
|
||||
|
||||
def _describe_git_version(path: Path, *, timeout=5, dirty_timeout=1) -> str | None:
|
||||
|
||||
@@ -3866,20 +3866,24 @@ function renderSessionListFromCache(){
|
||||
const offset=Math.sign(rawOffset)*(Math.abs(revealedOffset)+Math.sqrt(overshoot)*5);
|
||||
const progress=Math.min(1,Math.abs(revealedOffset)/72);
|
||||
const reveal=Math.abs(offset);
|
||||
const iconScale=Math.min(1,Math.max(.01,progress*1.7));
|
||||
const actionRevealScale=1.15;
|
||||
const iconScale=Math.min(1,Math.max(.01,progress*actionRevealScale));
|
||||
const badgeSize=34*iconScale;
|
||||
const iconSize=18*iconScale;
|
||||
const labelScale=Math.min(1,Math.max(.01,progress*1.7));
|
||||
const actionInset=0;
|
||||
const labelScale=Math.min(1,Math.max(.01,progress*actionRevealScale));
|
||||
const actionOpacity=Math.min(1,Math.max(.01,progress*actionRevealScale));
|
||||
const actionInset=6;
|
||||
const tileGap=6;
|
||||
const badgeStretch=Math.min(Math.max(0,reveal-34),overshoot*1.15,Math.max(0,reveal-badgeSize-actionInset-tileGap));
|
||||
const stretchStart=72/actionRevealScale;
|
||||
const stretchProgress=Math.max(0,reveal-stretchStart);
|
||||
const badgeStretch=Math.min(Math.max(0,reveal-34),stretchProgress*1.15,Math.max(0,reveal-badgeSize-actionInset-tileGap));
|
||||
el.style.setProperty('--session-swipe-offset',offset+'px');
|
||||
el.style.setProperty('--session-swipe-reveal',reveal+'px');
|
||||
el.style.setProperty('--session-swipe-badge-size',badgeSize+'px');
|
||||
el.style.setProperty('--session-swipe-icon-size',iconSize+'px');
|
||||
el.style.setProperty('--session-swipe-label-scale',labelScale);
|
||||
el.style.setProperty('--session-swipe-badge-stretch',badgeStretch+'px');
|
||||
el.style.setProperty('--session-swipe-progress',Math.pow(progress,1.5));
|
||||
el.style.setProperty('--session-swipe-progress',actionOpacity);
|
||||
el.classList.toggle('swiping-right',offset>0);
|
||||
el.classList.toggle('swiping-left',offset<0);
|
||||
};
|
||||
|
||||
@@ -807,8 +807,8 @@
|
||||
transform:scale(var(--session-swipe-label-scale,1));
|
||||
transform-origin:top center;
|
||||
}
|
||||
.session-swipe-affordance-right .session-swipe-action-stack{margin-left:0;}
|
||||
.session-swipe-affordance-left .session-swipe-action-stack{margin-right:0;}
|
||||
.session-swipe-affordance-right .session-swipe-action-stack{transform:translateX(6px);}
|
||||
.session-swipe-affordance-left .session-swipe-action-stack{transform:translateX(-6px);}
|
||||
.session-item.dragging{transition:background .15s,color .15s,box-shadow .15s ease;will-change:transform;}
|
||||
.session-item.session-reflowing{transition:background .15s,color .15s,transform .36s cubic-bezier(.2,.8,.2,1),box-shadow .15s ease;will-change:transform;}
|
||||
.session-item.swipe-committed,
|
||||
|
||||
@@ -123,16 +123,22 @@ def test_session_swipe_paint_uses_transform_only_exit():
|
||||
assert "--session-swipe-progress" in paint
|
||||
assert "--session-swipe-badge-stretch" in paint
|
||||
assert "const reveal=Math.abs(offset);" in paint
|
||||
assert "const iconScale=Math.min(1,Math.max(.01,progress*1.7));" in paint
|
||||
assert "const actionRevealScale=1.15;" in paint
|
||||
assert "const iconScale=Math.min(1,Math.max(.01,progress*actionRevealScale));" in paint
|
||||
assert "const badgeSize=34*iconScale;" in paint
|
||||
assert "const iconSize=18*iconScale;" in paint
|
||||
assert "const labelScale=Math.min(1,Math.max(.01,progress*1.7));" in paint
|
||||
assert "const actionInset=0;" in paint
|
||||
assert "const labelScale=Math.min(1,Math.max(.01,progress*actionRevealScale));" in paint
|
||||
assert "const actionOpacity=Math.min(1,Math.max(.01,progress*actionRevealScale));" in paint
|
||||
assert "const actionInset=6;" in paint
|
||||
assert "const tileGap=6;" in paint
|
||||
assert "const badgeStretch=Math.min(Math.max(0,reveal-34),overshoot*1.15,Math.max(0,reveal-badgeSize-actionInset-tileGap));" in paint
|
||||
assert "const stretchStart=72/actionRevealScale;" in paint
|
||||
assert "const stretchProgress=Math.max(0,reveal-stretchStart);" in paint
|
||||
assert "const badgeStretch=Math.min(Math.max(0,reveal-34),stretchProgress*1.15,Math.max(0,reveal-badgeSize-actionInset-tileGap));" in paint
|
||||
assert "el.style.setProperty('--session-swipe-badge-size',badgeSize+'px');" in paint
|
||||
assert "el.style.setProperty('--session-swipe-icon-size',iconSize+'px');" in paint
|
||||
assert "el.style.setProperty('--session-swipe-label-scale',labelScale);" in paint
|
||||
assert "el.style.setProperty('--session-swipe-progress',actionOpacity);" in paint
|
||||
assert "Math.pow(progress,1.5)" not in paint
|
||||
assert "window.innerWidth+'px'" in complete
|
||||
assert "el.style.height=rect.height+'px'" in complete
|
||||
assert "requestAnimationFrame(()=>el.classList.add('swipe-removing'))" in complete
|
||||
@@ -189,8 +195,8 @@ def test_session_swipe_actions_use_circular_icon_badges():
|
||||
assert "font-size:10px" in label
|
||||
assert "transform:scale(var(--session-swipe-label-scale,1))" in label
|
||||
assert "transform-origin:top center" in label
|
||||
assert ".session-swipe-affordance-right .session-swipe-action-stack{margin-left:0;}" in STYLE_CSS
|
||||
assert ".session-swipe-affordance-left .session-swipe-action-stack{margin-right:0;}" in STYLE_CSS
|
||||
assert ".session-swipe-affordance-right .session-swipe-action-stack{transform:translateX(6px);}" in STYLE_CSS
|
||||
assert ".session-swipe-affordance-left .session-swipe-action-stack{transform:translateX(-6px);}" in STYLE_CSS
|
||||
|
||||
|
||||
def test_session_removal_reflows_surviving_rows_smoothly():
|
||||
|
||||
@@ -34,6 +34,59 @@ def _make_run_git_side_effect(*sequence):
|
||||
_MODULE = 'api.updates'
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Tests for version dirty suffix
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class TestVersionDirtySuffix:
|
||||
def test_dirty_suffix_detects_diff_index_dirty_exit(self, tmp_path):
|
||||
"""git diff-index exits 1 with no output for normal dirty checkouts."""
|
||||
from api import updates
|
||||
|
||||
diff_index = subprocess.CompletedProcess(
|
||||
['git', 'diff-index', '--quiet', 'HEAD', '--'],
|
||||
1,
|
||||
stdout='',
|
||||
stderr='',
|
||||
)
|
||||
with patch(f'{_MODULE}.subprocess.run', return_value=diff_index), \
|
||||
patch(f'{_MODULE}._run_git', return_value=('changed file content', True)):
|
||||
assert updates._dirty_suffix(tmp_path).startswith('-dirty-')
|
||||
|
||||
def test_dirty_suffix_changes_with_diff_content(self, tmp_path):
|
||||
"""Dirty asset tokens should change when local tracked edits change."""
|
||||
from api import updates
|
||||
|
||||
diff_index = subprocess.CompletedProcess(
|
||||
['git', 'diff-index', '--quiet', 'HEAD', '--'],
|
||||
1,
|
||||
stdout='',
|
||||
stderr='',
|
||||
)
|
||||
with patch(f'{_MODULE}.subprocess.run', return_value=diff_index), \
|
||||
patch(f'{_MODULE}._run_git', return_value=('first diff', True)):
|
||||
first = updates._dirty_suffix(tmp_path)
|
||||
with patch(f'{_MODULE}.subprocess.run', return_value=diff_index), \
|
||||
patch(f'{_MODULE}._run_git', return_value=('second diff', True)):
|
||||
second = updates._dirty_suffix(tmp_path)
|
||||
assert first.startswith('-dirty-')
|
||||
assert second.startswith('-dirty-')
|
||||
assert first != second
|
||||
|
||||
def test_dirty_suffix_ignores_git_errors(self, tmp_path):
|
||||
"""Real git diagnostics should not mark unknown/broken repos dirty."""
|
||||
from api import updates
|
||||
|
||||
completed = subprocess.CompletedProcess(
|
||||
['git', 'diff-index', '--quiet', 'HEAD', '--'],
|
||||
128,
|
||||
stdout='',
|
||||
stderr='fatal: not a git repository',
|
||||
)
|
||||
with patch(f'{_MODULE}.subprocess.run', return_value=completed):
|
||||
assert updates._dirty_suffix(tmp_path) == ''
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Tests for _apply_update_inner() diagnostic paths
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user