feat: add GitHub actions to comment, merge, close, and reopen - #42
Merged
Conversation
Adds ResourceAction/MergeMethod/ActionKind domain types with pure availability gating, an ActionContext (node id, viewerCanUpdate, locked) fetched with every resource, repository merge-method flags on pull requests, and a GraphQL mutations adapter for comment, close, reopen, and merge.
Pure text-and-cursor state for the in-app comment editor: char-accurate inserts and deletes across multibyte text, CRLF-normalizing paste, width-aware wrapping shared by rendering and click mapping, sticky vertical movement through wrapped rows, and cursor-following scroll.
Wires write actions end to end: an actions footer button and Shift-A menu listing only the actions the viewer can take, confirm modals for close/reopen and merge (with an allowed-merge-method picker), the multiline comment composer modal with clickable submit/cancel buttons and click-to-place cursor, bracketed paste support, and a background mutation pipeline that mirrors the fetch runtime: spawn, deliver over a channel, finalize on the main thread, refresh on success. Every operation is reachable by mouse and keyboard through the same reducer arm.
A mutation finishing while the user had switched or closed tabs used to start the refresh against whichever tab was active, letting the refetched resource overwrite an unrelated tab. The refresh now runs inside the originating tab via apply_to_resource_tab, is skipped when that tab is gone or shows a different resource, and a closed tab can no longer leave the app with a stuck pending action.
Confirmation and menu shortcuts now require unmodified keys, so terminal control sequences like Ctrl-Y can no longer merge or close a resource. The comment composer is snapshotted with its resource tab, which keeps an in-flight comment on one tab from discarding an unsent draft on another and preserves drafts across tab switches.
…reshes Freezes the composer while its comment is posting so edits typed after Ctrl-S cannot be silently dropped when the posted snapshot closes the draft, with an explicit hint line while locked. Post-action refreshes that collide with an in-flight fetch are now queued on the app state and started once loading goes idle, instead of relying on the deferred-refresh flag that the older fetch's completion wipes.
Completion messages and draft handling now apply only when the originating tab still shows the acted-on resource; otherwise the pending flag is cleared without touching the new resource's status.
A replacement draft opened while an earlier comment posts now survives that comment's completion; the close is tied to the submitted body instead of whichever composer happens to be open.
osolmaz
force-pushed
the
feat/resource-actions
branch
from
July 23, 2026 12:31
e172a9e to
92b6a5f
Compare
An unrelated draft on another tab (or a replacement draft) stays editable while a comment posts; the posting lock and hint apply only to the composer whose body was submitted.
osolmaz
force-pushed
the
feat/resource-actions
branch
from
July 23, 2026 12:47
f85bcbb to
525296c
Compare
Adds boundary tests for composer editing, per-key reducer coverage, exact modal geometry and style assertions, merge-flag parsing, and paste event mapping. Restructures scroll_cursor_into_view into clamp form and simplifies composer layout guards so no equivalent mutants remain, and declares narrow cargo-mutants exclusions for the three terminal/network boundary functions with justifications.
osolmaz
force-pushed
the
feat/resource-actions
branch
from
July 23, 2026 12:47
525296c to
72403ad
Compare
Rewrites cursor_visual_in around a single last-matching-row rule (removing the redundant row_holds_cursor mechanism whose overlap made its mutants unobservable), drops the dead padding loop in the exact- height menu modal, converts wheel direction to an enum, reworks the deferred-refresh queue so entries survive a still-loading race instead of being dropped, and adds the missing negative tests for modified-key guards, overwide-char wrapping, and confirm-button alignment.
Ctrl+Alt+S no longer posts a comment; only an unmodified Ctrl chord submits or cancels. Combining marks and other zero-width code points now count as zero columns in wrapping, cursor math, and click mapping, matching how terminals render them.
Owner
Author
Final reportMerged as ReviewSix Codex review rounds (
Skipped (with rationale): tracking the posting composer by identity instead of body — only observable when a replacement draft is byte-identical to the comment that just posted, in which case nothing is lost. Validation
Mutation testing also surfaced one real bug beyond review findings: deferred refresh entries could be dropped when a fetch raced in; the queue now retains them. |
Owner
Author
|
Testing comments inside ghzinga |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
ghzinga could only watch a PR or issue; acting on it still meant switching to the browser.
This change adds GitHub write actions to the TUI: comment, merge, close, and reopen, right from the viewer.
Every step works with the mouse alone or the keyboard alone, comments are written in an in-app multiline composer, and the view refreshes from GitHub after each action so it never shows guessed state.
Actions only appear when the viewer is authenticated and actually allowed to act, and the server stays the final authority.
What Changed
The feature follows the existing Elm-style architecture: pure domain types and reducer logic, a GraphQL adapter for the writes, and a background pipeline that mirrors how fetches already work.
src/domain/action.rs):ResourceAction,MergeMethod,ActionKind, and pureavailable_actionsgating based on a newActionContext(GraphQL node id,viewerCanUpdate,locked) plus repository-allowed merge methods, all fetched with the existing resource query.src/github/mutations.rs): GraphQL mutations foraddComment,closeIssue/reopenIssue,closePullRequest/reopenPullRequest, andmergePullRequest, reusing the existing authenticated transport. The unauthenticated REST fallback stays read-only.[actions]footer button andAkey open a context-sensitive menu; close/reopen and merge get confirm modals (merge with a method picker limited to what the repo allows); comments get a multiline composer modal with clickable submit/cancel buttons, click-to-place cursor, wheel scroll, and width-aware wrapping shared between rendering and input mapping (src/app/composer.rs).src/fetch.rs,src/runner.rs): submissions spawn in the background, results come back over a dedicated channel, state is finalized on the main thread, and a successful action triggers an automatic refresh. Failures surface the API error in the status line and keep an unsent comment draft.HitTargetand a key binding.Testing
All local gates pass, and the new flows are covered by reducer, render, adapter, and pipeline tests.
cargo fmt --check,cargo check --all-targets,cargo clippy --all-targets --all-features -- -D warningscargo test: 597 tests pass (573 lib + architecture + CLI), including ~50 new tests for action availability, menu/confirm/composer key and click flows, paste handling, mutation guards, and modal rendering with hit-area assertionscargo llvm-cov --fail-under-lines 85: passes at 85.86% linescargo audit,slophammer-rs check .anddry .(0.4.0): cleancargo mutants --in-diff(executing run) was started locally; the CI gate runs the same check on this PRRisks
The riskiest part is state drift after a write, and the design avoids it by never patching state locally: after any successful action the resource is refetched from GitHub.
viewerCanUpdate,locked, and allowed merge methods, so a rare 403/409 is still handled gracefully.