Skip to content

Commit 0128420

Browse files
maphewclaude
andcommitted
feat: auto-detect username from git/environment for write operations
Implements automatic username detection to eliminate annoying prompts when performing write operations in the web UI. Server-side changes: - Added detectUsername() function that checks git config user.name, then environment variables (USER, USERNAME, LOGNAME) - Falls back to "web-user" if no username found - Username detected once at startup and passed to all templates Frontend changes: - Added initUsername() function to store server-provided username in localStorage (beady-username key) - Updated all 6 templates to initialize username on page load - Removed old prompt-based username collection from detail.html and issue_form.html User experience: - No prompts required - username auto-detected from git config - Persistent across sessions via localStorage - Consistent with git commit attribution Closes beady-41 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 7c6b48a commit 0128420

File tree

9 files changed

+93
-18
lines changed

9 files changed

+93
-18
lines changed

.beads/issues.jsonl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
{"id":"beady-39","content_hash":"83323d4a564a2d5a6dee4fa1745799b49e40511e35a7edece9451c5dd53e7d3a","title":"Add ui indicator when the server has shutdown or we've lost communication with it. (by changing text of [shutdown] button?)","description":"","status":"closed","priority":2,"issue_type":"task","created_at":"2025-10-30T16:47:20.7858358-07:00","updated_at":"2025-10-30T21:01:02.2387539-07:00","closed_at":"2025-10-30T21:01:02.2387539-07:00"}
3232
{"id":"beady-4","content_hash":"749de0c281574ebe4bccaa58ae31d485ea4bc6a6e0c8d4b1af4b0b1558c0c6c8","title":"bd-ui --help doesn't work","description":"","status":"closed","priority":2,"issue_type":"task","created_at":"2025-10-24T11:38:06.8393236-07:00","updated_at":"2025-10-26T19:44:25.3374606-07:00","closed_at":"2025-10-23T03:03:21.777440439-07:00"}
3333
{"id":"beady-40","content_hash":"a74b3d492cd1b9a62ea8daf00035eae2d01fa5226ed53c1e61db1e2c4e9da2c4","title":"stats cards across top of page are too big. Put into a small table. Then merge the Status filter into the table, so that clicking on Open filters for open, etc.","description":"","status":"closed","priority":2,"issue_type":"task","created_at":"2025-10-30T19:13:37.8779173-07:00","updated_at":"2025-10-30T20:46:24.8738872-07:00","closed_at":"2025-10-30T20:46:24.8738872-07:00"}
34-
{"id":"beady-41","content_hash":"28059053bf188d7255201b93cb723027990c9f2c44e66ab6f37dfdce892891d6","title":"for write ops, ask browser for user name for attribution instead of throwing up a prompt","description":"","status":"open","priority":2,"issue_type":"task","created_at":"2025-10-31T08:31:58.9882993-07:00","updated_at":"2025-10-31T08:31:58.9882993-07:00"}
34+
{"id":"beady-41","content_hash":"28059053bf188d7255201b93cb723027990c9f2c44e66ab6f37dfdce892891d6","title":"for write ops, ask browser for user name for attribution instead of throwing up a prompt","description":"","status":"closed","priority":2,"issue_type":"task","created_at":"2025-10-31T08:31:58.9882993-07:00","updated_at":"2025-10-31T08:55:19.9522669-07:00","closed_at":"2025-10-31T08:55:19.9522669-07:00"}
3535
{"id":"beady-5","content_hash":"6a2f7cc5014cb2480a54c9acd9f26e16ccad8a47773e88655db48b9638fd7445","title":"add feature: live reload","description":"","status":"closed","priority":2,"issue_type":"task","created_at":"2025-10-23T02:50:14.475464102-07:00","updated_at":"2025-10-26T19:44:25.3404749-07:00","closed_at":"2025-10-23T03:25:18.353531264-07:00"}
3636
{"id":"beady-7","content_hash":"5b2e3098e3a7f4363fc8f2d647f97a3ec5b9f8a4525fd406745fd8645dbe82a2","title":"missing templates causing panic in dev mode","description":"","status":"closed","priority":0,"issue_type":"bug","created_at":"2025-10-24T05:38:00.486024544-07:00","updated_at":"2025-10-26T19:44:25.3450313-07:00","closed_at":"2025-10-24T05:42:56.941119818-07:00"}
3737
{"id":"beady-8","content_hash":"040645dd42eef87c67cb0210f4272e57a3f3322614c9214cd8b924f45abdb5af","title":"why template \u0026 static in root and in cmd/bd-ui? which location is best practice?","description":"","status":"closed","priority":2,"issue_type":"task","created_at":"2025-10-24T05:50:56.38010406-07:00","updated_at":"2025-10-26T19:44:25.34703-07:00","closed_at":"2025-10-24T05:53:33.7105026-07:00"}

assets/beady/static/app.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,25 @@
22

33
console.log('Beads UI loaded');
44

5+
// Username management
6+
function initUsername(serverUsername) {
7+
// Check if username is already stored
8+
let username = localStorage.getItem('beady-username');
9+
10+
// If not stored and server provided one, use it
11+
if (!username && serverUsername) {
12+
username = serverUsername;
13+
try {
14+
localStorage.setItem('beady-username', username);
15+
console.log('Username initialized from server:', username);
16+
} catch (e) {
17+
console.warn('Could not save username to localStorage:', e);
18+
}
19+
}
20+
21+
return username || 'web-user';
22+
}
23+
524
// Theme functionality
625
function applyTheme(theme) {
726
const html = document.documentElement;
@@ -182,6 +201,9 @@ function initShutdown() {
182201

183202
// View selector functionality
184203
document.addEventListener('DOMContentLoaded', function() {
204+
// Initialize username (use server-provided username if available)
205+
initUsername(window.beadyServerUsername);
206+
185207
// Initialize theme
186208
initTheme();
187209

assets/beady/templates/blocked.html

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,10 @@ <h3><a href="/issue/{{.ID}}">{{.ID}}: {{.Title}}</a></h3>
5858
</nav>
5959
</footer>
6060

61+
<script>
62+
// Initialize username from server
63+
window.beadyServerUsername = "{{.Username}}";
64+
</script>
6165
<script src="/static/app.js"></script>
6266
</body>
6367
</html>

assets/beady/templates/detail.html

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -217,20 +217,19 @@ <h3>Close Issue</h3>
217217
</article>
218218
</dialog>
219219

220+
<script>
221+
// Initialize username from server
222+
window.beadyServerUsername = "{{.Username}}";
223+
</script>
220224
<script src="/static/app.js"></script>
221225
<script>
222226
function showCloseDialog() {
223227
document.getElementById('close-dialog').showModal();
224228
}
225229

226-
// Prompt for username on first visit
230+
// Initialize username on page load
227231
document.addEventListener('DOMContentLoaded', function() {
228-
if (!localStorage.getItem('beady-username')) {
229-
const username = prompt('Enter your username for attribution:');
230-
if (username) {
231-
localStorage.setItem('beady-username', username);
232-
}
233-
}
232+
initUsername(window.beadyServerUsername);
234233
});
235234

236235
// HTMX event handlers

assets/beady/templates/graph.html

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ <h1>Dependency Graph: {{.Issue.Title}}</h1>
3535
</script>
3636
</main>
3737

38+
<script>
39+
// Initialize username from server
40+
window.beadyServerUsername = "{{.Username}}";
41+
</script>
3842
<script src="/static/app.js"></script>
3943
</body>
4044
</html>

assets/beady/templates/index.html

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,10 @@ <h4><a href="/issue/{{.ID}}">{{.ID}}: {{.Title}}</a></h4>
202202
</nav>
203203
</footer>
204204

205+
<script>
206+
// Initialize username from server
207+
window.beadyServerUsername = "{{.Username}}";
208+
</script>
205209
<script src="/static/app.js"></script>
206210
</body>
207211
</html>

assets/beady/templates/issue_form.html

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -110,16 +110,15 @@ <h1>Create New Issue</h1>
110110
</article>
111111
</main>
112112

113+
<script>
114+
// Initialize username from server
115+
window.beadyServerUsername = "{{.Username}}";
116+
</script>
113117
<script src="/static/app.js"></script>
114118
<script>
115-
// Prompt for username on first visit
119+
// Initialize username on page load
116120
document.addEventListener('DOMContentLoaded', function() {
117-
if (!localStorage.getItem('beady-username')) {
118-
const username = prompt('Enter your username for attribution:');
119-
if (username) {
120-
localStorage.setItem('beady-username', username);
121-
}
122-
}
121+
initUsername(window.beadyServerUsername);
123122
});
124123

125124
// HTMX error handling

assets/beady/templates/ready.html

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,10 @@ <h3><a href="/issue/{{.ID}}">{{.ID}}: {{.Title}}</a></h3>
6767
</nav>
6868
</footer>
6969

70+
<script>
71+
// Initialize username from server
72+
window.beadyServerUsername = "{{.Username}}";
73+
</script>
7074
<script src="/static/app.js"></script>
7175
</body>
7276
</html>

cmd/beady/main.go

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,8 @@ var devMode bool
110110

111111
var help = flag.Bool("help", false, "Show help")
112112

113+
var detectedUsername string
114+
113115
var srv *http.Server
114116

115117
func printUsage() {
@@ -260,6 +262,10 @@ func main() {
260262
os.Exit(0)
261263
}
262264

265+
// Detect username for attribution
266+
detectedUsername = detectUsername()
267+
log.Printf("Detected username: %s", detectedUsername)
268+
263269
// Set filesystem for templates and static files
264270
if devMode {
265271
if _, err := os.Stat("assets/beady"); os.IsNotExist(err) {
@@ -500,6 +506,7 @@ func handleIndex(w http.ResponseWriter, r *http.Request) {
500506
"Issues": issuesWithLabels,
501507
"Stats": stats,
502508
"ActiveStatus": activeStatus,
509+
"Username": detectedUsername,
503510
}
504511

505512
w.Header().Set("Content-Type", "text/html; charset=utf-8")
@@ -540,6 +547,7 @@ func handleIssueDetail(w http.ResponseWriter, r *http.Request) {
540547
"Labels": labels,
541548
"Events": events,
542549
"HasDeps": len(deps) > 0 || len(dependents) > 0,
550+
"Username": detectedUsername,
543551
}
544552

545553
if err := tmplAll.ExecuteTemplate(w, "detail.html", data); err != nil {
@@ -572,6 +580,7 @@ func handleGraph(w http.ResponseWriter, r *http.Request) {
572580
data := map[string]interface{}{
573581
"Issue": issue,
574582
"DotGraph": dotGraph,
583+
"Username": detectedUsername,
575584
}
576585

577586
if err := tmplAll.ExecuteTemplate(w, "graph.html", data); err != nil {
@@ -622,6 +631,7 @@ func handleReady(w http.ResponseWriter, r *http.Request) {
622631
"Issues": issuesWithLabels,
623632
"Stats": stats,
624633
"ExcludeLabel": excludeLabel,
634+
"Username": detectedUsername,
625635
}
626636

627637
if err := tmplAll.ExecuteTemplate(w, "ready.html", data); err != nil {
@@ -647,8 +657,9 @@ func handleBlocked(w http.ResponseWriter, r *http.Request) {
647657
stats, _ := store.GetStatistics(ctx)
648658

649659
data := map[string]interface{}{
650-
"Blocked": blocked,
651-
"Stats": stats,
660+
"Blocked": blocked,
661+
"Stats": stats,
662+
"Username": detectedUsername,
652663
}
653664

654665
w.Header().Set("Content-Type", "text/html; charset=utf-8")
@@ -664,8 +675,12 @@ func handleNewIssue(w http.ResponseWriter, r *http.Request) {
664675
return
665676
}
666677

678+
data := map[string]interface{}{
679+
"Username": detectedUsername,
680+
}
681+
667682
w.Header().Set("Content-Type", "text/html; charset=utf-8")
668-
if err := tmplAll.ExecuteTemplate(w, "issue_form.html", nil); err != nil {
683+
if err := tmplAll.ExecuteTemplate(w, "issue_form.html", data); err != nil {
669684
http.Error(w, err.Error(), http.StatusInternalServerError)
670685
}
671686
}
@@ -889,6 +904,30 @@ func openBrowser(url string) error {
889904
return cmd.Start()
890905
}
891906

907+
// detectUsername attempts to determine the current user's name from various sources.
908+
// It tries in order: git user.name, environment variables (USER, USERNAME, LOGNAME),
909+
// and falls back to "web-user" if nothing is found.
910+
func detectUsername() string {
911+
// Try git config user.name first
912+
cmd := exec.Command("git", "config", "--global", "user.name")
913+
if output, err := cmd.Output(); err == nil {
914+
name := strings.TrimSpace(string(output))
915+
if name != "" {
916+
return name
917+
}
918+
}
919+
920+
// Try environment variables
921+
for _, envVar := range []string{"USER", "USERNAME", "LOGNAME"} {
922+
if name := os.Getenv(envVar); name != "" {
923+
return name
924+
}
925+
}
926+
927+
// Fallback
928+
return "web-user"
929+
}
930+
892931
// handleAPIShutdown handles graceful shutdown requests from the web UI.
893932
// It responds with a JSON success message and triggers a graceful server shutdown in a goroutine.
894933
// Only POST requests are accepted; other methods receive a 405 Method Not Allowed error.

0 commit comments

Comments
 (0)