Commit 663d13d
authored
feat: --gif mode (frames → inline animated GIF) (#33)
* feat: add gifenc package + go-quantize dep (frames to animated GIF)
* test: lock delay-rounding and zero-size-frame in gifenc
* feat: add assembleGIF cli helper (frame files to temp gif)
* fix: reject bare "." / ".." in assembleGIF opts.name
filepath.Base has no separator to strip from a bare "." or "..", so
it passed them through unchanged and Join resolved outside the temp
dir. Fall back to the default name for those two literal cases and
pin the fix with a regression test.
* fix: reject bare separator in assembleGIF opts.name too
filepath.Base of an all-separator path returns a single separator
unchanged (nothing to strip), so a "/" name passed the existing "."/
".." guard and Join collapsed it onto tmpDir itself, failing the
write with an is-a-directory error instead of falling back to
clip.gif. Extend the guard and pin it with a test case.
* feat: add --gif mode to upload flow
Wires --gif/--delay/--colors flags into runWithDeps and runUpload so
the CLI can collapse ordered frame files into one animated GIF and
upload that instead of the individual frames. --name now also labels
the --gif output basename and is routed through validateName (not
just assembleGIF's internal filepath.Base guard). --gif rejects stdin
input, and --colors/--delay are validated at the boundary rather than
silently coerced.
* fix: bound --delay and require .gif extension on --gif --name
Council review flagged two real gaps: --delay had no upper bound
(gifenc rounds it into a 16-bit GIF centisecond field, so values above
655350ms would silently wrap and corrupt playback speed instead of
erroring), and --gif --name didn't require a .gif extension (comment.go
picks inline-vs-link rendering purely from the uploaded basename's
extension, so a mismatched name would defeat --gif's whole point).
Three other findings from the same review were checked against the
actual code and refuted — no fix needed, see task-3-report.md.
* test: lock the --delay=655350 boundary for --gif
Adds the missing upper-boundary case for the --delay range check
(655350ms accepted) so an off-by-one in the > vs >= comparison would
be caught. The second council review round's other findings (claiming
the .gif-extension guard on --name was reachable with an empty name,
without --gif, or via stdin) were all refuted: the guard is nested
inside opts.gif && opts.name != "", and --gif + stdin is already
rejected earlier in the same function — both already covered by
passing tests.
* test: avoid cap() shadowing, decode-verify max-delay gif output
Renames the cap variable to captured across the added gif validation
tests (the brief's own verbatim Step-1 test still uses cap and is
left untouched). Also strengthens TestRun_GifMode_AcceptsMaxDelay to
decode the produced GIF and assert the encoded delay is exactly 65535
centiseconds, via a gifBytesCapturingGitClient that reads the pushed
file's bytes inside PushAttachments (assembleGIF's deferred cleanup
removes the temp file before runWithDeps returns, so bytes can't be
read back afterward).
* fix: drop unrequested .gif-name restriction (false rationale; keep --delay bound)
* feat: add frame-cap and size-ceiling guards to --gif
* test: add dedicated sampleEvenly boundary coverage
* test: use slices.Equal and cover nil/empty in sampleEvenly test
* fix: clamp gif delay to uint16 max to prevent re-encode-path wrap
The per-frame delay is written to the GIF stream as a uint16 by
encoding/gif; only the low side was clamped, so a delay above 65535
centiseconds (e.g. doubled delay on the --size-ceiling re-encode path)
silently truncated to a wrong, too-fast value instead of erroring or
saturating.
* fix: check DecodeAll error in new upper-clamp test
Council review flagged the ignored gif.DecodeAll error: a malformed
GIF would leave g nil and panic on the next line instead of failing
cleanly. Declined the accompanying boundary-value (65535 vs 65536 cs)
suggestion as out of scope for this fix.
* fix: document guard flags, reject negative guard values, clarify re-encode comment
Council review found README missing docs for --max-frames/--size-ceiling,
inconsistent boundary validation (negative values silently treated as
"disabled" instead of rejected like --colors/--delay), and an inaccurate
comment claiming delay-doubling always preserves playback speed.
Claude-Session: https://claude.ai/code/session_01XGKirUaMS7kttgBwCzcJ3B1 parent 0a0e9d5 commit 663d13d
9 files changed
Lines changed: 954 additions & 4 deletions
File tree
- internal
- cli
- gifenc
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
50 | 50 | | |
51 | 51 | | |
52 | 52 | | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
53 | 59 | | |
54 | 60 | | |
55 | 61 | | |
56 | 62 | | |
57 | 63 | | |
58 | 64 | | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
59 | 89 | | |
60 | 90 | | |
61 | 91 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | 3 | | |
| 4 | + | |
| 5 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
0 commit comments