From be1e50fd54b415b864ef2474664d607c2fa4cbf6 Mon Sep 17 00:00:00 2001 From: LamGC Date: Mon, 12 Oct 2026 23:16:36 +0800 Subject: [PATCH] fix: keep touch copy controls visible and pass POSIX lint --- .github/workflows/check.yml | 16 ++++++++++++---- .github/workflows/deploy.yml | 14 +++++++++++--- conf-sshd.sh | 34 +++++++++++++++++++++++++++------- site/assets/style.css | 5 +++++ tests/site.test.mjs | 8 ++++++++ 5 files changed, 63 insertions(+), 14 deletions(-) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 93a5b64..547e0f1 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -10,14 +10,22 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 15 steps: - - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 - - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0 with: node-version: '22' cache: npm - run: npm ci - name: Install shell test interpreters - run: sudo apt-get update && sudo apt-get install -y busybox shellcheck + run: | + if ! command -v busybox >/dev/null || ! command -v shellcheck >/dev/null; then + sudo apt-get update + sudo apt-get install -y busybox-static shellcheck + fi + busybox sh -c 'echo "BusyBox shell ready"' + shellcheck --version - name: Run unit, shell, and real-browser tests run: npm test env: @@ -29,7 +37,7 @@ jobs: run: npx wrangler deploy --dry-run --config cf-worker/wrangler.toml --outdir /tmp/worker-dry-run - name: Upload browser evidence if: always() - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2 with: name: browser-evidence path: /tmp/quickly-browser-artifacts diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 70507ba..6a5fc61 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -17,14 +17,22 @@ jobs: timeout-minutes: 15 environment: production steps: - - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 - - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0 with: node-version: '22' cache: npm - run: npm ci - name: Install shell test interpreters - run: sudo apt-get update && sudo apt-get install -y busybox shellcheck + run: | + if ! command -v busybox >/dev/null || ! command -v shellcheck >/dev/null; then + sudo apt-get update + sudo apt-get install -y busybox-static shellcheck + fi + busybox sh -c 'echo "BusyBox shell ready"' + shellcheck --version - run: npm test env: BROWSER_TESTS: '1' diff --git a/conf-sshd.sh b/conf-sshd.sh index 7f43f4e..425380e 100755 --- a/conf-sshd.sh +++ b/conf-sshd.sh @@ -54,7 +54,11 @@ select_mode() { } while [ "$#" -gt 0 ]; do case $1 in - -h|--help) [ "$#" -eq 1 ] && [ "$mode" = sync ] && [ "$cron_requested" = no ] || fail '--help must be used alone.'; usage; exit 0 ;; + -h|--help) + if [ "$#" -ne 1 ] || [ "$mode" != sync ] || [ "$cron_requested" != no ]; then + fail '--help must be used alone.' + fi + usage; exit 0 ;; -o|--only-update-keys) select_mode keys ;; -u|--update-self) select_mode update ;; --uninstall) select_mode uninstall ;; @@ -219,9 +223,13 @@ sha256() { } check_expected_file() { if [ "$2" = absent ]; then - [ ! -e "$1" ] && [ ! -L "$1" ] || fail "File appeared concurrently; retry after reviewing: $1" + if [ -e "$1" ] || [ -L "$1" ]; then + fail "File appeared concurrently; retry after reviewing: $1" + fi else - [ -f "$1" ] && cmp -s "$1" "$2" || fail "File changed concurrently; retry after reviewing: $1" + if [ ! -f "$1" ] || ! cmp -s "$1" "$2"; then + fail "File changed concurrently; retry after reviewing: $1" + fi fi } replace_file() { @@ -394,17 +402,29 @@ sync_keys() { if [ "$positions" = absent ]; then cat "$auth_snapshot" > "$new_auth" if [ -s "$new_auth" ] && ! last_lf "$new_auth"; then printf '\n' >> "$new_auth"; fi - printf '%s\n' "$BEGIN_KEYS" >> "$new_auth"; cat "$payload" >> "$new_auth"; printf '%s\n' "$END_KEYS" >> "$new_auth" + { + printf '%s\n' "$BEGIN_KEYS" + cat "$payload" + printf '%s\n' "$END_KEYS" + } >> "$new_auth" else # Validated awk output: two decimal offsets. # shellcheck disable=SC2086 set -- $positions dd if="$auth_snapshot" bs=1 count="$1" 2>/dev/null > "$new_auth" - printf '%s\n' "$BEGIN_KEYS" >> "$new_auth"; cat "$payload" >> "$new_auth"; printf '%s\n' "$END_KEYS" >> "$new_auth" - dd if="$auth_snapshot" bs=1 skip="$2" 2>/dev/null >> "$new_auth" + { + printf '%s\n' "$BEGIN_KEYS" + cat "$payload" + printf '%s\n' "$END_KEYS" + dd if="$auth_snapshot" bs=1 skip="$2" 2>/dev/null + } >> "$new_auth" fi else - printf '%s\n' "$BEGIN_KEYS" > "$new_auth"; cat "$payload" >> "$new_auth"; printf '%s\n' "$END_KEYS" >> "$new_auth" + { + printf '%s\n' "$BEGIN_KEYS" + cat "$payload" + printf '%s\n' "$END_KEYS" + } > "$new_auth" fi check_path "$auth_file" file check_expected_file "$auth_file" "$auth_snapshot" diff --git a/site/assets/style.css b/site/assets/style.css index 5c50993..7fb22fc 100644 --- a/site/assets/style.css +++ b/site/assets/style.css @@ -165,6 +165,11 @@ dialog::backdrop { background: rgb(10 22 15 / 52%); } .code-block > .copy-button { opacity: 0; transition: opacity 120ms ease; } .code-block:hover > .copy-button, .code-block:focus-within > .copy-button, .code-block > .copy-button[data-state="success"] { opacity: 1; } } +/* Keep copies discoverable on touch/hybrid devices and narrow layouts, even + when the browser reports a fine pointer or changes its emulated media. */ +@media (hover: none), (pointer: coarse), (any-pointer: coarse), (max-width: 680px) { + .code-block > .copy-button { opacity: 1; transition: none; } +} @media (max-width: 900px) { .page-shell { width: calc(100% - 56px); } .hero { grid-template-columns: 1fr 305px; gap: 36px; padding-block: 45px; } diff --git a/tests/site.test.mjs b/tests/site.test.mjs index 89c036c..ec7058d 100644 --- a/tests/site.test.mjs +++ b/tests/site.test.mjs @@ -223,3 +223,11 @@ test("CSS covers system theme, narrow viewports, touch copies, focus, and reduce assert.match(css, /prefers-reduced-motion/); assert.doesNotMatch(script, /innerHTML|insertAdjacentHTML|\beval\s*\(|\bfetch\s*\(/); }); + + +test("touch and narrow layouts explicitly override desktop copy-button hiding", () => { + const override = css.match(/@media \(hover: none\), \(pointer: coarse\), \(any-pointer: coarse\), \(max-width: 680px\) \{[\s\S]*?\n\}/); + assert.ok(override, "touch, hybrid, and narrow layouts need an explicit visibility rule"); + assert.match(override[0], /\.code-block > \.copy-button \{ opacity: 1; transition: none; \}/); + assert.ok(override.index > css.indexOf("@media (hover: hover) and (pointer: fine)"), "touch visibility must win the CSS cascade"); +});