Pressing Back on the "Connect to Nostr" first-connect dialog resolves to
AppConnectResult.Cancelled, which added the app's coordinate to an in-memory
`sessionCancelled` set. That set suppressed every future connect prompt for the
entire broker lifetime, so a later request — e.g. the user re-clicking "login"
in the in-app browser — was silently denied and the dialog never reappeared.
Because a Cancel persists nothing, the app also never showed up in Connected
Apps, leaving the user with nothing to clear to recover.
Replace the permanent suppression with a short, self-clearing cooldown
(`cancelledUntil` map): a Cancel suppresses re-prompts only briefly so the
burst of requests a page/napplet fires on load doesn't relaunch the dialog per
request, while a deliberate retry seconds later prompts again. The clock is
injectable for deterministic tests.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HDcts4HzwSff4fy6oSVA6D
The full-screen direct-WebView browser (NappletBrowserActivity, launched when
you type an address) and the sandbox host (NappletHostActivity) used an older
console design that diverged from the embedded tabs' Compose chrome:
- The Console row in NappletControlSheet was a plain action row and the bottom
pull-up grabber was always visible. Make Console a Switch toggle (like the Tor
row / the Compose TopControlSheet), and hide the whole NappletConsolePanel
until the toggle is on — turning it on reveals the sheet already pulled up,
mirroring BottomConsoleSheet.
- The console grabber/panel sat at elevation 0 while the top sheet's panel is at
6dp, so an open top sheet drew over the console when they overlapped (e.g. in
landscape). Elevate the console sheet above the top sheet so its pull tab and
log render on top, matching the Compose layer where BottomConsoleSheet is
composed after TopControlSheet.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A9kfpNrBRB8WJNi67NHGGX
Now that observeEvents re-emits the whole matching list each time,
GitStatusIndex and GitPullRequestUpdateIndex no longer need the imperative
launch/collect-into-MutableStateFlow wrapper carried over from the old
newEventBundles version. Each is now a single observeEvents().map { reduce }
.stateIn(scope, Eagerly, null) — dropping startIfNeeded(), the AtomicBoolean
double-start guard, and the MutableStateFlow/asStateFlow pair.
Eagerly (not WhileSubscribed) is required: callers read .value synchronously
(isClosedOrResolved, the feed filters, the home open-count derivations) and
must not see a stale map when nobody is collecting. All startIfNeeded() call
sites removed; stateIn shares one upstream subscription across collectors.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DpNmN8CvP6HnEsdTGAjVUr
Replace the third "Repositories" tab on the default bookmark screen with a
dedicated entry on the bookmark-lists screen, mirroring how Pinned Notes
works: a row in ListOfBookmarkGroupsFeedView that opens its own
BookmarkedRepositoriesScreen via the new Route.BookmarkedRepositories.
The row shows the bookmarked-repo count from
gitRepositoryListState.publicRepositoryAddressSet; the screen renders the
BookmarkRepositoriesFeedViewModel feed (moved to a repositories/ package),
invalidates on bookmark changes, and preloads any uncached repo
announcements via the EventFinder. Reverts the tab added to
BookmarkListScreen.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DpNmN8CvP6HnEsdTGAjVUr
- GitStatusIndex now subscribes to a kind-1630..1633-filtered
LocalCache.observeEvents instead of LocalCache.live.newEventBundles,
matching the GitPullRequestUpdateIndex change: the indexed observable
seeds from the cache index and re-emits the full list on each new status
event, so the manual onStart full-cache scan and per-bundle type
filtering are gone and the collector just reduces to latest-per-target.
- Bookmark screen gains a third "Repositories" tab listing the user's
bookmarked (NIP-51 kind 10018) git repositories. New
BookmarkRepositoriesFeedFilter resolves the public repository address set
to addressable notes (newest first); the screen invalidates it on
publicRepositoryAddressSet changes and preloads any uncached repo
announcements via the EventFinder.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DpNmN8CvP6HnEsdTGAjVUr
GitPullRequestUpdateIndex now subscribes to a kind-1619-filtered
LocalCache.observeEvents instead of LocalCache.live.newEventBundles. The
indexed observable seeds its matching set from the cache index (via init())
and re-emits the full list on each new PR update, so the manual onStart
full-cache scan and the per-bundle type filtering over every event of every
kind are both gone. The collector just reduces the list to the
latest-per-parent map. PR updates are rare, so recomputing the whole map per
emission is cheaper than scanning every bundle.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DpNmN8CvP6HnEsdTGAjVUr
- Top-bar repo bookmark toggle now switches between BookmarkAdd (with +)
and Bookmark glyphs instead of two identical glyphs, so the icon
visibly changes shape (not just tint) when starred/unstarred.
- Add a thin HorizontalDivider after the ReactionsRow on the repo home.
- Code browser: opening/closing a file or changing folders swaps the
scrollable in place, landing the new view at the top with no scroll
delta, which left the disappearing top bar stranded at its hidden
offset over a blank band. Expose the scaffold bar state via
LocalDisappearingBarState and reset it to visible on each in-place view
change.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DpNmN8CvP6HnEsdTGAjVUr
The commonMain GitRepositoryListState used Dispatchers.IO without importing
the multiplatform kotlinx.coroutines.IO extension, so it resolved to the
JVM-only member and broke the iOS native compile
(:commons:compileKotlinIosSimulatorArm64). Matches BookmarkListState.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DpNmN8CvP6HnEsdTGAjVUr
- Converts the New Issue composer from an AlertDialog to a dedicated screen
(new Route.GitRepositoryNewIssue + GitNewIssueScreen with its own top bar
and a Create action). The Issues FAB now navigates to it.
- The extended FAB rendered square because the app theme sets shapes.large
(the extended-FAB default shape) to 0.dp; pin an explicit RoundedCornerShape.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DpNmN8CvP6HnEsdTGAjVUr
- Repos that can't be cloned over http(s) (e.g. Iris's htree://) now show a
"Hosted externally" notice with an open-in-browser link instead of an empty
dashboard — on the home, the Code screen, and the feed repo card.
- Removed the "Maintained by" row from the project home.
- Tighter home section spacing (12 → 8dp) and a smaller bottom padding on the
feed repo card so the last-commit line sits closer to the reaction row.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DpNmN8CvP6HnEsdTGAjVUr
- Snapshot cache: a process-wide GitRepoSnapshotCache keyed by repo address.
The browser ViewModel serves an already-fetched default-branch snapshot
synchronously, so the stats render in share-to-image and don't re-fetch when
switching screens.
- Issues/PR screens: filter chips now live inside the disappearing top bar
(via the scaffold's belowBar slot) so they hide with it instead of leaving a
static black band; the feed uses normal content padding.
- New issue is now an extended FAB on the Issues screen.
- Top bar shows the repo description as a single-line subtitle under the name;
removed the duplicate description from the home body.
- Tighter spacing: home sections, code header rows (branch row / search /
breadcrumb).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DpNmN8CvP6HnEsdTGAjVUr
The find-or-create wizard can surface a wallet the user previously
DELETED — a relay that missed the kind:5 still serves the kind:17375 to
the crawl. adoptDiscoveredWallet rebroadcast that event verbatim (same id,
same created_at), which loses to the prior NIP-09 deletion two ways:
DeletionEvent.build emits both an `e` tag (old id) and an `a` tag (the
replaceable 17375:pubkey: address), so relays reject the duplicate id and
re-delete every version with created_at <= the deletion's the moment the
kind:5 propagates back — on relays and in our own LocalCache. The
"reactivated" wallet would then silently vanish.
Adopt now re-signs a FRESH kind:17375 + kind:10019 (via publishWalletEvents)
with the discovered wallet's own mints and P2PK key. A new id escapes the
`e`-tag delete and created_at=now escapes the `a`-tag delete, while the
same key + mints preserve the nutzap address and all recoverable funds
(the NUT-13 seed derives from the key, not the event id). Falls back to a
verbatim rebroadcast only if the wallet can't be decrypted.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EqmMR2QiULS5QGosSgSQAe
Deleting the Cashu wallet popped back to CashuWalletScreen, which on an
empty wallet auto-launches the find-or-create wizard — so the user was
funneled straight back into creating the wallet they just deleted.
Navigate to the top-level Wallet hub (Route.Wallet) via newStack instead,
which pops the Cashu screens off the back stack so the wizard never
composes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EqmMR2QiULS5QGosSgSQAe
In the find-or-create wizard, choosing "Create a new wallet" jumped
straight to the mint manager. Add a short celebratory interstitial first:
an animated check badge springs in (bouncy overshoot) behind an expanding
pulse ring, "Wallet Created" + "Now pick a few mints to host your sats"
fade up, a haptic fires, and a "Pick mints" button continues to the mint
selection.
The screen is purely presentational — the kind:17375 still isn't
published until the user adds a mint on the next screen — so "Pick mints"
uses popUpTo to replace the interstitial in the back stack, avoiding an
awkward return to the celebration.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EqmMR2QiULS5QGosSgSQAe
- Recent-activity rows navigate to the issue/PR they represent (routeFor).
- The last-commit strip is now tappable: on the home it opens the Code
screen; on the feed repo card it opens the repository.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DpNmN8CvP6HnEsdTGAjVUr
Home screen:
- Nav cards: tighter vertical spacing; badges now count only OPEN issues/PRs,
derived from the live GitStatusIndex (started on the home so the split is
correct without visiting the Issues screen first).
- Moved the reaction row to after the recent-activity pulse.
- Added the standard 3-dot note menu (MoreOptionsButton) to the top bar.
Feed card (RenderGitRepositoryEvent):
- Replaced the web/clone links with the same stat tiles + language bar +
last-commit strip used on the home, loaded from a lazily-fetched shallow
snapshot. (Factory made internal so the card can build the browser VM.)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DpNmN8CvP6HnEsdTGAjVUr
- Social bar now renders the app's canonical ReactionsRow (reply, boost,
like, zap, zapraiser, reaction gallery) instead of a bespoke subset, so
the repository announcement behaves exactly like any other note.
- Code browser file rows: replace the heavy 32dp boxed icon with a plain
20dp icon and tighten spacing/padding, reducing the oversized horizontal
gap before the file name.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DpNmN8CvP6HnEsdTGAjVUr
The owner avatar + project name was duplicated as the home's first content
line and the top-bar title. Consolidates it into the top bar: a small owner
avatar (tappable to the profile) + project name. The home hero now carries
only the description and topic/fork chips.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DpNmN8CvP6HnEsdTGAjVUr
The repositories feed never special-cased TopFilter.Mine, so it inherited
the shared Mine→all-follows fallback — "Mine" behaved identically to "All
Follows", making both selectors look unresponsive when toggled.
Mirrors the music/badges/communities/nsites pattern:
- GitRepositoriesFeedFilter: when the list is Mine, match repositories
authored by the logged-in user (feed + applyFilter).
- GitRepositoriesSubAssembler: when the list is Mine, query the user's own
repositories by author against their outbox relays (new
filterGitRepositoriesMine), bypassing the follow-list machinery.
"All Follows" already routed through the standard follows filter; it now
visibly differs from "Mine".
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DpNmN8CvP6HnEsdTGAjVUr
The mint picker (CashuMintsScreen) already renders NIP-87 directory
suggestions, but in the find-or-create wizard's "create a new wallet"
path they showed up empty while the edit-mints screen had them.
Cause: openMintDirectory() subscribed the directory against a one-shot
snapshot of the account's outbox relays (acc.outboxRelays.flow.value). A
freshly-restored account reaching the create path often still has its
relay lists loading, so the snapshot was empty, hit
CashuMintDirectoryState's empty-relay early-out, and never retried.
Outbox-only relays also don't reliably carry the broad NIP-87 mint
announcements.
Fix: subscribe reactively to the union of the user's OUTBOX relays and
their INDEXER relays. The flow re-subscribes the moment relays arrive,
and indexer relays aggregate NIP-87 mint data and fall back to a curated
default set — so the picker is populated even for a brand-new user with
no wallet and no recommendations of their own yet.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EqmMR2QiULS5QGosSgSQAe
Removes the old boxed "Overview" section block (About/Links/Topics/
Maintainers cards) that was stacked on top of the new dashboard, which made
the home read as two designs. Its useful content now lives in the dashboard
language:
- New RepoHero: owner avatar + "name / project" title (GitHub-style), with
description and topic chips.
- RepoMaintainersRow: a compact maintainer avatar cluster.
- Drops the Links card (clone/web URLs) as low-value.
Deletes GitRepositoryOverview.kt.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DpNmN8CvP6HnEsdTGAjVUr
Replaces the read-only zap/reaction/comment counts with the canonical
ZapReaction / LikeReaction / ReplyReaction actions, so the repository home
now supports one-tap zaps (with the amount dialog), reactions, and replying
to the repo announcement — each with its live counter.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DpNmN8CvP6HnEsdTGAjVUr
Turns the repository home into a data-rich dashboard combining git facts
with the Nostr social layer:
- Social row: live zap / reaction / comment counts on the repo announcement
(via observeNoteZaps/Reactions/ReplyCount).
- Stat tiles: branches, tags, file count, and last-updated relative time.
- Language breakdown bar: proportional colored segments computed from the
snapshot's file tree by extension (new GitRepoSnapshot.walkFileNames()).
- Last-commit strip: tip commit summary, author, time and short SHA, exposed
up-front via the new GitRepoSnapshot.tipCommit (no extra fetch).
- Nav cards now show open issue / PR counts.
- Recent-activity pulse: newest issues/patches merged from the feeds.
quartz: GitRepoSnapshot gains tipCommit (parsed in open()) and
walkFileNames() to enumerate blob paths for the language bar.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DpNmN8CvP6HnEsdTGAjVUr
Replaces the five-tab repository screen (Readme/Code/Overview/Issues/Patches)
with a scrollable Project Home and three dedicated screens:
- GitRepositoryScreen is now the home: repository facts (from the former
Overview), Code / Issues / Pull Requests navigation cards, and the README
rendered inline — all in one scroll.
- New GitRepositoryCode/Issues/Pulls screens, each owning its own
disappearing top bar, reached via new addressable routes.
This removes tab overflow and the swipe-paging between heavy screens, and
fixes the per-tab header issues structurally: the Code browser's branch/tag
and file-search header now live inside the scroll list, so they track the
disappearing top bar instead of staying pinned below it.
README and Overview were refactored into embeddable, non-scrolling sections
(GitReadmeSection, GitRepositoryOverviewSections) so the home can compose
them in a single scroll container.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DpNmN8CvP6HnEsdTGAjVUr
The KMP lifecycle-viewmodel artifact used by commons doesn't expose the
create(Class<T>) ViewModelProvider.Factory override (only the desktop/JVM
target hit this), so the factory now lives in amethyst alongside the
viewModel() call, mirroring the NestViewModel pattern. The commons
ViewModel keeps only platform-agnostic state.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DpNmN8CvP6HnEsdTGAjVUr
The status feed drew its filter-chip header at the top of the content area
(under the disappearing top bar) while the feed's LazyColumn separately
re-applied the full bar-height inset as content padding, leaving an empty
band above the first item. The header now consumes the scaffold top inset
itself and the inner feed renders with the top inset zeroed, matching the
Code tab's padding pattern.
Also adds a "Mine" option to the git repositories top-nav filter so the
feed can show only the logged-in user's own repositories. The feed side
already resolves TopFilter.Mine generically; this just exposes it via a
dedicated gitRepositoryRoutes catalog (kind3 + Around Me + Global + Mine).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DpNmN8CvP6HnEsdTGAjVUr
Relocates the two app-agnostic pieces of the NIP-34 code browser into commons
so the desktop front end can reuse them verbatim:
- GitRepositoryBrowserViewModel (+ GitBrowseState) → commons jvmAndroid
nip34Git package. Pure StateFlow ViewModel over quartz's GitHttpClient;
no Android/AccountViewModel/INav dependency.
- CodeHighlighter → commons commonMain nip34Git/ui. Pulls the Apache-2.0
dev.snipme:highlights dependency into commons commonMain.
Amethyst composables now import both from commons. The screen-level
composables stay app-side, matching the commons convention that shared UI
never takes AccountViewModel/INav.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DpNmN8CvP6HnEsdTGAjVUr
Adds NIP-51 (kind 10018) repository bookmarking with a star toggle in the
git repository top bar, backed by a new GitRepositoryListState in commons.
Removal rebuilds the public tag set and re-signs so encrypted private
bookmarks are preserved without decryption.
Adds a label-filter chip row and open/closed item counts to the Issues and
Patches & PRs status feeds. The active feed's distinct labels drive the
chips; selecting one filters the rendered list, and a stale selection is
dropped when switching status.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DpNmN8CvP6HnEsdTGAjVUr
Add a History action to the Code tab that shows a git-log-style list of the
branch's recent commits, and opens the diff a commit introduced (vs its first
parent) with the shared diff viewer.
quartz: GitCommit + a commit-object parser (skips multi-line headers like a
signed gpgsig), and GitHttpClient.loadHistory() — a shallow tree:0 fetch
(commits only) walked most-recent-first. The fetch filter is generalized from
a blob:none boolean to a filter spec so tree:0 can be requested. Unit-tested
against a real SSH-signed commit object.
amethyst: GitCommitLog (log list + per-commit diff), a History button on the
repo header, and ViewModel hooks (loadHistory / commitDiff).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DpNmN8CvP6HnEsdTGAjVUr
Emphasize what changed inside a modified line, not just the whole line.
A new pure IntralineDiff helper pairs each delete with its corresponding
add within a hunk and trims the common prefix/suffix to the differing
middle; GitDiffView layers a stronger add/delete background over just that
character span, on top of the existing syntax highlighting.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DpNmN8CvP6HnEsdTGAjVUr
Pull requests reference a clone URL + commit rather than embedding a patch,
so their changes were invisible. Now the app computes and renders them.
- quartz: a pure Myers O(ND) line-diff (LineDiff) producing the same
GitDiffHunk model the embedded-patch parser uses, unit-tested against git
-U3 output and with a reconstruction property check.
- quartz: GitHttpClient.computeDiff(cloneUrl, head, base?) — fetches both
commit trees, finds changed files by oid, batch-fetches the differing
blobs and line-diffs each into a ParsedPatch (base = merge base, or HEAD).
- amethyst: a "View changes" section on the PR card that loads the diff over
the git client and renders it with the shared GitDiffView.
- amethyst: GitStatusActions generalizes the issue open/close controls to
issues, patches and PRs — patches/PRs additionally get "Mark merged"
(GitStatusAppliedEvent). Visible to the author and repo owner/maintainers.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DpNmN8CvP6HnEsdTGAjVUr
Add an owner-only Edit action to the git repository screen's top bar that
opens a settings dialog. Because the repository event (kind 30617) is
addressable, saving republishes it under the same d-tag — preserving the
earliest-unique-commit, relays, maintainers and personal-fork flag — while
letting the owner edit the name, description, clone/web URLs and topics.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DpNmN8CvP6HnEsdTGAjVUr
Code browser:
- Branch & tag switching: the client now exposes all refs from ls-refs;
a branch/tag picker on the repo header reloads the tree at the chosen
ref (GitRepositoryBrowserViewModel.switchRef).
- In-tree filename search: a search field filters the whole tree
(GitRepoSnapshot.searchFiles) and shows matching paths.
- Image preview: png/jpg/gif/webp/bmp blobs render via Coil instead of the
binary notice.
Issues:
- Labels at creation: the new-issue composer takes a comma/space separated
label list, written as NIP-34 `t` tags.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DpNmN8CvP6HnEsdTGAjVUr
NSite manifests (kinds 15128/35128) were never found because all coordinate
construction hardcoded napplet kinds (15129/35129). The connected apps list
and detail screen therefore showed no icon or title for nsite entries.
- Add rememberManifestIconModel(author, identifier) in NappletFavoriteIcon.kt:
tries napplet coord first, falls back to nsite coord. resolveIconBlob already
handled all four event types — just needed the right coordinate.
- Replace rememberNappletManifest with rememberManifestEvent in
ConnectedAppsScreen.kt: watches both napplet and nsite notes, returns
whichever carries an event. NappletAppCard dispatches on the event type
to extract title/icon from NappletManifest, RootSiteEvent, or NamedSiteEvent.
- ConnectedAppDetailScreen.AppIdentityHeader: swap hardcoded kind+
rememberNappletIconModel call for rememberManifestIconModel(author, identifier).
- resolveNappletMeta in NappletManifestLookup.kt: widen the cache filter to
include nsite kinds and dispatch title/icon extraction across all four types.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013hTFpoExYYLYEGGtXBx6ZT
NIP-34 pull-request update events (kind 1619) revise a PR with a newer
commit / merge base, but the repository screen neither subscribed to them
nor reflected them, so updated PRs looked stale.
- Subscribe to GitPullRequestUpdateEvent.KIND in RepositoryContentKinds so
updates reach the repo screen.
- Add GitPullRequestUpdateIndex (mirrors GitStatusIndex): tracks the latest
update per parent PR id from LocalCache, exposed as a StateFlow.
- Fold the latest update into the parent PR card: the current commit, merge
base and clone URLs now reflect the newest revision, with a "Revised"
marker on both the PR card and the compact Patches-tab row.
Updates are folded into their parent PR rather than listed as separate rows.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DpNmN8CvP6HnEsdTGAjVUr
- Capability rows now open a dialog with radio options (Ask each time /
Always allow / Never allow) instead of the confusing Switch+revoke
button combo; requiresPerUseConsent capabilities omit "Always allow"
- AppIdentityHeader now reactively loads napplet/nsite icons via
rememberNappletIconModel using the full kind:pubkey:identifier coord
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013hTFpoExYYLYEGGtXBx6ZT
The toggle ON/OFF state was ambiguous (users couldn't tell if OFF meant
"ask me" or "permanently deny"). The revoke/Block icon looked like a deny
action but actually resets to "ask me each time".
- Add "Allow always" (primary) / "Never allow" (error) label above the
Switch so the toggle's two states are explicit
- Swap MaterialSymbols.Block for MaterialSymbols.Refresh on the reset
button — Refresh reads as "start over / go back to asking"
- Update its content description to "Ask me each time"
- Rename "Blocked" to "Requires per-use approval" for per-use-consent
capabilities to explain why there's no toggle
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013hTFpoExYYLYEGGtXBx6ZT
Build out the repository screen into a project-review hub.
PR/patch diff viewer:
- quartz: UnifiedDiffParser turns a NIP-34 patch (kind 1617) `git
format-patch` body into a commit message + structured per-file diffs,
bounding each hunk by its header counts so the mbox "-- " signature is
never miscounted. Unit-tested against a real git-generated patch.
- amethyst: GitDiffView renders a GitHub-style file-by-file diff —
stat summary, collapsible file cards, +/- line coloring, old/new line
numbers, and per-line syntax highlighting reused from the code browser
(size-guarded). Wired into the patch card; feeds keep the compact preview.
Issue management:
- New-issue composer (subject + body) that builds, signs and broadcasts a
GitIssueEvent via the account signer; reachable from the Issues tab.
- Author/maintainer Open/Close controls on the issue card, publishing
GitStatusOpen/Closed events that flow back through GitStatusIndex.
Issues tab polish:
- Open/Closed filter chips, label (#topic) chips on each row.
Adds git_diff_files_changed plural and issue/diff strings. No new icons.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DpNmN8CvP6HnEsdTGAjVUr
The raw event JSON toggle in the signer consent dialog is now labeled
"Show Event" / "Hide Event" instead of the generic "See more" / "See less".
The expanded JSON block also gains horizontal scroll (softWrap=false +
horizontalScroll) so wide event JSON doesn't get clipped on narrow screens.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013hTFpoExYYLYEGGtXBx6ZT