Fix repo action bar breaking on real (long) domains
HTTPS and SSH clone boxes were each their own flex:1 element competing for room in the same row as branch/Commits/Merge requests/Settings — fine with "localhost" in local testing, but git.neuromancer.ovh wrapped mid-URL. Regrouped into one compact clone box with an HTTPS/SSH toggle (reuses the existing generic [data-tab]/.gf-tabs/.gf-tabpane JS from the settings page, just restyled), separate from the actions row. Verified against the exact real-world conditions that broke it: took the rendered page and substituted the actual production domain back in, confirmed it now fits on one line instead of wrapping.
3 files changed
+41 −21
M
CHANGELOG.md
+4 −0
M
cmd/gitfed-web/handlers_repo.go
+23 −10
M
cmd/gitfed-web/render.go
+14 −11
CHANGELOG.md
@@ -1,5 +1,9 @@
# Changelog
+## 1.0.1
+
+- Fixed the repo page's action bar breaking on real (long) domains: the HTTPS and SSH clone boxes were each their own `flex: 1` box competing for room in the same row as the branch/Commits/Merge requests/Settings buttons, so a real hostname wrapped mid-URL instead of eliding cleanly. Regrouped into one compact clone box with an HTTPS/SSH toggle (reusing the same tab-switching JS as the settings page), separate from the row of action buttons. Found on the live instance right after making its own repo public — the first time a public, non-empty, admin-visible repo with a real long domain had ever actually rendered this bar.
+
## 1.0.0
First stable release. Federated SSH-certificate identity, per-repo/per-role ACLs, a self-service web UI (repo browser, commit history and detail, merge requests with review and a real server-side merge, pinned repos, federated notifications), anonymous read-only HTTPS clone for public repos, and a documented backup/restore procedure — see `README.md` for the full picture and `ROADMAP.md` for what was deliberately left out and why.
cmd/gitfed-web/handlers_repo.go
@@ -106,25 +106,38 @@ var repoTpl = newTpl("repo", `
</div>
<p class="muted">{{t .Lang "repo.owner"}}: {{.Repo.Owner}}</p>
-<div class="gf-action-bar">
+<div class="gf-actions-row">
{{if .Branch}}<span class="gf-btn">{{icon "branch"}} {{.Branch}}</span>{{end}}
- {{if .Repo.Public}}
- <div class="gf-clone-url">
- <span class="gf-clone-label">HTTPS</span>
+ {{if not .Empty}}<a href="/repo-commits/{{.Repo.Name}}" class="gf-btn">{{icon "history"}} {{t .Lang "repo.commits_title"}}</a>{{end}}
+ {{if not .Empty}}<a href="/repo-mrs/{{.Repo.Name}}" class="gf-btn">{{icon "branch"}} {{t .Lang "mr.list_title"}}</a>{{end}}
+ {{if .CanAdminister}}<a href="/repo-settings/{{.Repo.Name}}" class="gf-btn">{{t .Lang "nav.settings"}}</a>{{end}}
+</div>
+
+{{if .Repo.Public}}
+<div class="gf-clone-block">
+ <div class="gf-tabs gf-clone-toggle">
+ <button type="button" data-tab="clone-https" class="active">HTTPS</button>
+ <button type="button" data-tab="clone-ssh">SSH</button>
+ </div>
+ <div id="clone-https" class="gf-tabpane gf-clone-row active">
<span class="badge plain" title="{{t .Lang "repo.https_readonly_hint"}}">{{t .Lang "repo.read_only"}}</span>
<code>{{.HTTPSCloneURL}}</code>
<button type="button" class="linklike" data-copy="{{.HTTPSCloneURL}}" title="{{t .Lang "repo.copy_clone_url"}}" aria-label="{{t .Lang "repo.copy_clone_url"}}">{{icon "copy"}}</button>
</div>
- {{end}}
- <div class="gf-clone-url">
- <span class="gf-clone-label">SSH</span>
+ <div id="clone-ssh" class="gf-tabpane gf-clone-row">
+ <code>{{.CloneURL}}</code>
+ <button type="button" class="linklike" data-copy="{{.CloneURL}}" title="{{t .Lang "repo.copy_clone_url"}}" aria-label="{{t .Lang "repo.copy_clone_url"}}">{{icon "copy"}}</button>
+ </div>
+</div>
+{{else}}
+<div class="gf-clone-block single">
+ <div class="gf-clone-row">
+ <span class="label">SSH</span>
<code>{{.CloneURL}}</code>
<button type="button" class="linklike" data-copy="{{.CloneURL}}" title="{{t .Lang "repo.copy_clone_url"}}" aria-label="{{t .Lang "repo.copy_clone_url"}}">{{icon "copy"}}</button>
</div>
- {{if not .Empty}}<a href="/repo-commits/{{.Repo.Name}}" class="gf-btn">{{icon "history"}} {{t .Lang "repo.commits_title"}}</a>{{end}}
- {{if not .Empty}}<a href="/repo-mrs/{{.Repo.Name}}" class="gf-btn">{{icon "branch"}} {{t .Lang "mr.list_title"}}</a>{{end}}
- {{if .CanAdminister}}<a href="/repo-settings/{{.Repo.Name}}" class="gf-btn">{{t .Lang "nav.settings"}}</a>{{end}}
</div>
+{{end}}
{{if .Crumbs}}
<div class="gf-crumbs">
cmd/gitfed-web/render.go
@@ -323,17 +323,20 @@ const shellHeadSrc = `<!doctype html>
.gf-crumbs .sep { color: var(--text-faint); margin: 0 0.35em; }
.gf-repo-head { display: flex; align-items: center; gap: 0.55rem; flex-wrap: wrap; margin-top: 0.6rem; }
.gf-repo-head h1 { font-size: 1.3rem; margin: 0; font-weight: 700; }
- .gf-action-bar { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; margin: 1rem 0; }
- .gf-action-bar .gf-btn, .gf-action-bar .gf-clone-url { margin-top: 0; }
- .gf-clone-url {
- flex: 1; min-width: 200px; font-family: var(--mono); font-size: 0.8rem; color: var(--text-dim);
- background: var(--surface-2); border: 1px solid var(--border); border-radius: 7px; padding: 0.4rem 0.7rem;
- display: flex; align-items: center; gap: 0.5rem; overflow: hidden;
- }
- .gf-clone-label { flex-shrink: 0; font-family: var(--mono); font-size: 0.68rem; font-weight: 700; letter-spacing: 0.03em; color: var(--text-faint); }
- .gf-clone-url code { background: none; padding: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
- .gf-clone-url button { margin: 0 0 0 auto; padding: 0; background: none; border: none; color: var(--text-faint); cursor: pointer; flex-shrink: 0; font-size: 0.95rem; }
- .gf-clone-url button:hover { color: var(--text); }
+ .gf-actions-row { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; margin: 1rem 0 0.7rem; }
+ .gf-actions-row .gf-btn { margin-top: 0; }
+
+ .gf-clone-block { background: var(--surface-2); border: 1px solid var(--border); border-radius: 9px; overflow: hidden; margin-bottom: 1.25rem; max-width: 560px; }
+ .gf-clone-block .gf-tabs.gf-clone-toggle { display: flex; gap: 0.2rem; padding: 0.3rem; border-bottom: 1px solid var(--border); margin-bottom: 0; }
+ .gf-clone-block .gf-clone-toggle button { font-family: var(--mono); font-size: 0.76rem; font-weight: 700; letter-spacing: 0.03em; padding: 0.3rem 0.7rem; border-radius: 6px; border-bottom: none; margin-bottom: 0; color: var(--text-faint); }
+ .gf-clone-block .gf-clone-toggle button.active { background: var(--surface-3); color: var(--text); border-bottom-color: transparent; }
+ .gf-clone-row { display: flex; align-items: center; gap: 0.5rem; padding: 0.5rem 0.75rem; font-family: var(--mono); font-size: 0.8rem; color: var(--text-dim); }
+ .gf-clone-block .gf-tabpane.gf-clone-row { display: none; }
+ .gf-clone-block .gf-tabpane.gf-clone-row.active { display: flex; }
+ .gf-clone-row code { background: none; padding: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1; min-width: 0; }
+ .gf-clone-row .label { flex-shrink: 0; font-size: 0.68rem; font-weight: 700; letter-spacing: 0.03em; color: var(--text-faint); }
+ .gf-clone-row button.linklike { flex-shrink: 0; font-size: 0.95rem; }
+ .gf-clone-block.single .gf-clone-row { padding: 0.6rem 0.9rem; }
.gf-card { background: var(--surface); border: 1px solid var(--border); border-radius: 10px; overflow: hidden; margin-bottom: 1.25rem; }
.gf-file-table { display: table; width: 100%; border-collapse: collapse; margin: 0; }
.gf-file-table tr { border-bottom: 1px solid var(--border); }