Skip to content

feat: Add RClone Queue widget + code-review fixes - #523

Open
elhoim wants to merge 13 commits into
sirmalloc:mainfrom
elhoim:worktree-rclone-dropbox-queue-counter
Open

feat: Add RClone Queue widget + code-review fixes#523
elhoim wants to merge 13 commits into
sirmalloc:mainfrom
elhoim:worktree-rclone-dropbox-queue-counter

Conversation

@elhoim

@elhoim elhoim commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Adds RCloneQueueWidget: a status-line widget that tails the rclone VFS log (default remote dropbox) and surfaces the pending-upload queue length (RClone: <N> / n/a), with an in-TUI remote-name editor.
  • Includes 5 follow-up fixes from a /code-review high pass on this branch (one commit each):
    1. readLogTail now uses the actual bytesRead from fs.readSync instead of assuming the buffer was fully populated (avoids mixing stale/zero bytes into the tail read on a logrotate race).
    2. Added a persistent on-disk cache (~/.cache/ccstatusline/rclone-queue-cache.json, mirroring src/utils/git.ts's pattern) so the 15s cache actually helps — ccstatusline's piped mode runs as a fresh process per refresh, so the in-process-only cache never survived across invocations.
    3. getRemoteName now falls back to the default remote for a blank/whitespace-only metadata.remoteName, matching the editor's own save-time fallback.
    4. getRcloneLogPath now sanitizes remoteName via path.basename, preventing directory-traversal segments from escaping ~/.cache/rclone/.
    5. The remote-name editor now uses grapheme-aware cursor/edit indexing (same Intl.Segmenter helpers as CustomText.tsx), so a multi-code-unit character (e.g. an emoji) can't split a surrogate pair on backspace/delete.

Test plan

  • bun test src/widgets/__tests__/RCloneQueue.test.tsx — 43/43 pass
  • bun test (full suite) — all failures are pre-existing, unrelated Ink-input-timing flakiness (timezone-editor, PowerlineThemeSelector, TerminalWidthMenu, usage-fetch), confirmed non-deterministic across repeated runs and untouched by this diff
  • bun run lint — clean

🤖 Generated with Claude Code

@elhoim elhoim changed the title [feat] Add RClone Queue widget + code-review fixes feat: Add RClone Queue widget + code-review fixes Jul 22, 2026
elhoim and others added 13 commits August 2, 2026 22:00
Covers data source (log-tailing since --rc isn't enabled), widget
behavior, caching strategy, and registration/testing plan.
Discard a partial first line in the 64KB read window instead of
leaving the edge case unspecified.
4 tasks: data layer (log tail + cache), widget class, remote-name
editor, registration. TDD throughout, using bun test (not bunx vitest,
which fails to load this repo's vitest.config.ts in this environment).
The previous test implementation batched all backspace writes into a single
call using '\b'.repeat(7), causing Ink to coalesce them into one 'data' event
that contained control characters. This was not recognized as individual
key.backspace events, so the backspaces were ignored.

Fixed by writing each backspace separately with an await/flush between each
write, mirroring how a real keystroke stream arrives. This ensures each
backspace is received as a separate key.backspace event by Ink's useInput hook.

Fixes:
- "saves the typed remote name on Enter" test now actually deletes the
  pre-filled text and correctly saves "gdrive" instead of "dropboxgdrive"
- "falls back to the default remote name when saved empty" test now exercises
  the empty-string fallback logic instead of passing vacuously

All 35 tests now pass (previously 34 pass / 1 fail).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…er fill

fs.readSync can return fewer bytes than requested (e.g. the log file
shrinks between fstatSync and readSync during a logrotate
copytruncate). readLogTail ignored the return value and always
stringified the full pre-allocated buffer, silently mixing stale
zero-fill bytes into the most recent (tail) portion of the parsed log
text.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…oss statusline invocations

ccstatusline's piped mode runs as a fresh process per statusline
refresh, so the in-process Map cache was always empty on every
invocation and never actually avoided the 64KB tail-read + regex
re-parse it was meant to save, contrary to the design's stated intent.
Mirror src/utils/git.ts's pattern: pair the in-process cache with a
persistent JSON cache under ~/.cache/ccstatusline/, same 15s TTL, so
a fresh process can reuse a still-fresh reading instead of re-parsing
the log.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…teName is blank

getRemoteName used ?? so it only fell back to DEFAULT_REMOTE_NAME for
null/undefined metadata, not for an empty or whitespace-only string
(a value the metadata schema happily accepts). That diverged from the
editor's own save-time fallback, so a blank remoteName reaching
metadata through any other path (manual settings edit, future
import/migration) resolved to a log path that could never exist.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ide the rclone cache dir

The remote-name editor only blocked control characters, so a
remoteName containing "../" segments made getRcloneLogPath resolve
outside ~/.cache/rclone/, letting the widget tail-read and display
digits from arbitrary *.log files reachable by relative traversal.
path.basename strips any directory separators before the path is
joined, so the result can never escape the intended cache directory.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ote-name editor

RCloneRemoteEditor indexed the remote-name string with plain UTF-16
code units (text[i], slice(pos-1)), unlike CustomText.tsx's editor for
the same interaction. A character outside the BMP (e.g. an emoji) could
split a surrogate pair on backspace/delete, corrupt the cursor
highlight, and leave an unpaired surrogate saved into
metadata.remoteName. Reuse the same Intl.Segmenter-based
grapheme-index helpers CustomText.tsx already uses.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@elhoim
elhoim force-pushed the worktree-rclone-dropbox-queue-counter branch from 5f63e54 to 06d0c7e Compare August 2, 2026 22:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant