This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

appview/pages: add spindle selector in repo/fork pages

both pages now have an "advanced" section for users to configure
spindles, the default selection is "No Spindle"

Signed-off-by: oppiliappan <me@oppi.li>

author
oppiliappan
date (Jul 20, 2026, 8:51 PM +0100) commit bdebf8d1 parent 6d78fffa change-id uqxllrkz
+171 -4
+3 -1
appview/pages/pages.go
··· 694 694 695 695 type NewRepoParams struct { 696 696 BaseParams 697 - Knots []string 697 + Knots []string 698 + Spindles []string 698 699 } 699 700 700 701 func (p *Pages) NewRepo(w io.Writer, params NewRepoParams) error { ··· 704 705 type ForkRepoParams struct { 705 706 BaseParams 706 707 Knots []string 708 + Spindles []string 707 709 RepoInfo repoinfo.RepoInfo 708 710 } 709 711
+57 -1
appview/pages/templates/repo/fork.html
··· 124 124 </div> 125 125 </div> 126 126 127 - <div class="py-8 flex justify-end border-l border-gray-200 dark:border-gray-700 pl-6"> 127 + {{/* Step 3 — Advanced */}} 128 + <details class="group/advanced"> 129 + <summary class="flex gap-4 items-center relative border-l border-gray-200 dark:border-gray-700 pl-6 cursor-pointer list-none"> 130 + <div class="absolute top-1/2 -translate-y-1/2 -left-[11px]"> 131 + <div class="size-6 bg-gray-200 dark:bg-gray-600 dark:text-white rounded-full flex items-center justify-center"> 132 + {{ i "plus" "size-4" }} 133 + </div> 134 + </div> 135 + <div class="flex-1 flex flex-col gap-1"> 136 + <h2 class="text-lg font-medium dark:text-white">Advanced</h2> 137 + </div> 138 + </summary> 139 + 140 + <div class="flex gap-4 relative border-l border-gray-200 dark:border-gray-700 pl-6 pb-6"> 141 + <div class="flex-1 flex flex-col gap-2 pt-4"> 142 + <div class="space-y-4"> 143 + 144 + {{/* Spindle list */}} 145 + <div> 146 + <label class="block text-sm font-bold dark:text-white mb-1">Select a spindle</label> 147 + <div class="w-full dark:bg-gray-800 dark:text-white dark:border-gray-700 space-y-2"> 148 + <div class="flex items-center"> 149 + <input 150 + type="radio" 151 + name="spindle" 152 + value="" 153 + id="spindle-none" 154 + checked 155 + class="mr-2" 156 + /> 157 + <label for="spindle-none" class="dark:text-white">No spindle</label> 158 + </div> 159 + {{ range .Spindles }} 160 + <div class="flex items-center"> 161 + <input 162 + type="radio" 163 + name="spindle" 164 + value="{{ . }}" 165 + id="spindle-{{ . }}" 166 + class="mr-2" 167 + /> 168 + <label for="spindle-{{ . }}" class="dark:text-white lowercase">{{ . }}</label> 169 + </div> 170 + {{ end }} 171 + </div> 172 + <p class="text-sm text-gray-500 dark:text-gray-400 mt-1"> 173 + A spindle runs your CI workflows. 174 + You can also <a href="/settings/spindles" class="underline">register your own spindle</a>. 175 + </p> 176 + </div> 177 + 178 + </div> 179 + </div> 180 + </div> 181 + </details> 182 + 183 + <div class="py-8 flex justify-end"> 128 184 <button type="submit" class="btn-create flex items-center gap-2"> 129 185 {{ i "git-fork" "size-4" }} 130 186 Fork repository
+65 -2
appview/pages/templates/repo/new.html
··· 23 23 <form hx-post="/repo/new" hx-swap="none" hx-indicator="#spinner"> 24 24 {{ template "step-1" . }} 25 25 {{ template "step-2" . }} 26 + {{ template "step-3" . }} 26 27 27 28 <div class="mt-8 flex justify-end"> 28 29 <button type="submit" class="btn-create flex items-center gap-2"> ··· 63 64 {{ template "numberCircle" 2 }} 64 65 </div> 65 66 66 - <div class="flex-1"> 67 + <div class="flex-1 pb-12"> 67 68 <h2 class="text-lg font-medium dark:text-white">Configuration</h2> 68 69 <div class="text-sm text-gray-500 dark:text-gray-400 mb-4">Repository settings and hosting.</div> 69 70 ··· 73 74 </div> 74 75 </div> 75 76 </div> 77 + {{ end }} 78 + 79 + {{ define "step-3" }} 80 + <details class="group/advanced"> 81 + <summary class="flex gap-4 items-center relative border-l border-gray-200 dark:border-gray-700 pl-6 cursor-pointer list-none"> 82 + <div class="absolute -left-3 top-1/2 -translate-y-1/2"> 83 + <div class="w-6 h-6 bg-gray-200 dark:bg-gray-600 dark:text-white rounded-full flex items-center justify-center"> 84 + {{ i "plus" "w-4 h-4" }} 85 + </div> 86 + </div> 87 + 88 + <div class="flex-1"> 89 + <h2 class="text-lg font-medium dark:text-white">Advanced</h2> 90 + </div> 91 + </summary> 92 + 93 + <div class="flex gap-4 relative border-l border-gray-200 dark:border-gray-700 pl-6"> 94 + <div class="flex-1 pt-4"> 95 + <div class="space-y-2"> 96 + {{ template "spindle" . }} 97 + </div> 98 + </div> 99 + </div> 100 + </details> 76 101 {{ end }} 77 102 78 103 {{ define "name" }} ··· 171 196 </div> 172 197 {{ end }} 173 198 199 + {{ define "spindle" }} 200 + <!-- Spindle Selection --> 201 + <div> 202 + <label class="block text-sm font-bold dark:text-white mb-1"> 203 + Select a spindle 204 + </label> 205 + <div class="w-full space-y-2"> 206 + <div class="flex items-center"> 207 + <input 208 + type="radio" 209 + name="spindle" 210 + value="" 211 + class="mr-2" 212 + id="spindle-none" 213 + checked 214 + /> 215 + <label for="spindle-none" class="dark:text-white">No spindle</label> 216 + </div> 217 + {{ range .Spindles }} 218 + <div class="flex items-center"> 219 + <input 220 + type="radio" 221 + name="spindle" 222 + value="{{ . }}" 223 + class="mr-2" 224 + id="spindle-{{ . }}" 225 + /> 226 + <label for="spindle-{{ . }}" class="dark:text-white lowercase">{{ . }}</label> 227 + </div> 228 + {{ end }} 229 + </div> 230 + <p class="text-sm text-gray-500 dark:text-gray-400 mt-1"> 231 + A spindle runs your CI workflows. 232 + You can also <a href="/settings/spindles" class="underline">register your own spindle</a>. 233 + </p> 234 + </div> 235 + {{ end }} 236 + 174 237 {{ define "numberCircle" }} 175 - <div class="w-6 h-6 bg-gray-200 dark:bg-gray-600 rounded-full flex items-center justify-center text-sm font-medium mt-1"> 238 + <div class="w-6 h-6 bg-gray-200 dark:bg-gray-600 rounded-full flex items-center justify-center text-sm font-medium"> 176 239 {{.}} 177 240 </div> 178 241 {{ end }}
+22
appview/repo/repo.go
··· 1414 1414 user := rp.oauth.GetMultiAccountUser(r) 1415 1415 knots := rp.acl.KnotsForUser(r.Context(), user.Did) 1416 1416 1417 + spindles, err := rp.enforcer.GetSpindlesForUser(user.Did) 1418 + if err != nil { 1419 + l.Error("failed to fetch spindles", "err", err) 1420 + } 1421 + 1417 1422 rp.pages.ForkRepo(w, pages.ForkRepoParams{ 1418 1423 BaseParams: pages.BaseParamsFromContext(r.Context()), 1419 1424 Knots: knots, 1425 + Spindles: spindles, 1420 1426 RepoInfo: rp.repoResolver.GetRepoInfo(r, user), 1421 1427 }) 1422 1428 ··· 1433 1439 if !rp.acl.IsRepoCreateAllowed(r.Context(), targetKnot, user.Did) { 1434 1440 rp.pages.Notice(w, "repo", "You do not have permission to create a repo in this knot.") 1435 1441 return 1442 + } 1443 + 1444 + // optional spindle selection; validate the user is a member if provided 1445 + spindle := r.FormValue("spindle") 1446 + if spindle != "" { 1447 + validSpindles, err := rp.enforcer.GetSpindlesForUser(user.Did) 1448 + if err != nil { 1449 + l.Error("failed to fetch spindles", "err", err) 1450 + rp.pages.Notice(w, "repo", "Failed to configure spindle. Try again later.") 1451 + return 1452 + } 1453 + if !slices.Contains(validSpindles, spindle) { 1454 + rp.pages.Notice(w, "repo", "Invalid spindle selection.") 1455 + return 1456 + } 1436 1457 } 1437 1458 1438 1459 // choose a name for a fork ··· 1526 1547 Rkey: rkey, 1527 1548 Source: forkSource, 1528 1549 Description: forkDescription, 1550 + Spindle: spindle, 1529 1551 Created: time.Now(), 1530 1552 Labels: rp.config.Label.DefaultLabelDefs, 1531 1553 RepoDid: repoDid,
+24
appview/state/state.go
··· 7 7 "fmt" 8 8 "log/slog" 9 9 "net/http" 10 + "slices" 10 11 "strings" 11 12 "time" 12 13 ··· 443 444 user := s.oauth.GetMultiAccountUser(r) 444 445 knots := s.aclService.KnotsForUser(r.Context(), user.Did) 445 446 447 + spindles, err := s.enforcer.GetSpindlesForUser(user.Did) 448 + if err != nil { 449 + s.logger.Error("failed to fetch spindles", "err", err) 450 + } 451 + 446 452 s.pages.NewRepo(w, pages.NewRepoParams{ 447 453 BaseParams: pages.BaseParamsFromContext(r.Context()), 448 454 Knots: knots, 455 + Spindles: spindles, 449 456 }) 450 457 451 458 case http.MethodPost: ··· 487 494 s.pages.Notice(w, "repo", "Description must be 140 characters or fewer.") 488 495 return 489 496 } 497 + 498 + // optional spindle selection; validate the user is a member if provided 499 + spindle := r.FormValue("spindle") 500 + if spindle != "" { 501 + validSpindles, err := s.enforcer.GetSpindlesForUser(user.Did) 502 + if err != nil { 503 + l.Error("failed to fetch spindles", "err", err) 504 + s.pages.Notice(w, "repo", "Failed to configure spindle. Try again later.") 505 + return 506 + } 507 + if !slices.Contains(validSpindles, spindle) { 508 + s.pages.Notice(w, "repo", "Invalid spindle selection.") 509 + return 510 + } 511 + } 512 + l = l.With("spindle", spindle) 490 513 491 514 // ACL validation 492 515 if !s.aclService.IsRepoCreateAllowed(r.Context(), domain, user.Did) { ··· 564 587 Knot: domain, 565 588 Rkey: rkey, 566 589 Description: description, 590 + Spindle: spindle, 567 591 Created: time.Now(), 568 592 Labels: s.config.Label.DefaultLabelDefs, 569 593 RepoDid: repoDid,