Skip to content

Pr/solid rewrite - #2

Merged
satyamlohiya merged 34 commits into
betaversionio:mainfrom
AlyNotMe:pr/solid-rewrite
Sep 17, 2026
Merged

satyamlohiya merged 34 commits into
betaversionio:mainfrom
AlyNotMe:pr/solid-rewrite

Conversation

@AlyNotMe

@AlyNotMe AlyNotMe commented Sep 9, 2026

Copy link
Copy Markdown
Collaborator

Summary

SOLID rewrite of the backend + replacement of the React UI with a vanilla
webview modelled on the GitHub Desktop app, plus a series of git-flow
features. 32 commits, linear history on top of main (0 commits behind).

Architecture

  • The message-handler god object is split into cohesive services
    (working-tree, sync, branch, commit-actions, conflict, stash, diff,
    pull-request, repository-data) behind ports & adapters — services no
    longer touch vscode or simple-git directly, everything is mockable.
  • Webview ↔ extension messages are typed discriminated unions.
  • Single composition root (TimelineController).

Webview

  • Timeline and commit-detail panel rewritten as one self-contained
    HTML/CSS/JS view, layout aligned with the GitHub Desktop app.
  • Removed React, MUI, emotion, vite, axios, dayjs,
    @octokit/auth-oauth-device (sign-in now uses the native
    vscode.authentication API). Build is webpack-only. Bundle ~600 KB → 298 KB.
  • New diff renderer styled like VS Code's native diff editor: dual gutters,
    word-level intra-line highlighting.

Git security

  • Auth via an ephemeral -c http.extraheader instead of writing the token
    into .git/config. GIT_TERMINAL_PROMPT=0.

Features

  • force-push (--force-with-lease), branch comparison, undo last commit,
    amend, co-authors, conflict resolution (continue/abort/mark resolved),
    stash (push/apply/drop).
  • Branch dropdown ordered by recency (MRU via reflog).
  • Repository picker that also opens the folder in VS Code.
  • Pull request list + check out a PR via the GitHub REST API.

Removed

  • History Explorer (dead code), the old React timeline, dead interface types.

Open questions

  • This PR includes packaging changes (publisher, package name, version bump
    to 1.7.0) — happy to drop those or handle them separately.
  • repository.url in package.json still points at the old
    Satyam-2001/GitHub-Desktop fork.
  • Large diff: I can split this into 3 PRs (SOLID refactor / webview rewrite /
    features) if that's easier to review.

AlyNotMe and others added 30 commits September 3, 2026 04:15
The commit workflow (ChangesView, stage/unstage, commit, push/pull) was
fully implemented on both the webview and the message-handler side but the
tab switcher was commented out, leaving it unreachable.

- Timeline.tsx: re-enable <TimelineTabs>
- useTimelineState.ts: default activeTab to "changes"
- volta: pin node 22

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
When the timeline view is docked in the bottom panel the Popover was
taller than the viewport and got pushed off the top edge. Make the Paper
a flex column capped at the viewport height, let the branch list shrink,
and add marginThreshold so MUI keeps it within bounds.

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

The React/vite webview rendered blank when docked in the panel. Replace
WebviewHtmlService with an inlined (no bundle, nonce CSP) UI that mirrors
GitHub Desktop: repository / branch / sync toolbar, Changes + History
tabs, filter, per-file checkboxes with status squares, commit box, and a
unified diff pane on the right.

Backend: add commitFiles (stage + commit a pathspec), discardFiles, and
getWorkingDiff message handlers.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- global [hidden] rule so .tabpane display:flex no longer defeats it
  (Changes and History were rendering on top of each other)
- hide the right pane and let the left column go full-width when no
  file is selected, instead of showing an empty "No file selected" block

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- send updateAccounts (active account login/avatar) on refresh and show
  the GitHub avatar image in the commit box, falling back to initials
- give #fileList a min-height and make the Changes pane scroll, so the
  file list stays visible even when the view is docked in a short panel
- shrink the default Description height; pin the commit box to the bottom
- check newly appeared changed files by default, preserve prior choices

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Match GitHub Desktop's tighter spacing: shorter toolbar (42px), smaller
tab/label/input fonts, less padding everywhere, 20px avatar, 30px min
Description. File list keeps flex priority with a small 44px floor;
hide filter / summary rows entirely when there are no changes.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The branch dropdown was fed git.branch().all, which includes
remotes/origin/* tracking refs. Selecting one ran `git checkout
remotes/origin/dev` and detached HEAD.

- git-operations: expose local branches plus remote-only branches by
  their short name, never the remotes/<remote>/<name> form
- message-handler: strip any remotes/<remote>/ prefix before checkout

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The old `git branch -vv` bracket parser missed the tracking branch in
some formats (and could false-positive on brackets in commit subjects),
so a published branch showed "Publish branch / not on the remote yet".
Use `git rev-parse @{upstream}` and `git rev-list --left-right --count`.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Show "Publish branch" only when a remote exists, git reported no
upstream, and there is nothing ahead/behind. Any uncertain state (status
not yet received) now falls back to "Fetch origin".

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The timeline webview was rebuilt as a self-contained vanilla HTML/JS view
(webview-html.service.ts). The old React app (bootstrap.tsx -> app/App.tsx
-> components/Timeline/**), its hooks, the mock git.service, the DI/domain
layers, and the unregistered changes/history tree providers were still
built but never loaded. Removed.

Kept: app/bridge.ts and app/components/CommitDetailPanel/** (used by the
commit-detail and history-explorer bundles), and the History Explorer
feature untouched.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
MessageHandlerService (944 lines, 40-case switch, god object) is replaced by:

- GitClientFactory / AccountGitClientFactory: single owner of git client
  creation and credentials. withAuth() injects the token via ephemeral
  GIT_CONFIG_* env vars, so it is never written to .git/config and there is
  no shared remote URL to race on (fixes the token-leak + race bugs).
- InboundMessage / OutboundMessage: discriminated unions, no `any`, no casts.
- MessageRouter: Map<command, handler>; adding a command no longer edits a
  switch (Open/Closed).
- Cohesive services, each with one reason to change:
  SyncService, WorkingTreeService, BranchService, CommitActionsService,
  PullRequestService, DiffService, RepositoryDataService (read model).
- Ports (RepositoryContext, Notifier, Browser, WebviewChannel, Refresher)
  with VS Code adapters — services no longer import `vscode` or simpleGit.
- TimelineController: composition root + the one place that recomputes and
  broadcasts state.

Behaviour changes:
- pull / merge / revert / cherry-pick now detect conflicts and emit a
  `mergeConflict` message instead of swallowing the error.
- publish uses authentication (was a plain client -> first publish failed).
- commit only clears the input on a `commitSucceeded` ack.
- branch activity is one `for-each-ref` call, not one `git log` per branch.
- the provider debounces refresh (400ms) so save-all triggers one recompute.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Replace the two-line sync cell (value + sub-label span) with a single
value plus a title tooltip; shrink toolbar font/padding.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Rewire the features that existed in the old React timeline onto the vanilla
webview, now that the backend handlers are reachable again:

- History rows: right-click context menu (checkout / branch-from / tag /
  cherry-pick / revert / reset / copy SHA / view on GitHub).
- Branch dropdown: "New branch…" (with a Bring-changes / Stash choice when
  the working tree is dirty) and "Create pull request…".
- History: "Load more commits" button + scroll-to-bottom paging, wired to
  loadMoreCommitsResponse with de-duplication.
- commitSucceeded ack clears the commit inputs; a failed commit keeps them.
- mergeConflict switches to the Changes tab and shows a warning bar.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Remove FileIconInfo / CommitDetailFile / the untyped WebviewMessage from the
timeline interfaces barrel (superseded by the discriminated unions in
messages.ts); document the survivors.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- 50px toolbar with stacked label/value cells (Current repository /
  Current branch / sync) and a sub-line on the sync cell.
- 41px tabs with an accent underline on the active tab.
- Roomier file rows (28px) with filled octicon-style status squares.
- Commit box on its own chrome panel: 28px avatar, rounded inputs,
  full-width accent "Commit to <branch>" button, taller description.
- Larger empty states, softer menus (8px radius, deeper shadow),
  accent-tinted diff gutters, consistent 6px control radius.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
New backend services (SOLID: one responsibility each, wired via the router):

- WorkingTreeService.amend / undoLastCommit — amend rewrites HEAD (warns
  when it is already on a remote); undo is `reset --soft HEAD~1`.
- ConflictService — markResolved / continue / abort, driving whichever of
  merge / cherry-pick / revert / rebase is detected in the git dir
  (detection shared with the read model via git-operation-state.ts).
- StashService — push (with untracked) / apply / pop / drop.
- RepositoryDataService now reports operation, conflicted paths, canUndo
  and the stash list in the snapshot; the controller broadcasts
  updateOperation + updateStashes on every refresh.

Webview:

- Commit box: "Amend last commit" checkbox (prefills the last message) and
  an "Add co-authors" field that appends Co-Authored-By trailers.
- History tab: an "Undo <last commit>" bar when undo is safe.
- Conflict bar with Abort / Continue (Continue enabled once no file is
  left conflicted); conflicted files show "Mark resolved".
- Branch dropdown: "Stash all changes" and the list of existing stashes
  with pop / delete.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
SOLID backend rewrite, GitHub Desktop-style UI, and the undo / amend /
co-authors / conflict-resolution / stash features. Version bump so the
packaged .vsix installs over 1.1.13.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Toolbar back to a single 32px row (icon + value + caret), no stacked
  label — the 50px two-line cells were unusable at sidebar width.
- .tabpane clips and #fileList is the only scroller (min-height:0), so the
  commit box stays pinned and reachable however short the panel is.
- Trim commit-box padding / description height.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- #pane-changes is now one scroll container; the commit box is
  position:sticky so it stays in view but the whole pane (file list OR
  the "no changes" message) scrolls, even with an empty working tree.
- Toolbar 30px, tabs 32px, smaller "no local changes" block — less
  wasted vertical space in a narrow sidebar.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Squashes a branch of work on the timeline webview:

- Removed the dead React timeline (superseded by the vanilla webview).
- Split the 944-line MessageHandlerService into cohesive services behind
  a MessageRouter and typed message unions; credentials now go through
  GitClientFactory.withAuth (ephemeral env, never written to .git/config),
  fixing a token-leak and a remote-URL race.
- Reworked the webview to match GitHub Desktop; compact toolbar, single
  scroll on the Changes pane.
- New: commit context menu, new-branch (bring/stash), create PR, load
  more history, undo last commit, amend, co-authors, conflict
  resolution (mark resolved / continue / abort), stash management.

Released as 1.2.2.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Delete the unused History Explorer webview (never registered in
  contributes.views): provider, React app, types, vite entry.
- Drop the 8 githubDesktop.commit.* commands from package.json — declared
  but never registered (the actions run via webview messages).
- Remove the unused @tanstack/react-query dependency.
- Rewrite docs/overview.md for the current SOLID architecture.

Release 1.2.3.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Force-push:
- SyncService.forcePush() uses --force-with-lease behind a confirm.
- The sync cell shows a "Diverged ↑N ↓M" state when the branch and its
  upstream have both moved; clicking it (or right-clicking any sync
  state) opens a menu: Fetch / Pull / Push / Force-push origin.

Compare branches:
- BranchService.compare() runs `git log <branch>..HEAD` and the reverse,
  emitting branchComparison.
- The branch dropdown gets a "⇄" button per branch; it opens a compare
  view in the History tab with the two divergent commit lists and an
  exit control. A refresh leaves compare mode.

Extracted commit-log.ts (COMMIT_LOG_FORMAT / parseCommitLogLine / logRange
/ formatRelativeTime) and reused it in loadMoreCommits.

Release 1.3.0.

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

Reworked the webview to follow image-1.png (the actual app):

- Toolbar: two-line cells (small label + bold value) at 50px, like the
  app — "Current repository / <name>", "Current branch / <branch>",
  "<sync action> / Last fetched …" with the ahead/behind badge. Falls
  back to a 34px single line when the view is narrow (sidebar).
- Right pane: the rich "No local changes" state — big heading, subtitle
  and action cards (Push/Publish/Pull as the primary card, Create pull
  request, View on GitHub). When there are changes it shows the diff (or
  a "select a file" hint); hidden entirely in narrow mode.
- Commit form: co-authors is now a person-icon toggle, "Amend" checkbox
  next to it; a "Committed … · Undo" bar sits under the form in both
  tabs (replaces the History-only undo bar).
- New openRepoOnGitHub message + PullRequestService.openRepo().

Fix: renderToolbar no longer writes #commitBranch (updateCommitBtn owns
the button label and was replacing that node, making renderToolbar throw
and freezing the sync cell on "Fetch origin").

Verified layouts with headless-Chrome screenshots (wide panel + narrow
sidebar, empty + dirty tree).

Release 1.4.0.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Single 30px toolbar row (icon + value + caret), no stacked labels; 28px
tabs; tighter commit box. The two-line GitHub Desktop toolbar wastes too
much vertical space in the VS Code panel.

Release 1.4.1.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Bug: withAuth() set GIT_CONFIG_* / GIT_TERMINAL_PROMPT through simple-git's
.env(), which trips its unsafe-env guard ("Use of GIT_CONFIG_COUNT is not
permitted…"), breaking every authenticated push/pull/fetch. Now the token
rides on `-c http.https://github.com/.extraheader=…` (not on .env, not in
.git/config), and GIT_TERMINAL_PROMPT=0 is set once at activation.

Repository picker: the "Current repository" cell now lists every tracked
repo (filterable, with paths) and switches the active one in-session, plus
"Add local repository…" (folder picker) and "Clone repository…". New
RepositoryContext.list/setActive/addLocal/clone + updateRepositoryList
message.

Release 1.4.2.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
RepositoryContext.setActive is now async: if the picked repo isn't already
a workspace folder it runs `vscode.openFolder` (same window), so choosing
a repo in the "Current repository" list also switches the editor to it.
If it's already open, it just switches the in-session active repo without
a reload.

Release 1.4.3.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
#pane-changes is a flex column again: conflict/filter/select-all are
fixed headers, #fileList is the only scroller, the commit box and the
"Committed … · Undo" bar are fixed footers. No more sticky overlay with
file rows sliding behind the form.

Release 1.4.4.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Revert the flex-footer approach: #pane-changes scrolls as one column,
the file list has no inner scroll, and the commit form + undo bar flow
right after it. Nothing is sticky or overlaid — scrolling the pane moves
the files and the form together.

Release 1.4.5.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
listBranches now returns local branches current-first, then by reflog
checkout order (true MRU), then by last commit date; remote-only branches
after. The "Current branch" picker follows that order.

Release 1.5.0.

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

The separate React/MUI webview duplicated what the timeline right pane
already does. Commit detail (header, changed-files list, per-file diff with
a back link) now renders inline in the right pane, vanilla like the rest.

Removed: src/webviews/commitDetail, src/webviews/app, vite.webview.config.ts,
and deps @mui/*, @emotion/*, react, react-dom, @octokit/auth-oauth-device
(dead), axios (dead), dayjs (dead). Build is now webpack-only.
Bundle 600KB+ -> 298KB; vsix -> 258KB.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
AlyNotMe and others added 3 commits September 9, 2026 12:15
Rewrote the diff view to match VS Code's diff editor: dual line-number
gutters (old/new), a sign column, and token-level intra-line highlighting
that marks the exact words changed between a removed line and its
replacement (LCS over word tokens). Hunk headers show the function context.
Colors come from --vscode-diffEditor-* so it tracks the active theme.
The same renderer backs working-tree diffs and per-file commit diffs.

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

The branch dropdown gains a "Branches / Pull requests" toggle. The PR tab
fetches open PRs via the GitHub REST API (new GitHubApi port, backed by the
account manager's Octokit) and lists them most-recently-updated first, with
author, head branch and draft/fork markers. Selecting one runs
`git fetch origin pull/<n>/head:pr/<n>` (works for same-repo and fork PRs)
and checks out the local pr/<n> branch.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Drop esModuleInterop, baseUrl/paths and jsx — all vestigial now that the
React webview is gone. Fixes the editor's "option is deprecated" warnings.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@satyamlohiya

Copy link
Copy Markdown
Member

Really glad to see someone take this on so aggressively — the SOLID refactor, the webview rewrite, and the git-flow features (force-push, stash, conflict resolution, PR checkout) are a huge amount of ground covered in one PR. Nice work.

Two features I'd love to see added on top of this:

  1. Merge a branch directly from the extension — being able to pick a source/target branch and merge without dropping to the terminal would complete the branch workflow nicely.
  2. View changed files for a specific commit — clicking a commit in the timeline and seeing its file list (and ideally diffs) would make history review much easier.

Really appreciate the effort here — thank you!

@AlyNotMe

Copy link
Copy Markdown
Collaborator Author

Thanks for the kind words and the feedback — really appreciate it!

Both of those are great suggestions, I'll get them added tomorrow:

  • Merge a branch directly from the extension
  • View changed files (and diffs) for a specific commit

Will ping this thread once they're in.

Adds "Merge into current branch..." to the branch dropdown: pick a source
branch from a filterable list and it merges into the current branch via
BranchService.merge (already handled conflicts, just had no UI trigger).
Addresses feedback from satyamlohiya on the SOLID-rewrite PR.

BranchService.merge now guards against the direction mistake it's easy to
make when the current branch is the destination: merging the default
branch (repo.defaultBranch, falling back to "main") into anything requires
an explicit confirm, since it's often a sign you meant to merge the other
way. Merging into a non-default current branch gets a lighter, non-blocking
warning instead.

The second request from the PR review (view changed files for a commit)
was already covered: clicking a commit in History shows its file list and
per-file diffs in the right pane.
@AlyNotMe

Copy link
Copy Markdown
Collaborator Author

Just pushed both of these:

  • Merge a branch directly from the extension — a "Merge into current
    branch…" entry in the branch dropdown, pick a source branch and it
    merges into the one you're on. Also added a confirm when the branch
    being merged is the default branch (easy mistake to make since the
    current branch is always the destination), and a lighter warning when
    merging into a non-default branch.
  • View changed files for a specific commit was already there — clicking a
    commit in History shows its file list and per-file diffs on the right.

Let me know if anything looks off!

@satyamlohiya
satyamlohiya merged commit 0633cae into betaversionio:main Sep 17, 2026
satyamlohiya added a commit that referenced this pull request Sep 17, 2026
The SOLID rewrite (#2) removed vite/React and consolidated the build into
a single webpack compile step, but ci.yml and release.yml still used pnpm
and called the now-nonexistent "build-webview" script, breaking CI on
every push since the merge.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
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.

2 participants