docs: enhance README and homepage with two screenshot plates
- Add assets/before-after.png — Plate i: five real renames laid out as a
typeset table; mulberry highlight on the description segment; visual
callouts for the U+202F gap and the user-keyword preservation case
- Add assets/session.png — Plate ii: editorial paper frame around an
ink-dark Claude Code session card showing user prompt, claude
orchestration, parallel Haiku fan-out across ten batches, and the run
receipt
- Keep the source HTMLs (assets/{before-after,session}.html) so the
plates can be regenerated via headless Brave at 1600x1100
- README.md rewritten: centered hero with embedded plate i, dedicated
"A session, end to end" section embedding plate ii, new highlights
(multi-prefix, idempotent), new "What the parser accepts" table,
gotchas extended to 13, real-world impact promoted to a 3-row table
- docs/index.html surgically extended (existing editorial CSS preserved):
new .plate and .additions components, nav gets what's-new and session
links, new section 04 "What's new" with three additions cards plus a
run-iii receipt, original receipt relabeled run-ii, gotchas list
extended with #11-13, new section 07 "The session" embedding plate ii,
install renumbered to 08
- Image refs in docs/index.html use absolute gitea raw URLs so they
resolve when served from gitea pages or viewed locally
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+243
-6
@@ -657,6 +657,132 @@ footer .colophon {
|
||||
max-width: 50ch;
|
||||
}
|
||||
|
||||
/* ───── Plate (image + caption) ───────────────── */
|
||||
|
||||
.plate {
|
||||
margin: clamp(56px, 7vw, 96px) 0 0;
|
||||
text-align: center;
|
||||
}
|
||||
.plate figure {
|
||||
margin: 0;
|
||||
display: inline-block;
|
||||
max-width: 100%;
|
||||
position: relative;
|
||||
}
|
||||
.plate figure::before, .plate figure::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: 56px; height: 1px;
|
||||
background: var(--accent);
|
||||
top: 50%;
|
||||
}
|
||||
.plate figure::before { left: -68px; }
|
||||
.plate figure::after { right: -68px; }
|
||||
@media (max-width: 1100px) {
|
||||
.plate figure::before, .plate figure::after { display: none; }
|
||||
}
|
||||
.plate img {
|
||||
display: block;
|
||||
width: 100%;
|
||||
max-width: 1180px;
|
||||
height: auto;
|
||||
border: 1px solid var(--rule);
|
||||
background: var(--paper);
|
||||
box-shadow:
|
||||
0 28px 56px -34px rgba(28,25,22,.30),
|
||||
0 6px 14px -8px rgba(28,25,22,.18);
|
||||
}
|
||||
.plate figcaption {
|
||||
font-family: var(--mono);
|
||||
font-size: 11px;
|
||||
letter-spacing: 0.22em;
|
||||
text-transform: uppercase;
|
||||
color: var(--ink-mute);
|
||||
margin-top: 22px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 14px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.plate figcaption .dot { color: var(--accent); }
|
||||
.plate figcaption .b { color: var(--ink); }
|
||||
|
||||
/* ───── Additions (what's new this revision) ──── */
|
||||
|
||||
.additions {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 0;
|
||||
border: 1px solid var(--rule);
|
||||
background: var(--paper-2);
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
@media (max-width: 920px) { .additions { grid-template-columns: 1fr; } }
|
||||
.add {
|
||||
padding: 32px 28px 28px;
|
||||
border-right: 1px solid var(--rule);
|
||||
position: relative;
|
||||
}
|
||||
.add:last-child { border-right: none; }
|
||||
@media (max-width: 920px) {
|
||||
.add { border-right: none; border-bottom: 1px solid var(--rule); }
|
||||
.add:last-child { border-bottom: none; }
|
||||
}
|
||||
.add .badge {
|
||||
font-family: var(--mono);
|
||||
font-size: 10.5px;
|
||||
letter-spacing: 0.20em;
|
||||
color: var(--accent);
|
||||
text-transform: uppercase;
|
||||
margin-bottom: 14px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
.add .badge .num {
|
||||
display: inline-block;
|
||||
border: 1px solid var(--accent);
|
||||
border-radius: 50%;
|
||||
width: 22px; height: 22px;
|
||||
text-align: center; line-height: 20px;
|
||||
font-size: 10px;
|
||||
}
|
||||
.add h3 {
|
||||
font-family: var(--serif);
|
||||
font-style: italic;
|
||||
font-weight: 500;
|
||||
font-variation-settings: "opsz" 60;
|
||||
font-size: 26px;
|
||||
line-height: 1.15;
|
||||
margin: 0 0 12px;
|
||||
letter-spacing: -0.012em;
|
||||
color: var(--ink);
|
||||
}
|
||||
.add p {
|
||||
font-size: 15px;
|
||||
color: var(--ink-soft);
|
||||
line-height: 1.55;
|
||||
margin: 0 0 14px;
|
||||
}
|
||||
.add code {
|
||||
font-family: var(--mono);
|
||||
font-size: 12px;
|
||||
color: var(--accent-deep);
|
||||
background: rgba(122,31,61,.08);
|
||||
padding: 1px 6px;
|
||||
}
|
||||
.add .glyph {
|
||||
position: absolute;
|
||||
top: 24px; right: 22px;
|
||||
font-family: var(--serif);
|
||||
font-style: italic;
|
||||
font-size: 42px;
|
||||
color: var(--accent-soft);
|
||||
line-height: 1;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* ───── Reveal animation ─────────────────────── */
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
@@ -686,8 +812,9 @@ footer .colophon {
|
||||
<nav class="top">
|
||||
<a href="#problem">problem</a>
|
||||
<a href="#pipeline">pipeline</a>
|
||||
<a href="#whats-new">what's new</a>
|
||||
<a href="#gotchas">gotchas</a>
|
||||
<a href="#cases">use cases</a>
|
||||
<a href="#session">session</a>
|
||||
<a href="#install">install</a>
|
||||
<a href="https://gitea.tojo.team/cardinale/screenshot-rename">repo ↗</a>
|
||||
</nav>
|
||||
@@ -837,14 +964,29 @@ footer .colophon {
|
||||
<p style="font-family:var(--mono); font-size:12px; color:var(--ink-mute); letter-spacing:0.1em; text-transform:uppercase; margin-top:20px;">
|
||||
The original timestamp survives unchanged. Sorting still works. The description sits between, set off by em-dashes.
|
||||
</p>
|
||||
|
||||
<div class="plate reveal">
|
||||
<figure>
|
||||
<img src="https://gitea.tojo.team/cardinale/screenshot-rename/raw/branch/main/assets/before-after.png" alt="Plate i — five real renames, including U+202F handling and user-keyword preservation">
|
||||
<figcaption>
|
||||
<span class="bar" style="display:inline-block;width:28px;height:1px;background:var(--ink-mute);"></span>
|
||||
<span>Plate i</span>
|
||||
<span class="dot">·</span>
|
||||
<span class="b">Five real renames</span>
|
||||
<span class="dot">·</span>
|
||||
<span>CleanShot · Apple Screenshot · user-keyword</span>
|
||||
<span class="bar" style="display:inline-block;width:28px;height:1px;background:var(--ink-mute);"></span>
|
||||
</figcaption>
|
||||
</figure>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ───── Receipt ──────────────────────────────────── -->
|
||||
<!-- ───── Receipt (run ii) ─────────────────────────── -->
|
||||
<section>
|
||||
<div class="wrap">
|
||||
<div class="receipt reveal">
|
||||
<div class="head">screenshot-rename · run log · 2026-05-04</div>
|
||||
<div class="head">screenshot-rename · run ii · 2026-05-04 · 196 files</div>
|
||||
<div class="line"><span>source files</span><span class="v">196</span></div>
|
||||
<div class="line"><span>resized to 1568px</span><span class="v">196</span></div>
|
||||
<div class="line"><span>frames extracted (mp4 / pdf)</span><span class="v">9</span></div>
|
||||
@@ -860,10 +1002,74 @@ footer .colophon {
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ───── What's new ───────────────────────────────── -->
|
||||
<section id="whats-new">
|
||||
<div class="wrap">
|
||||
<div class="section-label"><span class="num">04</span>What's new this revision</div>
|
||||
<h2><em>Three</em> additions, one mixed run.</h2>
|
||||
<p class="lede-2">
|
||||
A second batch of files surfaced cases the first run never hit: Apple's own
|
||||
<em>Screenshot</em> filenames, user-typed keyword prefixes, and folders where some
|
||||
files were already renamed. The pipeline now handles all three in a single pass
|
||||
— and the parser learned a new gotcha along the way.
|
||||
</p>
|
||||
|
||||
<div class="additions reveal">
|
||||
<div class="add">
|
||||
<span class="glyph">a.</span>
|
||||
<div class="badge"><span class="num">i</span>multi-prefix</div>
|
||||
<h3>CleanShot <em>and</em> Screenshot, in one run.</h3>
|
||||
<p>
|
||||
The parser now accepts both <code>CleanShot 2026-MM-DD at HH.MM.SS.png</code> and
|
||||
Apple's <code>Screenshot 2026-MM-DD at H.MM.SS PM.png</code>. Mixed folders no
|
||||
longer need two passes — the manifest builder picks up either prefix.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="add">
|
||||
<span class="glyph">b.</span>
|
||||
<div class="badge"><span class="num">ii</span>keyword preservation</div>
|
||||
<h3>Hand-typed prefixes survive.</h3>
|
||||
<p>
|
||||
A file named <code>jojo travel CleanShot 2026-...png</code> carries user knowledge
|
||||
the AI doesn't have. The parser strips the keyword phrase, title-cases it, and
|
||||
prepends it to the AI description — so the new filename reads
|
||||
<em>Jojo Travel Flight Australia Melbourne Flightaware Map Route</em>.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="add">
|
||||
<span class="glyph">c.</span>
|
||||
<div class="badge"><span class="num">iii</span>idempotent re-runs</div>
|
||||
<h3>Re-running won't stack descriptions.</h3>
|
||||
<p>
|
||||
The parser now detects names already in the
|
||||
<code>App - Description - timestamp.ext</code> form and excludes them from the
|
||||
manifest. You can re-run the skill on a partially-renamed folder without the
|
||||
name growing on every pass.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="receipt reveal" style="max-width: 820px;">
|
||||
<div class="head">screenshot-rename · run iii · 2026-05-04 · mixed prefixes</div>
|
||||
<div class="line"><span>source files</span><span class="v">20</span></div>
|
||||
<div class="line"><span>CleanShot</span><span class="v">14</span></div>
|
||||
<div class="line"><span>Apple Screenshot (with U+202F)</span><span class="v">5</span></div>
|
||||
<div class="line"><span>user-prefixed</span><span class="v">1</span></div>
|
||||
<div class="line"><span>already-renamed (skipped)</span><span class="v">0</span></div>
|
||||
<div class="line"><span>plan validated</span><span class="v">20 renames · 0 errors</span></div>
|
||||
<div class="line"><span>file count before / after</span><span class="v">20 = 20</span></div>
|
||||
<div class="line total"><span>renames committed</span><span class="ok">20 ✓</span></div>
|
||||
<div class="line total"><span>files lost</span><span class="ok">0 ✓</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ───── Gotchas ──────────────────────────────────── -->
|
||||
<section id="gotchas">
|
||||
<div class="wrap">
|
||||
<div class="section-label"><span class="num">04</span>The rules that prevent data loss</div>
|
||||
<div class="section-label"><span class="num">05</span>The rules that prevent data loss</div>
|
||||
<h2>Every rule below was <em>paid for</em>.</h2>
|
||||
<p class="lede-2">
|
||||
During development, four files were destroyed by a one-line bash mistake.
|
||||
@@ -880,6 +1086,9 @@ footer .colophon {
|
||||
<li><b>Run renames foreground.</b><code>Bash run_in_background</code> with <code>while read</code> may exit early with no progress. Run via Python in the same shell — <code>os.rename</code> is just a syscall.</li>
|
||||
<li><b>Validate the filename column.</b>Haiku occasionally returns the resized <code>.jpg</code> name instead of the original <code>.png</code>. The plan-builder must try alternate extensions when the claimed source isn't found.</li>
|
||||
<li><b>Preserve the original extension.</b>The pipeline reads from a resized JPEG but renames the original <code>.mp4</code> / <code>.pdf</code>. Write the source extension back into the new name.</li>
|
||||
<li><b>Apple Screenshot files use <code>U+202F</code>.</b>The narrow no-break space sits between the seconds and AM/PM. Haiku echoes it as ASCII space, the lookup misses, and every Screenshot file is dropped from the plan with a misleading <code>NO_DESC</code> error. Normalize on both sides; emit ASCII space in the new name.</li>
|
||||
<li><b>Re-runs must skip already-renamed files.</b>Without an <code>^App - .+ - timestamp.ext$</code> exclusion rule the parser will pile a second AI description into every name on every run. The pipeline detects and excludes them.</li>
|
||||
<li><b>User-typed keyword prefix is signal.</b>A name like <code>jojo travel CleanShot ...</code> carries knowledge the AI doesn't have. Strip the keyword phrase, title-case it, and prepend it to the description before assembly. Don't drop it.</li>
|
||||
</ol>
|
||||
</div>
|
||||
</section>
|
||||
@@ -887,7 +1096,7 @@ footer .colophon {
|
||||
<!-- ───── Use cases ────────────────────────────────── -->
|
||||
<section id="cases">
|
||||
<div class="wrap">
|
||||
<div class="section-label"><span class="num">05</span>Use cases</div>
|
||||
<div class="section-label"><span class="num">06</span>Use cases</div>
|
||||
<h2>What this looks like in <em>practice</em>.</h2>
|
||||
<p class="lede-2">
|
||||
The skill earns its keep when "Spotlight will find it" stops being true. Four scenarios where it has.
|
||||
@@ -929,10 +1138,38 @@ footer .colophon {
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ───── Session (plate ii) ───────────────────────── -->
|
||||
<section id="session">
|
||||
<div class="wrap">
|
||||
<div class="section-label"><span class="num">07</span>A session, end to end</div>
|
||||
<h2>What it <em>looks like</em> when you ask.</h2>
|
||||
<p class="lede-2">
|
||||
The skill is conversational at the top, mechanical underneath. You ask in plain
|
||||
English; ten Haiku agents fan out in a single round-trip; Python validates the
|
||||
plan and applies it under the file-count audit. The whole thing fits on one page.
|
||||
</p>
|
||||
|
||||
<div class="plate reveal">
|
||||
<figure>
|
||||
<img src="https://gitea.tojo.team/cardinale/screenshot-rename/raw/branch/main/assets/session.png" alt="Plate ii — Claude Code session showing user prompt, parallel Haiku fan-out across ten batches, and the run receipt">
|
||||
<figcaption>
|
||||
<span class="bar" style="display:inline-block;width:28px;height:1px;background:var(--ink-mute);"></span>
|
||||
<span>Plate ii</span>
|
||||
<span class="dot">·</span>
|
||||
<span class="b">A session, end to end</span>
|
||||
<span class="dot">·</span>
|
||||
<span>10 agents · ~3 min · zero loss</span>
|
||||
<span class="bar" style="display:inline-block;width:28px;height:1px;background:var(--ink-mute);"></span>
|
||||
</figcaption>
|
||||
</figure>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ───── Install ──────────────────────────────────── -->
|
||||
<section id="install">
|
||||
<div class="wrap">
|
||||
<div class="section-label"><span class="num">06</span>Install & run</div>
|
||||
<div class="section-label"><span class="num">08</span>Install & run</div>
|
||||
<h2>Three commands, <em>one folder</em>.</h2>
|
||||
<p class="lede-2">
|
||||
The skill installs as a Claude Code skill. Once cloned into <code>~/.claude/skills/</code>, it
|
||||
|
||||
Reference in New Issue
Block a user