feat: surface github.com link in sidebar main workspace - #40
Conversation
调研 anomalyco/ghostty-web(Ghostty VT 解析器 WASM 化 + xterm.js API 兼容) 与 myworktree 的相似/差异点,以及作为 xterm.js 替代 renderer 的借鉴方案。 结论:精准命中 TERMINAL_FILTER_REVIEW.md 与 CHINESE_IME_ANALYSIS.md 已记录 的 OSC/DA 回声与复杂脚本渲染两类痛点,建议以 feature flag 灰度引入。
Render a small GitHub Mark icon to the right of the sidebar main workspace project name when the main repo's git remote resolves to github.com. Clicking opens the canonical https://github.com/<owner>/<repo> URL in a new tab via <a target="_blank" rel="noopener noreferrer">, with event.stopPropagation() so the icon click does not trigger the row's selectWorktree handler. Backend: - Add gitx.GitHubURL(root) resolver. Prefers 'origin', then iterates 'git remote'. Normalizes SCP / HTTPS / ssh:// forms, strips .git and trailing slashes, and returns '' for any host other than github.com (case-insensitive). GitHub Enterprise and non-Git remotes are intentionally not surfaced. - /api/main response gains a github_url string field (always present; empty when no link can be resolved). Tests cover all three URL formats, malformed inputs, GitHub Enterprise, non-GitHub hosts, .git suffix, and the /api/main field plumbing. Docs (per docs-first convention): CHANGELOG, API, ARCHITECTURE, PRD, README.md, README.zh-CN.md.
Code ReviewBug — SCP-style regex 硬编码
|
The githubSCPRE regex hardcoded 'git@github.com:...' as the only recognized SCP-style prefix. Repositories whose origin URL uses a non-'git' username (e.g. a '\~/.ssh/config' alias mapping 'github.com' to 'User mywork', or a CI bot configured as 'ci-bot@github.com:...') would silently fail to surface a GitHub icon in the sidebar — the URL was still valid, but the resolver returned '' so the icon was never rendered. Loosen the regex to '[^@/]+@github\.com:...' so it accepts any non-empty user segment, mirroring the existing githubSSHRE pattern and bringing the two ssh-shaped forms back into symmetry. - remote.go: relax githubSCPRE; update Go doc to mention arbitrary user and replace the misleading 'default SSH port only' line. - gitx_test.go: add three new parse cases (ssh config alias user, CI bot user, dotted user) so the relaxation is covered. - docs/API.md: update the supported URL formats list to call out the arbitrary user segment. No behavior change for existing 'git@' URLs; tests + vet + build all green.
Review responseThanks for the careful review. Addressing each point: ✅ Fixed — SCP regex hardcoded
|
# Conflicts: # CHANGELOG.md # docs/PRD.md # internal/app/app_test.go # internal/ui/static/index.html
PR #40 (GitHub sidebar link) and PR #42 (branch divergence detection) both inlined a ~10-line `git remote` parser in their respective files. Git's content-level merge cannot detect duplicate top-level symbols across files, so the collision only surfaced as a Go compiler error after the merge. The fix was to drop the divergent.go copy in the merge commit, leaving a single implementation in remote.go. This commit goes one step further: move that single implementation to a new remotes.go file and export it as ListRemotes, so future features that need the same helper have a clearly signposted canonical entry point instead of being tempted to inline their own copy. No behavior change. The two existing callers (GitHubURL in remote.go and effectiveHead in diverged.go) now invoke ListRemotes directly.
|
Closing to retrigger CI — the PR head was a merge commit (846d9a6) which suppressed the synchronize event for the follow-up push. Reopening to get a fresh merge-state evaluation. |
`gofmt -l` on Linux/macOS CI rejects the file without a trailing newline. Run gofmt to comply.
Summary
github.com. Clicking the icon opens the canonicalhttps://github.com/<owner>/<repo>URL in a new tab./api/maingithub_urlfield is non-empty; non-GitHub remotes and GitHub Enterprise intentionally do not surface a link.Backend
gitx.GitHubURL(gitRoot)resolver. Prefersorigin, then iteratesgit remote. Normalizes SCP / HTTPS /ssh://forms, strips.gitand trailing slashes, and returns the empty string for any host other thangithub.com(case-insensitive). Never errors out — timeouts and unparseable URLs degrade to""so the caller can treat the result as a pure boolean.GET /api/mainresponse gains agithub_urlstring field (always present; empty when no link can be resolved).Frontend
index.html:renderSidebarMain()conditionally appends an<a target="_blank" rel="noopener noreferrer">GitHub icon to the.wt-namerow.event.stopPropagation()prevents the icon click from triggering the row'sselectWorktree(MAIN_WT_ID)handler. The URL is passed throughencodeURIas defense in depth..wt-nameflex layout already supportsflex: 1; min-width: 0so the project name shrinks while the icon stays right-aligned.Tests
gitx_test.go:TestGitHubURLcovers HTTPS / HTTP / SCP / ssh:// forms, GitHub Enterprise,.gitsuffix, trailing slashes, leading/trailing whitespace, non-GitHub hosts, no remote, and origin-preferred-vs-fallback ordering.app_test.go:TestHandleMainassertsgithub_urlis a string in the response and that its value matches the resolver's output for the same git root.Docs (docs-first)
CHANGELOG.md— Unreleased section entry.docs/API.md—/api/mainresponse shape and field semantics.docs/ARCHITECTURE.md— sidebar main-workspace behavior.docs/PRD.md— main workspace section.README.md/README.zh-CN.md— §Features (MVP) bullet.Verification
go build ./...✅go vet ./...✅go test ./internal/gitx/... ./internal/app/...✅Notes
ssh://git@github.com:22/owner/repo.git(explicit SSH port) is intentionally not recognized; only the default-portssh://form is matched. This matches the behavior documented indocs/API.md.escapeHtmlwas applied to the existingonclick="selectWorktree('')"interpolations as a small XSS hardening side-effect.