diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 61442e7..5fcf36c 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -23,7 +23,6 @@ jobs:
toolchain: ${{ env.RUST_TOOLCHAIN }}
profile: minimal
override: true
- - uses: swatinem/rust-cache@v2
- name: Build (Makefile linux-arm64)
run: make ubuntu-arm64
- name: Run tests
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 60a1e1e..6746936 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -79,8 +79,6 @@ jobs:
profile: minimal
override: true
- - uses: swatinem/rust-cache@v2
-
- name: Install packaging tools
run: cargo install cargo-deb cargo-generate-rpm
diff --git a/README.md b/README.md
index 2f44dbc..c176216 100644
--- a/README.md
+++ b/README.md
@@ -66,7 +66,10 @@ Explore Kingfisher’s built-in report viewer and its `--access-map`, which can
kingfisher scan /path/to/scan --access-map --view-report
```
-
+
+
+**Click to view video**
+[](docs/kingfisher-usage-access-map-02.mp4)
# Table of Contents
diff --git a/docs/demos/.gitignore b/docs/demos/.gitignore
index 8d7839d..d8d07cb 100644
--- a/docs/demos/.gitignore
+++ b/docs/demos/.gitignore
@@ -137,3 +137,6 @@ dist
# SvelteKit build / generate output
.svelte-kit
+
+
+pw-out/*
diff --git a/docs/demos/findings-thumbnail.png b/docs/demos/findings-thumbnail.png
new file mode 100644
index 0000000..574f04a
Binary files /dev/null and b/docs/demos/findings-thumbnail.png differ
diff --git a/docs/demos/merge.sh b/docs/demos/merge.sh
deleted file mode 100755
index d230858..0000000
--- a/docs/demos/merge.sh
+++ /dev/null
@@ -1,28 +0,0 @@
-GIF_IN="../kingfisher-usage-access-map-01.gif"
-WEBM_IN="pw-out/066d10b5ae5d3603dacd69417a8227c6.webm"
-OUT_GIF="../kingfisher-usage-access-map-01+accessmap.gif"
-
-# 1) Normalize GIF -> MP4 (H.264, fixed fps/size)
-ffmpeg -y -i "$GIF_IN" \
- -vf "fps=12,scale=960:-2:flags=lanczos" \
- -an -c:v libx264 -pix_fmt yuv420p -crf 18 -preset veryfast \
- gif_part.mp4
-
-# 2) Normalize WEBM -> MP4 (same settings)
-ffmpeg -y -i "$WEBM_IN" \
- -vf "fps=12,scale=960:-2:flags=lanczos" \
- -an -c:v libx264 -pix_fmt yuv420p -crf 18 -preset veryfast \
- webm_part.mp4
-
-# 3) Concatenate via filter (video-only; reliable)
-ffmpeg -y -i gif_part.mp4 -i webm_part.mp4 \
- -filter_complex "[0:v][1:v]concat=n=2:v=1:a=0[v]" \
- -map "[v]" -c:v libx264 -pix_fmt yuv420p -crf 18 -preset veryfast \
- combined.mp4
-
-# 4) Convert combined MP4 -> GIF (single palette across whole thing)
-ffmpeg -y -i combined.mp4 \
- -vf "fps=12,scale=960:-1:flags=lanczos,split[s0][s1];[s0]palettegen=max_colors=256[p];[s1][p]paletteuse=dither=bayer" \
- "$OUT_GIF"
-
-echo "Wrote: $OUT_GIF"
diff --git a/docs/demos/record.mjs b/docs/demos/record.mjs
index 30ab60a..5defc84 100644
--- a/docs/demos/record.mjs
+++ b/docs/demos/record.mjs
@@ -6,24 +6,142 @@ fs.mkdirSync(outDir, { recursive: true });
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
+const cursorOverlayScript = `
+(() => {
+ const style = document.createElement('style');
+ style.textContent = \`
+ #__pw_cursor {
+ position: fixed;
+ top: 0; left: 0;
+ width: 18px; height: 18px;
+ transform: translate(-100px, -100px);
+ z-index: 2147483647;
+ pointer-events: none;
+ }
+ #__pw_cursor svg { width: 18px; height: 18px; }
+ #__pw_cursor .dot {
+ fill: rgba(255,255,255,0.9);
+ stroke: rgba(0,0,0,0.85);
+ stroke-width: 2;
+ }
+ #__pw_click {
+ position: fixed;
+ width: 8px; height: 8px;
+ border-radius: 50%;
+ transform: translate(-100px, -100px);
+ z-index: 2147483646;
+ pointer-events: none;
+ opacity: 0;
+ border: 2px solid rgba(0,0,0,0.6);
+ }
+ \`;
+ document.documentElement.appendChild(style);
+
+ const cursor = document.createElement('div');
+ cursor.id = '__pw_cursor';
+ cursor.innerHTML = \`
+
+ \`;
+ document.documentElement.appendChild(cursor);
+
+ const click = document.createElement('div');
+ click.id = '__pw_click';
+ document.documentElement.appendChild(click);
+
+ let x = -100, y = -100;
+ const move = (nx, ny) => {
+ x = nx; y = ny;
+ cursor.style.transform = \`translate(\${x}px, \${y}px)\`;
+ click.style.transform = \`translate(\${x}px, \${y}px)\`;
+ };
+
+ window.addEventListener('pointermove', (e) => move(e.clientX, e.clientY), { passive: true });
+ window.addEventListener('mousemove', (e) => move(e.clientX, e.clientY), { passive: true });
+
+ window.addEventListener('pointerdown', () => {
+ click.style.transition = 'none';
+ click.style.opacity = '0.9';
+ click.style.width = '8px';
+ click.style.height = '8px';
+ requestAnimationFrame(() => {
+ click.style.transition = 'all 250ms ease-out';
+ click.style.opacity = '0';
+ click.style.width = '28px';
+ click.style.height = '28px';
+ });
+ }, { passive: true });
+})();
+`;
+
+// Converts native