this repo has no description

Compare changes

Choose any two refs to compare.

+9 -8
appview/issues/issues.go
··· 129 129 } 130 130 131 131 rp.pages.RepoSingleIssue(w, pages.RepoSingleIssueParams{ 132 - LoggedInUser: user, 133 - RepoInfo: rp.repoResolver.GetRepoInfo(r, user), 134 - Issue: issue, 135 - CommentList: issue.CommentList(), 136 - Backlinks: backlinks, 137 - Reactions: reactionMap, 138 - UserReacted: userReactions, 139 - LabelDefs: defs, 132 + LoggedInUser: user, 133 + RepoInfo: rp.repoResolver.GetRepoInfo(r, user), 134 + Issue: issue, 135 + CommentList: issue.CommentList(), 136 + Backlinks: backlinks, 137 + OrderedReactionKinds: models.OrderedReactionKinds, 138 + Reactions: reactionMap, 139 + UserReacted: userReactions, 140 + LabelDefs: defs, 140 141 }) 141 142 } 142 143
+15
appview/knots/knots.go
··· 40 40 Knotstream *eventconsumer.Consumer 41 41 } 42 42 43 + type tab = map[string]any 44 + 45 + var ( 46 + knotsTabs []tab = []tab{ 47 + {"Name": "profile", "Icon": "user"}, 48 + {"Name": "keys", "Icon": "key"}, 49 + {"Name": "emails", "Icon": "mail"}, 50 + {"Name": "notifications", "Icon": "bell"}, 51 + {"Name": "knots", "Icon": "volleyball"}, 52 + {"Name": "spindles", "Icon": "spool"}, 53 + } 54 + ) 55 + 43 56 func (k *Knots) Router() http.Handler { 44 57 r := chi.NewRouter() 45 58 ··· 71 84 k.Pages.Knots(w, pages.KnotsParams{ 72 85 LoggedInUser: user, 73 86 Registrations: registrations, 87 + Tabs: knotsTabs, 74 88 Tab: "knots", 75 89 }) 76 90 } ··· 134 148 Members: members, 135 149 Repos: repoMap, 136 150 IsOwner: true, 151 + Tabs: knotsTabs, 137 152 Tab: "knots", 138 153 }) 139 154 }
+39
appview/models/pipeline.go
··· 1 1 package models 2 2 3 3 import ( 4 + "fmt" 4 5 "slices" 6 + "strings" 5 7 "time" 6 8 7 9 "github.com/bluesky-social/indigo/atproto/syntax" ··· 50 52 } 51 53 52 54 return 0 55 + } 56 + 57 + // produces short summary of successes: 58 + // - "0/4" when zero successes of 4 workflows 59 + // - "4/4" when all successes of 4 workflows 60 + // - "0/0" when no workflows run in this pipeline 61 + func (p Pipeline) ShortStatusSummary() string { 62 + counts := make(map[spindle.StatusKind]int) 63 + for _, w := range p.Statuses { 64 + counts[w.Latest().Status] += 1 65 + } 66 + 67 + total := len(p.Statuses) 68 + successes := counts[spindle.StatusKindSuccess] 69 + 70 + return fmt.Sprintf("%d/%d", successes, total) 71 + } 72 + 73 + // produces a string of the form "3/4 success, 2/4 failed, 1/4 pending" 74 + func (p Pipeline) LongStatusSummary() string { 75 + counts := make(map[spindle.StatusKind]int) 76 + for _, w := range p.Statuses { 77 + counts[w.Latest().Status] += 1 78 + } 79 + 80 + total := len(p.Statuses) 81 + 82 + var result []string 83 + // finish states first, followed by start states 84 + states := append(spindle.FinishStates[:], spindle.StartStates[:]...) 85 + for _, state := range states { 86 + if count, ok := counts[state]; ok { 87 + result = append(result, fmt.Sprintf("%d/%d %s", count, total, state.String())) 88 + } 89 + } 90 + 91 + return strings.Join(result, ", ") 53 92 } 54 93 55 94 func (p Pipeline) Counts() map[string]int {
+20 -29
appview/pages/funcmap.go
··· 32 32 "tangled.org/core/crypto" 33 33 ) 34 34 35 - type tab map[string]string 36 - 37 35 func (p *Pages) funcMap() template.FuncMap { 38 36 return template.FuncMap{ 39 37 "split": func(s string) []string { ··· 336 334 }, 337 335 "deref": func(v any) any { 338 336 val := reflect.ValueOf(v) 339 - if val.Kind() == reflect.Ptr && !val.IsNil() { 337 + if val.Kind() == reflect.Pointer && !val.IsNil() { 340 338 return val.Elem().Interface() 341 339 } 342 340 return nil ··· 368 366 return p.AvatarUrl(handle, "") 369 367 }, 370 368 "langColor": enry.GetColor, 371 - "layoutSide": func() string { 372 - return "col-span-1 md:col-span-2 lg:col-span-3" 369 + "reverse": func(s any) any { 370 + if s == nil { 371 + return nil 372 + } 373 + 374 + v := reflect.ValueOf(s) 375 + 376 + if v.Kind() != reflect.Slice { 377 + return s 378 + } 379 + 380 + length := v.Len() 381 + reversed := reflect.MakeSlice(v.Type(), length, length) 382 + 383 + for i := range length { 384 + reversed.Index(i).Set(v.Index(length - 1 - i)) 385 + } 386 + 387 + return reversed.Interface() 373 388 }, 374 - "layoutCenter": func() string { 375 - return "col-span-1 md:col-span-8 lg:col-span-6" 376 - }, 377 - 378 389 "normalizeForHtmlId": func(s string) string { 379 390 normalized := strings.ReplaceAll(s, ":", "_") 380 391 normalized = strings.ReplaceAll(normalized, ".", "_") ··· 386 397 return "error" 387 398 } 388 399 return fp 389 - }, 390 - // constant values used to define a template 391 - "const": func() map[string]any { 392 - return map[string]any{ 393 - "OrderedReactionKinds": models.OrderedReactionKinds, 394 - // would be great to have ordered maps right about now 395 - "UserSettingsTabs": []tab{ 396 - {"Name": "profile", "Icon": "user"}, 397 - {"Name": "keys", "Icon": "key"}, 398 - {"Name": "emails", "Icon": "mail"}, 399 - {"Name": "notifications", "Icon": "bell"}, 400 - {"Name": "knots", "Icon": "volleyball"}, 401 - {"Name": "spindles", "Icon": "spool"}, 402 - }, 403 - "RepoSettingsTabs": []tab{ 404 - {"Name": "general", "Icon": "sliders-horizontal"}, 405 - {"Name": "access", "Icon": "users"}, 406 - {"Name": "pipelines", "Icon": "layers-2"}, 407 - }, 408 - } 409 400 }, 410 401 } 411 402 }
+36 -18
appview/pages/pages.go
··· 336 336 337 337 type UserProfileSettingsParams struct { 338 338 LoggedInUser *oauth.User 339 + Tabs []map[string]any 339 340 Tab string 340 341 } 341 342 ··· 374 375 type UserKeysSettingsParams struct { 375 376 LoggedInUser *oauth.User 376 377 PubKeys []models.PublicKey 378 + Tabs []map[string]any 377 379 Tab string 378 380 } 379 381 ··· 384 386 type UserEmailsSettingsParams struct { 385 387 LoggedInUser *oauth.User 386 388 Emails []models.Email 389 + Tabs []map[string]any 387 390 Tab string 388 391 } 389 392 ··· 394 397 type UserNotificationSettingsParams struct { 395 398 LoggedInUser *oauth.User 396 399 Preferences *models.NotificationPreferences 400 + Tabs []map[string]any 397 401 Tab string 398 402 } 399 403 ··· 413 417 type KnotsParams struct { 414 418 LoggedInUser *oauth.User 415 419 Registrations []models.Registration 420 + Tabs []map[string]any 416 421 Tab string 417 422 } 418 423 ··· 426 431 Members []string 427 432 Repos map[string][]models.Repo 428 433 IsOwner bool 434 + Tabs []map[string]any 429 435 Tab string 430 436 } 431 437 ··· 444 450 type SpindlesParams struct { 445 451 LoggedInUser *oauth.User 446 452 Spindles []models.Spindle 453 + Tabs []map[string]any 447 454 Tab string 448 455 } 449 456 ··· 453 460 454 461 type SpindleListingParams struct { 455 462 models.Spindle 463 + Tabs []map[string]any 456 464 Tab string 457 465 } 458 466 ··· 465 473 Spindle models.Spindle 466 474 Members []string 467 475 Repos map[string][]models.Repo 476 + Tabs []map[string]any 468 477 Tab string 469 478 } 470 479 ··· 872 881 SubscribedLabels map[string]struct{} 873 882 ShouldSubscribeAll bool 874 883 Active string 884 + Tabs []map[string]any 875 885 Tab string 876 886 Branches []types.Branch 877 887 } ··· 885 895 LoggedInUser *oauth.User 886 896 RepoInfo repoinfo.RepoInfo 887 897 Active string 898 + Tabs []map[string]any 888 899 Tab string 889 900 Collaborators []Collaborator 890 901 } ··· 898 909 LoggedInUser *oauth.User 899 910 RepoInfo repoinfo.RepoInfo 900 911 Active string 912 + Tabs []map[string]any 901 913 Tab string 902 914 Spindles []string 903 915 CurrentSpindle string ··· 935 947 Backlinks []models.RichReferenceLink 936 948 LabelDefs map[string]*models.LabelDefinition 937 949 938 - Reactions map[models.ReactionKind]models.ReactionDisplayData 939 - UserReacted map[models.ReactionKind]bool 950 + OrderedReactionKinds []models.ReactionKind 951 + Reactions map[models.ReactionKind]models.ReactionDisplayData 952 + UserReacted map[models.ReactionKind]bool 940 953 } 941 954 942 955 func (p *Pages) RepoSingleIssue(w io.Writer, params RepoSingleIssueParams) error { ··· 1090 1103 MergeCheck types.MergeCheckResponse 1091 1104 ResubmitCheck ResubmitResult 1092 1105 Pipelines map[string]models.Pipeline 1106 + Diff *types.NiceDiff 1107 + DiffOpts types.DiffOpts 1093 1108 1094 - Reactions map[models.ReactionKind]models.ReactionDisplayData 1095 - UserReacted map[models.ReactionKind]bool 1109 + OrderedReactionKinds []models.ReactionKind 1110 + Reactions map[models.ReactionKind]models.ReactionDisplayData 1111 + UserReacted map[models.ReactionKind]bool 1096 1112 1097 1113 LabelDefs map[string]*models.LabelDefinition 1098 1114 } ··· 1103 1119 } 1104 1120 1105 1121 type RepoPullPatchParams struct { 1106 - LoggedInUser *oauth.User 1107 - RepoInfo repoinfo.RepoInfo 1108 - Pull *models.Pull 1109 - Stack models.Stack 1110 - Diff *types.NiceDiff 1111 - Round int 1112 - Submission *models.PullSubmission 1113 - DiffOpts types.DiffOpts 1122 + LoggedInUser *oauth.User 1123 + RepoInfo repoinfo.RepoInfo 1124 + Pull *models.Pull 1125 + Stack models.Stack 1126 + Diff *types.NiceDiff 1127 + Round int 1128 + Submission *models.PullSubmission 1129 + OrderedReactionKinds []models.ReactionKind 1130 + DiffOpts types.DiffOpts 1114 1131 } 1115 1132 1116 1133 // this name is a mouthful ··· 1119 1136 } 1120 1137 1121 1138 type RepoPullInterdiffParams struct { 1122 - LoggedInUser *oauth.User 1123 - RepoInfo repoinfo.RepoInfo 1124 - Pull *models.Pull 1125 - Round int 1126 - Interdiff *patchutil.InterdiffResult 1127 - DiffOpts types.DiffOpts 1139 + LoggedInUser *oauth.User 1140 + RepoInfo repoinfo.RepoInfo 1141 + Pull *models.Pull 1142 + Round int 1143 + Interdiff *patchutil.InterdiffResult 1144 + OrderedReactionKinds []models.ReactionKind 1145 + DiffOpts types.DiffOpts 1128 1146 } 1129 1147 1130 1148 // this name is a mouthful
+1
appview/pages/templates/fragments/tabSelector.html
··· 9 9 {{ range $index, $value := $all }} 10 10 {{ $isActive := eq $value.Key $active }} 11 11 <a href="?{{ $name }}={{ $value.Key }}" 12 + hx-boost=true 12 13 {{ if $include }} 13 14 hx-get="?{{ $name }}={{ $value.Key }}" 14 15 hx-include="{{ $include }}"
+1 -1
appview/pages/templates/layouts/repobase.html
··· 1 1 {{ define "title" }}{{ .RepoInfo.FullName }}{{ end }} 2 2 3 3 {{ define "content" }} 4 - <section id="repo-header" class="mb-4 p-2 dark:text-white"> 4 + <section id="repo-header" class="mb-2 py-2 px-4 dark:text-white"> 5 5 <div class="text-lg flex flex-col sm:flex-row items-start gap-4 justify-between"> 6 6 <!-- left items --> 7 7 <div class="flex flex-col gap-2">
+1 -1
appview/pages/templates/repo/fragments/diff.html
··· 18 18 {{ range $idx, $hunk := $diff }} 19 19 {{ with $hunk }} 20 20 <details open id="file-{{ .Id }}" class="group border border-gray-200 dark:border-gray-700 w-full mx-auto rounded bg-white dark:bg-gray-800 drop-shadow-sm" tabindex="{{ add $idx 1 }}"> 21 - <summary class="list-none cursor-pointer sticky top-0"> 21 + <summary class="list-none cursor-pointer sticky top-12"> 22 22 <div id="diff-file-header" class="rounded cursor-pointer bg-white dark:bg-gray-800 flex justify-between"> 23 23 <div id="left-side-items" class="p-2 flex gap-2 items-center overflow-x-auto"> 24 24 <span class="group-open:hidden inline">{{ i "chevron-right" "w-4 h-4" }}</span>
+1 -8
appview/pages/templates/repo/fragments/diffChangedFiles.html
··· 1 1 {{ define "repo/fragments/diffChangedFiles" }} 2 - {{ $stat := .Stat }} 3 2 {{ $fileTree := fileTree .ChangedFiles }} 4 3 <section class="overflow-x-auto text-sm px-6 py-2 border border-gray-200 dark:border-gray-700 w-full mx-auto min-h-full rounded bg-white dark:bg-gray-800 drop-shadow-sm"> 5 - <div class="diff-stat"> 6 - <div class="flex gap-2 items-center"> 7 - <strong class="text-sm uppercase dark:text-gray-200">Changed files</strong> 8 - {{ template "repo/fragments/diffStatPill" $stat }} 9 - </div> 10 - {{ template "repo/fragments/fileTree" $fileTree }} 11 - </div> 4 + {{ template "repo/fragments/fileTree" $fileTree }} 12 5 </section> 13 6 {{ end }}
+22 -25
appview/pages/templates/repo/fragments/diffOpts.html
··· 1 1 {{ define "repo/fragments/diffOpts" }} 2 - <section class="flex flex-col gap-2 overflow-x-auto text-sm px-6 py-2 border border-gray-200 dark:border-gray-700 w-full mx-auto rounded bg-white dark:bg-gray-800 drop-shadow-sm"> 3 - <strong class="text-sm uppercase dark:text-gray-200">options</strong> 4 - {{ $active := "unified" }} 5 - {{ if .Split }} 6 - {{ $active = "split" }} 7 - {{ end }} 2 + {{ $active := "unified" }} 3 + {{ if .Split }} 4 + {{ $active = "split" }} 5 + {{ end }} 8 6 9 - {{ $unified := 10 - (dict 11 - "Key" "unified" 12 - "Value" "unified" 13 - "Icon" "square-split-vertical" 14 - "Meta" "") }} 15 - {{ $split := 16 - (dict 17 - "Key" "split" 18 - "Value" "split" 19 - "Icon" "square-split-horizontal" 20 - "Meta" "") }} 21 - {{ $values := list $unified $split }} 7 + {{ $unified := 8 + (dict 9 + "Key" "unified" 10 + "Value" "unified" 11 + "Icon" "square-split-vertical" 12 + "Meta" "") }} 13 + {{ $split := 14 + (dict 15 + "Key" "split" 16 + "Value" "split" 17 + "Icon" "square-split-horizontal" 18 + "Meta" "") }} 19 + {{ $values := list $unified $split }} 22 20 23 - {{ template "fragments/tabSelector" 24 - (dict 25 - "Name" "diff" 26 - "Values" $values 27 - "Active" $active) }} 28 - </section> 21 + {{ template "fragments/tabSelector" 22 + (dict 23 + "Name" "diff" 24 + "Values" $values 25 + "Active" $active) }} 29 26 {{ end }} 30 27
-50
appview/pages/templates/repo/fragments/reactions.html
··· 1 - {{ define "repo/fragments/reactions" }} 2 - <div class="flex flex-wrap items-center gap-2"> 3 - {{- $reactions := .Reactions -}} 4 - {{- $userReacted := .UserReacted -}} 5 - {{- $threadAt := .ThreadAt -}} 6 - 7 - {{ template "reactionsPopup" }} 8 - {{ range $kind := const.OrderedReactionKinds }} 9 - {{ $reactionData := index $reactions $kind }} 10 - {{ template "repo/fragments/reaction" 11 - (dict 12 - "Kind" $kind 13 - "Count" $reactionData.Count 14 - "IsReacted" (index $userReacted $kind) 15 - "ThreadAt" $threadAt 16 - "Users" $reactionData.Users) }} 17 - {{ end }} 18 - </div> 19 - {{ end }} 20 - 21 - {{ define "reactionsPopup" }} 22 - <details 23 - id="reactionsPopUp" 24 - class="relative inline-block" 25 - > 26 - <summary 27 - class="flex justify-center items-center min-w-8 min-h-8 rounded border border-gray-200 dark:border-gray-700 28 - hover:bg-gray-50 29 - hover:border-gray-300 30 - dark:hover:bg-gray-700 31 - dark:hover:border-gray-600 32 - cursor-pointer list-none" 33 - > 34 - {{ i "smile" "size-4" }} 35 - </summary> 36 - <div 37 - class="absolute flex left-0 z-10 mt-4 rounded bg-white dark:bg-gray-800 dark:text-white border border-gray-200 dark:border-gray-700 shadow-lg" 38 - > 39 - {{ range $kind := const.OrderedReactionKinds }} 40 - <button 41 - id="reactBtn-{{ $kind }}" 42 - class="size-12 hover:bg-gray-100 dark:hover:bg-gray-700" 43 - hx-on:click="this.parentElement.parentElement.removeAttribute('open')" 44 - > 45 - {{ $kind }} 46 - </button> 47 - {{ end }} 48 - </div> 49 - </details> 50 - {{ end }}
+30
appview/pages/templates/repo/fragments/reactionsPopUp.html
··· 1 + {{ define "repo/fragments/reactionsPopUp" }} 2 + <details 3 + id="reactionsPopUp" 4 + class="relative inline-block" 5 + > 6 + <summary 7 + class="flex justify-center items-center min-w-8 min-h-8 rounded border border-gray-200 dark:border-gray-700 8 + hover:bg-gray-50 9 + hover:border-gray-300 10 + dark:hover:bg-gray-700 11 + dark:hover:border-gray-600 12 + cursor-pointer list-none" 13 + > 14 + {{ i "smile" "size-4" }} 15 + </summary> 16 + <div 17 + class="absolute flex left-0 z-10 mt-4 rounded bg-white dark:bg-gray-800 dark:text-white border border-gray-200 dark:border-gray-700 shadow-lg" 18 + > 19 + {{ range $kind := . }} 20 + <button 21 + id="reactBtn-{{ $kind }}" 22 + class="size-12 hover:bg-gray-100 dark:hover:bg-gray-700" 23 + hx-on:click="this.parentElement.parentElement.removeAttribute('open')" 24 + > 25 + {{ $kind }} 26 + </button> 27 + {{ end }} 28 + </div> 29 + </details> 30 + {{ end }}
+35 -22
appview/pages/templates/repo/issues/fragments/commentList.html
··· 1 1 {{ define "repo/issues/fragments/commentList" }} 2 - <div class="flex flex-col gap-8"> 2 + <div class="flex flex-col gap-4"> 3 3 {{ range $item := .CommentList }} 4 4 {{ template "commentListing" (list $ .) }} 5 5 {{ end }} ··· 19 19 <div class="rounded border border-gray-200 dark:border-gray-700 w-full overflow-hidden shadow-sm bg-gray-50 dark:bg-gray-800/50"> 20 20 {{ template "topLevelComment" $params }} 21 21 22 - <div class="relative ml-4 border-l-2 border-gray-200 dark:border-gray-700"> 22 + <div class="relative ml-10 border-l-2 border-gray-200 dark:border-gray-700"> 23 23 {{ range $index, $reply := $comment.Replies }} 24 - <div class="relative "> 25 - <!-- Horizontal connector --> 26 - <div class="absolute left-0 top-6 w-4 h-1 bg-gray-200 dark:bg-gray-700"></div> 27 - 28 - <div class="pl-2"> 29 - {{ 30 - template "replyComment" 31 - (dict 32 - "RepoInfo" $root.RepoInfo 33 - "LoggedInUser" $root.LoggedInUser 34 - "Issue" $root.Issue 35 - "Comment" $reply) 36 - }} 37 - </div> 24 + <div class="-ml-4"> 25 + {{ 26 + template "replyComment" 27 + (dict 28 + "RepoInfo" $root.RepoInfo 29 + "LoggedInUser" $root.LoggedInUser 30 + "Issue" $root.Issue 31 + "Comment" $reply) 32 + }} 38 33 </div> 39 34 {{ end }} 40 35 </div> ··· 44 39 {{ end }} 45 40 46 41 {{ define "topLevelComment" }} 47 - <div class="rounded px-6 py-4 bg-white dark:bg-gray-800"> 48 - {{ template "repo/issues/fragments/issueCommentHeader" . }} 49 - {{ template "repo/issues/fragments/issueCommentBody" . }} 42 + <div class="rounded px-6 py-4 bg-white dark:bg-gray-800 flex gap-2 "> 43 + <div class="flex-shrink-0"> 44 + <img 45 + src="{{ tinyAvatar .Comment.Did }}" 46 + alt="" 47 + class="rounded-full size-8 mr-1 border-2 border-gray-100 dark:border-gray-900" 48 + /> 49 + </div> 50 + <div class="flex-1 min-w-0"> 51 + {{ template "repo/issues/fragments/issueCommentHeader" . }} 52 + {{ template "repo/issues/fragments/issueCommentBody" . }} 53 + </div> 50 54 </div> 51 55 {{ end }} 52 56 53 57 {{ define "replyComment" }} 54 - <div class="p-4 w-full mx-auto overflow-hidden"> 55 - {{ template "repo/issues/fragments/issueCommentHeader" . }} 56 - {{ template "repo/issues/fragments/issueCommentBody" . }} 58 + <div class="py-4 pr-4 w-full mx-auto overflow-hidden flex gap-2 "> 59 + <div class="flex-shrink-0"> 60 + <img 61 + src="{{ tinyAvatar .Comment.Did }}" 62 + alt="" 63 + class="rounded-full size-8 mr-1 border-2 border-gray-100 dark:border-gray-900" 64 + /> 65 + </div> 66 + <div class="flex-1 min-w-0"> 67 + {{ template "repo/issues/fragments/issueCommentHeader" . }} 68 + {{ template "repo/issues/fragments/issueCommentBody" . }} 69 + </div> 57 70 </div> 58 71 {{ end }}
-63
appview/pages/templates/repo/issues/fragments/globalIssueListing.html
··· 1 - {{ define "repo/issues/fragments/globalIssueListing" }} 2 - <div class="flex flex-col gap-2"> 3 - {{ range .Issues }} 4 - <div class="rounded drop-shadow-sm bg-white px-6 py-4 dark:bg-gray-800 dark:border-gray-700"> 5 - <div class="pb-2 mb-3"> 6 - <div class="flex items-center gap-3 mb-2"> 7 - <a 8 - href="/{{ resolve .Repo.Did }}/{{ .Repo.Name }}" 9 - class="text-blue-600 dark:text-blue-400 font-medium hover:underline text-sm" 10 - > 11 - {{ resolve .Repo.Did }}/{{ .Repo.Name }} 12 - </a> 13 - </div> 14 - <a 15 - href="/{{ resolve .Repo.Did }}/{{ .Repo.Name }}/issues/{{ .IssueId }}" 16 - class="no-underline hover:underline" 17 - > 18 - {{ .Title | description }} 19 - <span class="text-gray-500">#{{ .IssueId }}</span> 20 - </a> 21 - </div> 22 - <div class="text-sm text-gray-500 dark:text-gray-400 flex flex-wrap items-center gap-1"> 23 - {{ $bgColor := "bg-gray-800 dark:bg-gray-700" }} 24 - {{ $icon := "ban" }} 25 - {{ $state := "closed" }} 26 - {{ if .Open }} 27 - {{ $bgColor = "bg-green-600 dark:bg-green-700" }} 28 - {{ $icon = "circle-dot" }} 29 - {{ $state = "open" }} 30 - {{ end }} 31 - 32 - <span class="inline-flex items-center rounded px-2 py-[5px] {{ $bgColor }} text-sm"> 33 - {{ i $icon "w-3 h-3 mr-1.5 text-white dark:text-white" }} 34 - <span class="text-white dark:text-white">{{ $state }}</span> 35 - </span> 36 - 37 - <span class="ml-1"> 38 - {{ template "user/fragments/picHandleLink" .Did }} 39 - </span> 40 - 41 - <span class="before:content-['ยท']"> 42 - {{ template "repo/fragments/time" .Created }} 43 - </span> 44 - 45 - <span class="before:content-['ยท']"> 46 - {{ $s := "s" }} 47 - {{ if eq (len .Comments) 1 }} 48 - {{ $s = "" }} 49 - {{ end }} 50 - <a href="/{{ resolve .Repo.Did }}/{{ .Repo.Name }}/issues/{{ .IssueId }}" class="text-gray-500 dark:text-gray-400">{{ len .Comments }} comment{{$s}}</a> 51 - </span> 52 - 53 - {{ $state := .Labels }} 54 - {{ range $k, $d := $.LabelDefs }} 55 - {{ range $v, $s := $state.GetValSet $d.AtUri.String }} 56 - {{ template "labels/fragments/label" (dict "def" $d "val" $v "withPrefix" true) }} 57 - {{ end }} 58 - {{ end }} 59 - </div> 60 - </div> 61 - {{ end }} 62 - </div> 63 - {{ end }}
+2 -1
appview/pages/templates/repo/issues/fragments/issueCommentHeader.html
··· 1 1 {{ define "repo/issues/fragments/issueCommentHeader" }} 2 2 <div class="flex flex-wrap items-center gap-2 text-sm text-gray-500 dark:text-gray-400 "> 3 - {{ template "user/fragments/picHandleLink" .Comment.Did }} 3 + {{ resolve .Comment.Did }} 4 4 {{ template "hats" $ }} 5 + <span class="before:content-['ยท']"></span> 5 6 {{ template "timestamp" . }} 6 7 {{ $isCommentOwner := and .LoggedInUser (eq .LoggedInUser.Did .Comment.Did) }} 7 8 {{ if and $isCommentOwner (not .Comment.Deleted) }}
+2 -2
appview/pages/templates/repo/issues/fragments/issueListing.html
··· 21 21 {{ $state = "open" }} 22 22 {{ end }} 23 23 24 - <span class="inline-flex items-center rounded px-2 py-[5px] {{ $bgColor }} text-sm"> 24 + <span class="inline-flex items-center rounded px-2 py-[5px] {{ $bgColor }}"> 25 25 {{ i $icon "w-3 h-3 mr-1.5 text-white dark:text-white" }} 26 - <span class="text-white dark:text-white">{{ $state }}</span> 26 + <span class="text-white dark:text-white text-sm">{{ $state }}</span> 27 27 </span> 28 28 29 29 <span class="ml-1">
+1 -1
appview/pages/templates/repo/issues/fragments/putIssue.html
··· 18 18 <textarea 19 19 name="body" 20 20 id="body" 21 - rows="6" 21 + rows="15" 22 22 class="w-full resize-y" 23 23 placeholder="Describe your issue. Markdown is supported." 24 24 >{{ if .Issue }}{{ .Issue.Body }}{{ end }}</textarea>
+3 -3
appview/pages/templates/repo/issues/fragments/replyIssueCommentPlaceholder.html
··· 1 1 {{ define "repo/issues/fragments/replyIssueCommentPlaceholder" }} 2 - <div class="p-2 border-t flex gap-2 items-center border-gray-300 dark:border-gray-700"> 2 + <div class="py-2 px-6 border-t flex gap-2 items-center border-gray-300 dark:border-gray-700"> 3 3 {{ if .LoggedInUser }} 4 4 <img 5 5 src="{{ tinyAvatar .LoggedInUser.Did }}" 6 6 alt="" 7 - class="rounded-full h-6 w-6 mr-1 border border-gray-300 dark:border-gray-700" 7 + class="rounded-full size-8 mr-1 border-2 border-gray-300 dark:border-gray-700" 8 8 /> 9 9 {{ end }} 10 10 <input 11 - class="w-full py-2 border-none focus:outline-none" 11 + class="w-full p-0 border-none focus:outline-none" 12 12 placeholder="Leave a reply..." 13 13 hx-get="/{{ .RepoInfo.FullName }}/issues/{{ .Issue.IssueId }}/comment/{{ .Comment.Id }}/reply" 14 14 hx-trigger="focus"
+26 -10
appview/pages/templates/repo/issues/issue.html
··· 35 35 {{ if .Issue.Body }} 36 36 <article id="body" class="mt-4 prose dark:prose-invert">{{ .Issue.Body | markdown }}</article> 37 37 {{ end }} 38 - <div class="mt-4"> 39 - {{ template "repo/fragments/reactions" 40 - (dict "Reactions" .Reactions 41 - "UserReacted" .UserReacted 42 - "ThreadAt" .Issue.AtUri) }} 38 + <div class="flex flex-wrap gap-2 items-stretch mt-4"> 39 + {{ template "issueReactions" . }} 43 40 </div> 44 41 </section> 45 42 {{ end }} ··· 61 58 {{ $icon = "circle-dot" }} 62 59 {{ end }} 63 60 <div class="inline-flex items-center gap-2"> 64 - <div id="state" 65 - class="inline-flex items-center rounded px-3 py-1 {{ $bgColor }}"> 66 - {{ i $icon "w-4 h-4 mr-1.5 text-white" }} 67 - <span class="text-white">{{ .Issue.State }}</span> 68 - </div> 61 + <span class="inline-flex items-center rounded px-2 py-[5px] {{ $bgColor }}"> 62 + {{ i $icon "w-3 h-3 mr-1.5 text-white dark:text-white" }} 63 + <span class="text-white dark:text-white text-sm">{{ .Issue.State }}</span> 64 + </span> 65 + 69 66 <span class="text-gray-500 dark:text-gray-400 text-sm flex flex-wrap items-center gap-1"> 70 67 opened by 71 68 {{ template "user/fragments/picHandleLink" .Issue.Did }} ··· 109 106 {{ i "loader-circle" "size-3 animate-spin hidden group-[.htmx-request]:inline" }} 110 107 </a> 111 108 {{ end }} 109 + 110 + {{ define "issueReactions" }} 111 + <div class="flex items-center gap-2"> 112 + {{ template "repo/fragments/reactionsPopUp" .OrderedReactionKinds }} 113 + {{ range $kind := .OrderedReactionKinds }} 114 + {{ $reactionData := index $.Reactions $kind }} 115 + {{ 116 + template "repo/fragments/reaction" 117 + (dict 118 + "Kind" $kind 119 + "Count" $reactionData.Count 120 + "IsReacted" (index $.UserReacted $kind) 121 + "ThreadAt" $.Issue.AtUri 122 + "Users" $reactionData.Users) 123 + }} 124 + {{ end }} 125 + </div> 126 + {{ end }} 127 + 112 128 113 129 {{ define "repoAfter" }} 114 130 <div class="flex flex-col gap-4 mt-4">
+60 -69
appview/pages/templates/repo/pipelines/fragments/pipelineSymbol.html
··· 1 1 {{ define "repo/pipelines/fragments/pipelineSymbol" }} 2 - <div class="cursor-pointer"> 3 - {{ $c := .Counts }} 4 - {{ $statuses := .Statuses }} 5 - {{ $total := len $statuses }} 6 - {{ $success := index $c "success" }} 7 - {{ $fail := index $c "failed" }} 8 - {{ $timeout := index $c "timeout" }} 9 - {{ $empty := eq $total 0 }} 10 - {{ $allPass := eq $success $total }} 11 - {{ $allFail := eq $fail $total }} 12 - {{ $allTimeout := eq $timeout $total }} 13 - 14 - {{ if $empty }} 15 - <div class="flex gap-1 items-center"> 16 - {{ i "hourglass" "size-4 text-gray-600 dark:text-gray-400 " }} 17 - <span>0/{{ $total }}</span> 18 - </div> 19 - {{ else if $allPass }} 20 - <div class="flex gap-1 items-center"> 21 - {{ i "check" "size-4 text-green-600" }} 22 - <span>{{ $total }}/{{ $total }}</span> 23 - </div> 24 - {{ else if $allFail }} 25 - <div class="flex gap-1 items-center"> 26 - {{ i "x" "size-4 text-red-500" }} 27 - <span>0/{{ $total }}</span> 28 - </div> 29 - {{ else if $allTimeout }} 30 - <div class="flex gap-1 items-center"> 31 - {{ i "clock-alert" "size-4 text-orange-500" }} 32 - <span>0/{{ $total }}</span> 33 - </div> 2 + <div class="cursor-pointer flex gap-2 items-center"> 3 + {{ template "symbol" .Pipeline }} 4 + {{ if .ShortSummary }} 5 + {{ .Pipeline.ShortStatusSummary }} 34 6 {{ else }} 35 - {{ $radius := f64 8 }} 36 - {{ $circumference := mulf64 2.0 (mulf64 3.1416 $radius) }} 37 - {{ $offset := 0.0 }} 38 - <div class="flex gap-1 items-center"> 39 - <svg class="w-4 h-4 transform -rotate-90" viewBox="0 0 20 20"> 40 - <circle cx="10" cy="10" r="{{ $radius }}" fill="none" stroke="#f3f4f633" stroke-width="2"/> 7 + {{ .Pipeline.LongStatusSummary }} 8 + {{ end }} 9 + </div> 10 + {{ end }} 41 11 42 - {{ range $kind, $count := $c }} 43 - {{ $color := "" }} 44 - {{ if or (eq $kind "pending") (eq $kind "running") }} 45 - {{ $color = "#eab308" }} {{/* amber-500 */}} 46 - {{ else if eq $kind "success" }} 47 - {{ $color = "#10b981" }} {{/* green-500 */}} 48 - {{ else if eq $kind "cancelled" }} 49 - {{ $color = "#6b7280" }} {{/* gray-500 */}} 50 - {{ else if eq $kind "timeout" }} 51 - {{ $color = "#fb923c" }} {{/* orange-400 */}} 52 - {{ else }} 53 - {{ $color = "#ef4444" }} {{/* red-500 for failed or unknown */}} 54 - {{ end }} 12 + {{ define "symbol" }} 13 + {{ $c := .Counts }} 14 + {{ $statuses := .Statuses }} 15 + {{ $total := len $statuses }} 16 + {{ $success := index $c "success" }} 17 + {{ $fail := index $c "failed" }} 18 + {{ $timeout := index $c "timeout" }} 19 + {{ $empty := eq $total 0 }} 20 + {{ $allPass := eq $success $total }} 21 + {{ $allFail := eq $fail $total }} 22 + {{ $allTimeout := eq $timeout $total }} 55 23 56 - {{ $percent := divf64 (f64 $count) (f64 $total) }} 57 - {{ $length := mulf64 $percent $circumference }} 58 - 59 - <circle 60 - cx="10" cy="10" r="{{ $radius }}" 61 - fill="none" 62 - stroke="{{ $color }}" 63 - stroke-width="2" 64 - stroke-dasharray="{{ printf "%.2f %.2f" $length (subf64 $circumference $length) }}" 65 - stroke-dashoffset="{{ printf "%.2f" (negf64 $offset) }}" 66 - /> 67 - {{ $offset = addf64 $offset $length }} 68 - {{ end }} 69 - </svg> 70 - <span>{{ $success }}/{{ $total }}</span> 71 - </div> 72 - {{ end }} 73 - </div> 24 + {{ if $empty }} 25 + {{ i "hourglass" "size-4 text-gray-600 dark:text-gray-400 " }} 26 + {{ else if $allPass }} 27 + {{ i "check" "size-4 text-green-600 dark:text-green-500" }} 28 + {{ else if $allFail }} 29 + {{ i "x" "size-4 text-red-600 dark:text-red-500" }} 30 + {{ else if $allTimeout }} 31 + {{ i "clock-alert" "size-4 text-orange-500" }} 32 + {{ else }} 33 + {{ $radius := f64 8 }} 34 + {{ $circumference := mulf64 2.0 (mulf64 3.1416 $radius) }} 35 + {{ $offset := 0.0 }} 36 + <svg class="w-4 h-4 transform -rotate-90" viewBox="0 0 20 20"> 37 + <circle cx="10" cy="10" r="{{ $radius }}" fill="none" class="stroke-gray-200 dark:stroke-gray-700" stroke-width="2"/> 38 + {{ range $kind, $count := $c }} 39 + {{ $colorClass := "" }} 40 + {{ if or (eq $kind "pending") (eq $kind "running") }} 41 + {{ $colorClass = "stroke-yellow-600 dark:stroke-yellow-500" }} 42 + {{ else if eq $kind "success" }} 43 + {{ $colorClass = "stroke-green-600 dark:stroke-green-500" }} 44 + {{ else if eq $kind "cancelled" }} 45 + {{ $colorClass = "stroke-gray-600 dark:stroke-gray-500" }} 46 + {{ else if eq $kind "timeout" }} 47 + {{ $colorClass = "stroke-orange-600 dark:stroke-orange-500" }} 48 + {{ else }} 49 + {{ $colorClass = "stroke-red-600 dark:stroke-red-500" }} 50 + {{ end }} 51 + {{ $percent := divf64 (f64 $count) (f64 $total) }} 52 + {{ $length := mulf64 $percent $circumference }} 53 + <circle 54 + cx="10" cy="10" r="{{ $radius }}" 55 + fill="none" 56 + class="{{ $colorClass }}" 57 + stroke-width="2" 58 + stroke-dasharray="{{ printf "%.2f %.2f" $length (subf64 $circumference $length) }}" 59 + stroke-dashoffset="{{ printf "%.2f" (negf64 $offset) }}" 60 + /> 61 + {{ $offset = addf64 $offset $length }} 62 + {{ end }} 63 + </svg> 64 + {{ end }} 74 65 {{ end }}
+1 -1
appview/pages/templates/repo/pipelines/fragments/pipelineSymbolLong.html
··· 4 4 <div class="relative inline-block"> 5 5 <details class="relative"> 6 6 <summary class="cursor-pointer list-none"> 7 - {{ template "repo/pipelines/fragments/pipelineSymbol" .Pipeline }} 7 + {{ template "repo/pipelines/fragments/pipelineSymbol" (dict "Pipeline" $pipeline "ShortSummary" true) }} 8 8 </summary> 9 9 {{ template "repo/pipelines/fragments/tooltip" $ }} 10 10 </details>
+17 -17
appview/pages/templates/repo/pulls/fragments/pullActions.html
··· 22 22 {{ $isLastRound := eq $roundNumber $lastIdx }} 23 23 {{ $isSameRepoBranch := .Pull.IsBranchBased }} 24 24 {{ $isUpToDate := .ResubmitCheck.No }} 25 - <div id="actions-{{$roundNumber}}" class="flex flex-wrap gap-2 relative"> 25 + <div id="actions-{{$roundNumber}}" class="flex flex-wrap gap-2 relative p-2"> 26 26 <button 27 27 hx-get="/{{ .RepoInfo.FullName }}/pulls/{{ .Pull.PullId }}/round/{{ $roundNumber }}/comment" 28 28 hx-target="#actions-{{$roundNumber}}" 29 29 hx-swap="outerHtml" 30 - class="btn p-2 flex items-center gap-2 no-underline hover:no-underline group"> 31 - {{ i "message-square-plus" "w-4 h-4" }} 32 - <span>comment</span> 30 + class="btn-flat p-2 flex items-center gap-2 no-underline hover:no-underline group"> 31 + {{ i "message-square-plus" "w-4 h-4 inline group-[.htmx-request]:hidden" }} 33 32 {{ i "loader-circle" "w-4 h-4 animate-spin hidden group-[.htmx-request]:inline" }} 33 + comment 34 34 </button> 35 35 {{ if .BranchDeleteStatus }} 36 36 <button 37 37 hx-delete="/{{ .BranchDeleteStatus.Repo.Did }}/{{ .BranchDeleteStatus.Repo.Name }}/branches" 38 38 hx-vals='{"branch": "{{ .BranchDeleteStatus.Branch }}" }' 39 39 hx-swap="none" 40 - class="btn p-2 flex items-center gap-2 no-underline hover:no-underline group text-red-500 hover:text-red-700 dark:text-red-400 dark:hover:text-red-300"> 40 + class="btn-flat p-2 flex items-center gap-2 no-underline hover:no-underline group text-red-500 hover:text-red-700 dark:text-red-400 dark:hover:text-red-300"> 41 41 {{ i "git-branch" "w-4 h-4" }} 42 42 <span>delete branch</span> 43 43 {{ i "loader-circle" "w-4 h-4 animate-spin hidden group-[.htmx-request]:inline" }} ··· 52 52 hx-post="/{{ .RepoInfo.FullName }}/pulls/{{ .Pull.PullId }}/merge" 53 53 hx-swap="none" 54 54 hx-confirm="Are you sure you want to merge pull #{{ .Pull.PullId }} into the `{{ .Pull.TargetBranch }}` branch?" 55 - class="btn p-2 flex items-center gap-2 group" {{ $disabled }}> 56 - {{ i "git-merge" "w-4 h-4" }} 57 - <span>merge{{if $stackCount}} {{$stackCount}}{{end}}</span> 55 + class="btn-flat p-2 flex items-center gap-2 group" {{ $disabled }}> 56 + {{ i "git-merge" "w-4 h-4 inline group-[.htmx-request]:hidden" }} 58 57 {{ i "loader-circle" "w-4 h-4 animate-spin hidden group-[.htmx-request]:inline" }} 58 + merge{{if $stackCount}} {{$stackCount}}{{end}} 59 59 </button> 60 60 {{ end }} 61 61 ··· 74 74 {{ end }} 75 75 76 76 hx-disabled-elt="#resubmitBtn" 77 - class="btn p-2 flex items-center gap-2 disabled:opacity-50 disabled:cursor-not-allowed group" {{ $disabled }} 77 + class="btn-flat p-2 flex items-center gap-2 disabled:opacity-50 disabled:cursor-not-allowed group" {{ $disabled }} 78 78 79 79 {{ if $disabled }} 80 80 title="Update this branch to resubmit this pull request" ··· 82 82 title="Resubmit this pull request" 83 83 {{ end }} 84 84 > 85 - {{ i "rotate-ccw" "w-4 h-4" }} 86 - <span>resubmit</span> 85 + {{ i "rotate-ccw" "w-4 h-4 inline group-[.htmx-request]:hidden" }} 87 86 {{ i "loader-circle" "w-4 h-4 animate-spin hidden group-[.htmx-request]:inline" }} 87 + resubmit 88 88 </button> 89 89 {{ end }} 90 90 ··· 92 92 <button 93 93 hx-post="/{{ .RepoInfo.FullName }}/pulls/{{ .Pull.PullId }}/close" 94 94 hx-swap="none" 95 - class="btn p-2 flex items-center gap-2 group"> 96 - {{ i "ban" "w-4 h-4" }} 97 - <span>close</span> 95 + class="btn-flat p-2 flex items-center gap-2 group"> 96 + {{ i "ban" "w-4 h-4 inline group-[.htmx-request]:hidden" }} 98 97 {{ i "loader-circle" "w-4 h-4 animate-spin hidden group-[.htmx-request]:inline" }} 98 + close 99 99 </button> 100 100 {{ end }} 101 101 ··· 103 103 <button 104 104 hx-post="/{{ .RepoInfo.FullName }}/pulls/{{ .Pull.PullId }}/reopen" 105 105 hx-swap="none" 106 - class="btn p-2 flex items-center gap-2 group"> 107 - {{ i "refresh-ccw-dot" "w-4 h-4" }} 108 - <span>reopen</span> 106 + class="btn-flat p-2 flex items-center gap-2 group"> 107 + {{ i "refresh-ccw-dot" "w-4 h-4 inline group-[.htmx-request]:hidden" }} 109 108 {{ i "loader-circle" "w-4 h-4 animate-spin hidden group-[.htmx-request]:inline" }} 109 + reopen 110 110 </button> 111 111 {{ end }} 112 112 </div>
+22 -12
appview/pages/templates/repo/pulls/fragments/pullHeader.html
··· 1 1 {{ define "repo/pulls/fragments/pullHeader" }} 2 - <header class="pb-4"> 2 + <header class="pb-2"> 3 3 <h1 class="text-2xl dark:text-white"> 4 4 {{ .Pull.Title | description }} 5 5 <span class="text-gray-500 dark:text-gray-400">#{{ .Pull.PullId }}</span> ··· 17 17 {{ $icon = "git-merge" }} 18 18 {{ end }} 19 19 20 - <section class="mt-2"> 20 + <section> 21 21 <div class="flex items-center gap-2"> 22 - <div 23 - id="state" 24 - class="inline-flex items-center rounded px-3 py-1 {{ $bgColor }}" 22 + <span 23 + class="inline-flex items-center rounded px-2 py-[5px] {{ $bgColor }} text-sm" 25 24 > 26 - {{ i $icon "w-4 h-4 mr-1.5 text-white" }} 25 + {{ i $icon "w-3 h-3 mr-1.5 text-white" }} 27 26 <span class="text-white">{{ .Pull.State.String }}</span> 28 - </div> 27 + </span> 29 28 <span class="text-gray-500 dark:text-gray-400 text-sm flex flex-wrap items-center gap-1"> 30 29 opened by 31 30 {{ template "user/fragments/picHandleLink" .Pull.OwnerDid }} ··· 64 63 </article> 65 64 {{ end }} 66 65 67 - <div class="mt-2"> 68 - {{ template "repo/fragments/reactions" 69 - (dict "Reactions" .Reactions 70 - "UserReacted" .UserReacted 71 - "ThreadAt" .Pull.AtUri) }} 66 + {{ with .OrderedReactionKinds }} 67 + <div class="flex items-center gap-2 mt-2"> 68 + {{ template "repo/fragments/reactionsPopUp" . }} 69 + {{ range $kind := . }} 70 + {{ $reactionData := index $.Reactions $kind }} 71 + {{ 72 + template "repo/fragments/reaction" 73 + (dict 74 + "Kind" $kind 75 + "Count" $reactionData.Count 76 + "IsReacted" (index $.UserReacted $kind) 77 + "ThreadAt" $.Pull.AtUri 78 + "Users" $reactionData.Users) 79 + }} 80 + {{ end }} 72 81 </div> 82 + {{ end }} 73 83 </section> 74 84 75 85
+39 -24
appview/pages/templates/repo/pulls/fragments/pullNewComment.html
··· 1 1 {{ define "repo/pulls/fragments/pullNewComment" }} 2 2 <div 3 3 id="pull-comment-card-{{ .RoundNumber }}" 4 - class="bg-white dark:bg-gray-800 rounded drop-shadow-sm p-4 relative w-full flex flex-col gap-2"> 5 - <div class="text-sm text-gray-500 dark:text-gray-400"> 6 - {{ resolve .LoggedInUser.Did }} 7 - </div> 4 + class="w-full flex flex-col gap-2"> 5 + {{ template "user/fragments/picHandleLink" .LoggedInUser.Did }} 8 6 <form 9 7 hx-post="/{{ .RepoInfo.FullName }}/pulls/{{ .Pull.PullId }}/round/{{ .RoundNumber }}/comment" 10 - hx-indicator="#create-comment-spinner" 11 8 hx-swap="none" 12 - class="w-full flex flex-wrap gap-2" 9 + hx-on::after-request="if(event.detail.successful) this.reset()" 10 + hx-disabled-elt="#reply-{{ .RoundNumber }}" 11 + class="w-full flex flex-wrap gap-2 group" 13 12 > 14 13 <textarea 15 14 name="body" 16 15 class="w-full p-2 rounded border border-gray-200" 16 + rows=8 17 17 placeholder="Add to the discussion..."></textarea 18 18 > 19 - <button type="submit" class="btn flex items-center gap-2"> 20 - {{ i "message-square" "w-4 h-4" }} 21 - <span>comment</span> 22 - <span id="create-comment-spinner" class="group"> 23 - {{ i "loader-circle" "w-4 h-4 animate-spin hidden group-[.htmx-request]:inline" }} 24 - </span> 25 - </button> 26 - <button 27 - type="button" 28 - class="btn flex items-center gap-2 group" 29 - hx-get="/{{ .RepoInfo.FullName }}/pulls/{{ .Pull.PullId }}/round/{{ .RoundNumber }}/actions" 30 - hx-swap="outerHTML" 31 - hx-target="#pull-comment-card-{{ .RoundNumber }}" 32 - > 33 - {{ i "x" "w-4 h-4" }} 34 - <span>cancel</span> 35 - {{ i "loader-circle" "w-4 h-4 animate-spin hidden group-[.htmx-request]:inline" }} 36 - </button> 19 + {{ template "replyActions" . }} 37 20 <div id="pull-comment"></div> 38 21 </form> 39 22 </div> 40 23 {{ end }} 24 + 25 + {{ define "replyActions" }} 26 + <div class="flex flex-wrap items-stretch justify-end gap-2 text-gray-500 dark:text-gray-400 text-sm w-full"> 27 + {{ template "cancel" . }} 28 + {{ template "reply" . }} 29 + </div> 30 + {{ end }} 31 + 32 + {{ define "cancel" }} 33 + <button 34 + type="button" 35 + class="btn text-red-500 dark:text-red-400 flex gap-2 items-center group" 36 + hx-get="/{{ .RepoInfo.FullName }}/pulls/{{ .Pull.PullId }}/round/{{ .RoundNumber }}/actions" 37 + hx-swap="outerHTML" 38 + hx-target="#actions-{{.RoundNumber}}" 39 + > 40 + {{ i "x" "w-4 h-4" }} 41 + <span>cancel</span> 42 + </button> 43 + {{ end }} 44 + 45 + {{ define "reply" }} 46 + <button 47 + type="submit" 48 + id="reply-{{ .RoundNumber }}" 49 + class="btn-create flex items-center gap-2"> 50 + {{ i "reply" "w-4 h-4 inline group-[.htmx-request]:hidden" }} 51 + {{ i "loader-circle" "w-4 h-4 animate-spin hidden group-[.htmx-request]:inline" }} 52 + reply 53 + </button> 54 + {{ end }} 55 +
+20
appview/pages/templates/repo/pulls/fragments/replyPullCommentPlaceholder.html
··· 1 + {{ define "repo/pulls/fragments/replyPullCommentPlaceholder" }} 2 + <div class="py-2 px-6 border-t flex gap-2 items-center border-gray-300 dark:border-gray-700"> 3 + {{ if .LoggedInUser }} 4 + <img 5 + src="{{ tinyAvatar .LoggedInUser.Did }}" 6 + alt="" 7 + class="rounded-full size-8 mr-1 border-2 border-gray-300 dark:border-gray-700" 8 + /> 9 + {{ end }} 10 + <input 11 + class="w-full p-0 border-none focus:outline-none" 12 + placeholder="Leave a reply..." 13 + hx-get="/{{ .Submission.ID }}/reply" 14 + hx-trigger="focus" 15 + hx-target="closest div" 16 + hx-swap="outerHTML" 17 + > 18 + </input> 19 + </div> 20 + {{ end }}
+1 -1
appview/pages/templates/repo/pulls/fragments/summarizedPullHeader.html
··· 18 18 {{ $lastSubmission := index .Submissions $latestRound }} 19 19 {{ $commentCount := len $lastSubmission.Comments }} 20 20 {{ if and $pipeline $pipeline.Id }} 21 - {{ template "repo/pipelines/fragments/pipelineSymbol" $pipeline }} 21 + {{ template "repo/pipelines/fragments/pipelineSymbol" (dict "Pipeline" $pipeline "ShortSummary" true) }} 22 22 <span class="before:content-['ยท'] before:select-none text-gray-500 dark:text-gray-400"></span> 23 23 {{ end }} 24 24 <span>
+334 -77
appview/pages/templates/repo/pulls/pull.html
··· 6 6 {{ template "repo/pulls/fragments/og" (dict "RepoInfo" .RepoInfo "Pull" .Pull) }} 7 7 {{ end }} 8 8 9 + {{ define "mainLayout" }} 10 + <div class="px-1 col-span-full flex-grow flex flex-col gap-4"> 11 + {{ block "contentLayout" . }} 12 + {{ block "content" . }}{{ end }} 13 + {{ end }} 14 + </div> 15 + {{ end }} 16 + 9 17 {{ define "repoContentLayout" }} 10 - <div class="grid grid-cols-1 md:grid-cols-10 gap-4 w-full"> 11 - <div class="col-span-1 md:col-span-8"> 12 - <section class="bg-white dark:bg-gray-800 p-6 rounded relative w-full mx-auto dark:text-white"> 18 + <div class="grid grid-cols-1 md:grid-cols-10 gap-y-2 gap-x-4 w-full"> 19 + <div class="col-span-1 md:col-span-7"> 20 + <section class="bg-white dark:bg-gray-800 p-6 rounded relative w-full mx-auto dark:text-white h-full"> 13 21 {{ block "repoContent" . }}{{ end }} 14 22 </section> 15 23 {{ block "repoAfter" . }}{{ end }} 16 24 </div> 17 - <div class="col-span-1 md:col-span-2 flex flex-col gap-6"> 25 + <div class="col-span-1 md:col-span-3 flex flex-col gap-6"> 18 26 {{ template "repo/fragments/labelPanel" 19 27 (dict "RepoInfo" $.RepoInfo 20 28 "Defs" $.LabelDefs ··· 26 34 "Backlinks" $.Backlinks) }} 27 35 {{ template "repo/fragments/externalLinkPanel" $.Pull.AtUri }} 28 36 </div> 37 + 38 + <style> 39 + #filesToggle:checked ~ div label[for="filesToggle"] .show-text { display: none; } 40 + #filesToggle:checked ~ div label[for="filesToggle"] .hide-text { display: inline; } 41 + #filesToggle:not(:checked) ~ div label[for="filesToggle"] .hide-text { display: none; } 42 + 43 + #filesToggle:checked ~ div div#files { width: 10vw; margin-right: 1rem; } 44 + #filesToggle:not(:checked) ~ div div#files { width: 0; display: hidden; margin-right: 0; } 45 + 46 + #subsToggle:checked ~ div div#subs { width: 25vw; margin-left: 1rem; } 47 + #subsToggle:not(:checked) ~ div div#subs { width: 0; display: hidden; margin-left: 0; } 48 + </style> 49 + 50 + <!-- Checkboxes must come first as siblings --> 51 + <input type="checkbox" id="filesToggle" class="peer/files hidden" checked/> 52 + <input type="checkbox" id="subsToggle" class="peer/subs hidden" checked/> 53 + 54 + <!-- Top bar with controls --> 55 + <div class="sticky top-0 z-30 bg-slate-100 dark:bg-gray-900 flex items-center gap-2 col-span-full h-12"> 56 + <label for="filesToggle" class="inline-flex items-center justify-center rounded cursor-pointer p-2 text-normal font-normal normalcase"> 57 + <span class="show-text">{{ i "panel-left-open" "size-5" }}</span> 58 + <span class="hide-text">{{ i "panel-left-close" "size-5" }}</span> 59 + </label> 60 + {{ template "repo/fragments/diffStatPill" .Diff.Stat }} 61 + {{ .Diff.Stat.FilesChanged }} changed file{{ if ne .Diff.Stat.FilesChanged 1 }}s{{ end }} 62 + <div class="flex-grow"></div> 63 + {{ template "repo/fragments/diffOpts" .DiffOpts }} 64 + <label for="subsToggle" class="inline-flex items-center justify-center rounded cursor-pointer p-2"> 65 + {{ i "message-square-more" "size-5" }} 66 + </label> 67 + </div> 68 + 69 + <div class="flex col-span-full"> 70 + <!-- left panel --> 71 + <div id="files" class="w-0 overflow-hidden sticky top-12 max-h-screen overflow-y-auto pb-12"> 72 + {{ template "repo/fragments/diffChangedFiles" .Diff }} 73 + </div> 74 + 75 + <!-- main content --> 76 + <div class="flex-1 min-w-0 sticky top-12 pb-12"> 77 + {{ template "repo/fragments/diff" (list .Diff .DiffOpts) }} 78 + </div> 79 + 80 + <!-- right panel --> 81 + <div id="subs" class="w-0 overflow-hidden max-h-screen flex flex-col sticky top-12 pb-12"> 82 + <div class="z-20 sticky top-0 rounded-t p-3 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700"> 83 + <h2 class="font-bold uppercase">history</h2> 84 + </div> 85 + <div class="flex flex-col-reverse gap-4 overflow-y-auto"> 86 + {{ template "submissions2" . }} 87 + </div> 88 + </div> 89 + </div> 29 90 </div> 30 91 {{ end }} 31 92 32 93 {{ define "repoContent" }} 33 94 {{ template "repo/pulls/fragments/pullHeader" . }} 34 - 35 95 {{ if .Pull.IsStacked }} 36 96 <div class="mt-8"> 37 97 {{ template "repo/pulls/fragments/pullStack" . }} ··· 40 100 {{ end }} 41 101 42 102 {{ define "repoAfter" }} 43 - <section id="submissions" class="mt-4"> 44 - <div class="flex flex-col gap-4"> 45 - {{ block "submissions" . }} {{ end }} 103 + <div id="pull-close"></div> 104 + <div id="pull-reopen"></div> 105 + {{ end }} 106 + 107 + {{ define "submissions2" }} 108 + {{ $lastIdx := sub (len .Pull.Submissions) 1 }} 109 + {{ range $ridx, $item := reverse .Pull.Submissions }} 110 + {{ $idx := sub $lastIdx $ridx }} 111 + <div class="rounded border border-gray-200 dark:border-gray-700 w-full shadow-sm bg-gray-50 dark:bg-gray-800/50"> 112 + {{ with $item }} 113 + {{ $patches := .AsFormatPatch }} 114 + {{ $round := .RoundNumber }} 115 + <div class="rounded px-6 py-4 bg-white dark:bg-gray-800 flex gap-2"> 116 + <div class="flex-shrink-0"> 117 + <img 118 + src="{{ tinyAvatar $.Pull.OwnerDid }}" 119 + alt="" 120 + class="rounded-full size-8 mr-1 border-2 border-gray-100 dark:border-gray-900" 121 + /> 122 + </div> 123 + <!-- right column: name and body in two rows --> 124 + <div class="flex-1 min-w-0 flex flex-col gap-1"> 125 + <div class="flex gap-2 items-center justify-between mb-1"> 126 + <span class="inline-flex items-center gap-2 text-sm text-gray-500 dark:text-gray-400"> 127 + {{ resolve $.Pull.OwnerDid }} submitted v{{ $round }} 128 + <span class="select-none before:content-['\00B7']"></span> 129 + <a class="text-gray-500 dark:text-gray-400 hover:text-gray-500" href="#round-#{{ $round }}">{{ template "repo/fragments/shortTimeAgo" .Created }}</a> 130 + </span> 131 + {{ if ne $idx 0 }} 132 + <a class="flex items-center gap-2 no-underline hover:no-underline text-sm" 133 + hx-boost="true" 134 + href="/{{ $.RepoInfo.FullName }}/pulls/{{ $.Pull.PullId }}/round/{{$round}}/interdiff"> 135 + {{ i "chevrons-left-right-ellipsis" "w-4 h-4 rotate-90" }} 136 + <span class="hidden md:inline">interdiff</span> 137 + </a> 138 + {{ end }} 139 + </div> 140 + <details class="group"> 141 + <summary class="list-none cursor-pointer flex items-center gap-2"> 142 + <span>{{ i "git-commit-horizontal" "w-4 h-4" }}</span> 143 + {{ len $patches }} commit{{ if ne (len $patches) 1 }}s{{ end }} 144 + </summary> 145 + {{ range $patches }} 146 + <div id="commit-{{.SHA}}" class="py-1 relative w-full md:max-w-3/5 md:w-fit flex flex-col text-gray-600 dark:text-gray-300"> 147 + <div class="flex items-baseline gap-2"> 148 + <div> 149 + <!-- attempt to resolve $fullRepo: this is possible only on non-deleted forks and branches --> 150 + {{ $fullRepo := "" }} 151 + {{ if and $.Pull.IsForkBased $.Pull.PullSource.Repo }} 152 + {{ $fullRepo = printf "%s/%s" $.Pull.OwnerDid $.Pull.PullSource.Repo.Name }} 153 + {{ else if $.Pull.IsBranchBased }} 154 + {{ $fullRepo = $.RepoInfo.FullName }} 155 + {{ end }} 156 + 157 + <!-- if $fullRepo was resolved, link to it, otherwise just span without a link --> 158 + {{ if $fullRepo }} 159 + <a href="/{{ $fullRepo }}/commit/{{ .SHA }}" class="font-mono text-gray-600 dark:text-gray-300">{{ slice .SHA 0 8 }}</a> 160 + {{ else }} 161 + <span class="font-mono">{{ slice .SHA 0 8 }}</span> 162 + {{ end }} 163 + </div> 164 + 165 + <div> 166 + <span>{{ .Title | description }}</span> 167 + {{ if gt (len .Body) 0 }} 168 + <button 169 + class="py-1/2 px-1 mx-2 bg-gray-200 hover:bg-gray-400 rounded dark:bg-gray-700 dark:hover:bg-gray-600" 170 + hx-on:click="document.getElementById('body-{{$round}}-{{.SHA}}').classList.toggle('hidden')" 171 + > 172 + {{ i "ellipsis" "w-3 h-3" }} 173 + </button> 174 + {{ end }} 175 + {{ if gt (len .Body) 0 }} 176 + <p id="body-{{$round}}-{{.SHA}}" class="hidden mt-1 text-sm pb-2">{{ nl2br .Body }}</p> 177 + {{ end }} 178 + </div> 179 + </div> 180 + </div> 181 + {{ end }} 182 + </details> 183 + <div> 184 + {{ block "pipelineStatus" (list $ .) }} {{ end }} 185 + </div> 186 + {{ if eq $lastIdx .RoundNumber }} 187 + {{ block "mergeCheck" $ }} {{ end }} 188 + {{ end }} 189 + </div> 190 + </div> 191 + <div class="relative ml-10 border-l-2 border-gray-200 dark:border-gray-700"> 192 + {{ range $cidx, $c := .Comments }} 193 + <div id="comment-{{$c.ID}}" class="flex gap-2 -ml-4 py-4 w-full mx-auto"> 194 + <!-- left column: profile picture --> 195 + <div class="flex-shrink-0"> 196 + <img 197 + src="{{ tinyAvatar $c.OwnerDid }}" 198 + alt="" 199 + class="rounded-full size-8 mr-1 border-2 border-gray-100 dark:border-gray-900" 200 + /> 201 + </div> 202 + <!-- right column: name and body in two rows --> 203 + <div class="flex-1 min-w-0"> 204 + <!-- Row 1: Author and timestamp --> 205 + <div class="text-sm text-gray-500 dark:text-gray-400 flex items-center gap-1"> 206 + <span>{{ resolve $c.OwnerDid }}</span> 207 + <span class="before:content-['ยท']"></span> 208 + <a class="text-gray-500 dark:text-gray-400 hover:text-gray-500 dark:hover:text-gray-300" href="#comment-{{.ID}}">{{ template "repo/fragments/time" $c.Created }}</a> 209 + </div> 210 + <!-- Row 2: Body text --> 211 + <div class="prose dark:prose-invert mt-1"> 212 + {{ $c.Body | markdown }} 213 + </div> 214 + </div> 215 + </div> 216 + {{ end }} 217 + </div> 218 + {{ end }} 219 + {{ if eq $lastIdx .RoundNumber }} 220 + {{ block "mergeStatus" $ }} {{ end }} 221 + {{ block "resubmitStatus" $ }} {{ end }} 222 + {{ end }} 223 + {{ if $.LoggedInUser }} 224 + {{ template "repo/pulls/fragments/pullActions" 225 + (dict 226 + "LoggedInUser" $.LoggedInUser 227 + "Pull" $.Pull 228 + "RepoInfo" $.RepoInfo 229 + "RoundNumber" .RoundNumber 230 + "MergeCheck" $.MergeCheck 231 + "ResubmitCheck" $.ResubmitCheck 232 + "BranchDeleteStatus" $.BranchDeleteStatus 233 + "Stack" $.Stack) }} 234 + {{ else }} 235 + <div class="bg-amber-50 dark:bg-amber-900 border border-amber-500 rounded drop-shadow-sm p-2 relative flex gap-2 items-center"> 236 + <a href="/signup" class="btn-create py-0 hover:no-underline hover:text-white flex items-center gap-2"> 237 + sign up 238 + </a> 239 + <span class="text-gray-500 dark:text-gray-400">or</span> 240 + <a href="/login" class="underline">login</a> 241 + to add to the discussion 242 + </div> 243 + {{ end }} 244 + </div> 245 + {{ end }} 246 + {{ end }} 247 + 248 + {{ define "newComment" }} 249 + {{ $root := index . 0 }} 250 + {{ $submission := index . 1 }} 251 + <form 252 + id="comment-form" 253 + hx-post="/{{ $root.RepoInfo.FullName }}/pulls/{{ $root.Pull.PullId }}/round/{{ $submission.RoundNumber }}/comment" 254 + hx-on::after-request="if(event.detail.successful) this.reset()" 255 + > 256 + <div class="bg-white dark:bg-gray-800 rounded drop-shadow-sm py-4 px-4 relative w-full"> 257 + <div class="text-sm pb-2 text-gray-500 dark:text-gray-400"> 258 + {{ template "user/fragments/picHandleLink" $root.LoggedInUser.Did }} 46 259 </div> 47 - </section> 260 + <textarea 261 + id="comment-textarea" 262 + name="body" 263 + class="w-full p-2 rounded border border-gray-200 dark:border-gray-700" 264 + placeholder="Add to the discussion" 265 + rows="8" 266 + ></textarea> 267 + <div id="pull-comment"></div> 268 + </div> 269 + {{ template "replyActions" . }} 270 + </form> 271 + {{ end }} 48 272 49 - <div id="pull-close"></div> 50 - <div id="pull-reopen"></div> 273 + {{ define "replyActions" }} 274 + <div class="flex flex-wrap items-stretch justify-end gap-2 text-gray-500 dark:text-gray-400 text-sm"> 275 + {{ template "cancel" . }} 276 + {{ template "reply" . }} 277 + </div> 278 + {{ end }} 279 + 280 + {{ define "cancel" }} 281 + <button 282 + class="btn text-red-500 dark:text-red-400 flex gap-2 items-center group" 283 + hx-get="TODO" 284 + hx-target="TODO" 285 + hx-swap="outerHTML"> 286 + {{ i "x" "size-4" }} 287 + cancel 288 + </button> 289 + {{ end }} 290 + 291 + {{ define "reply" }} 292 + <button 293 + id="TODO" 294 + type="submit" 295 + class="btn-create flex items-center gap-2 no-underline hover:no-underline"> 296 + {{ i "reply" "w-4 h-4 inline group-[.htmx-request]:hidden" }} 297 + {{ i "loader-circle" "w-4 h-4 animate-spin hidden group-[.htmx-request]:inline" }} 298 + reply 299 + </button> 51 300 {{ end }} 52 301 53 302 {{ define "submissions" }} ··· 214 463 {{ end }} 215 464 {{ end }} 216 465 466 + {{ define "mergeCheck" }} 467 + {{ $isOpen := .Pull.State.IsOpen }} 468 + {{ if and $isOpen .MergeCheck .MergeCheck.Error }} 469 + <div class="flex items-center gap-2"> 470 + {{ i "triangle-alert" "w-4 h-4 text-red-500 dark:text-red-300" }} 471 + {{ .MergeCheck.Error }} 472 + </div> 473 + {{ else if and $isOpen .MergeCheck .MergeCheck.IsConflicted }} 474 + <details class="group"> 475 + <summary class="flex items-center justify-between cursor-pointer list-none"> 476 + <div class="flex items-center gap-2 "> 477 + {{ i "triangle-alert" "w-4 h-4" }} 478 + <span class="font-medium">merge conflicts detected</span> 479 + </div> 480 + <div> 481 + <span class="group-open:hidden inline">{{ i "chevrons-up-down" "w-4 h-4" }}</span> 482 + <span class="hidden group-open:inline">{{ i "chevrons-down-up" "w-4 h-4" }}</span> 483 + </div> 484 + </summary> 485 + {{ if gt (len .MergeCheck.Conflicts) 0 }} 486 + <ul class="space-y-1 mt-2"> 487 + {{ range .MergeCheck.Conflicts }} 488 + {{ if .Filename }} 489 + <li class="flex items-center"> 490 + {{ i "file-warning" "inline-flex w-4 h-4 mr-1.5 text-red-500 dark:text-red-300 flex-shrink-0" }} 491 + <span class="font-mono" style="word-break: keep-all; overflow-wrap: break-word;">{{ .Filename }}</span> 492 + </li> 493 + {{ else if .Reason }} 494 + <li class="flex items-center"> 495 + {{ i "file-warning" "w-4 h-4 mr-1.5 text-red-500 dark:text-red-300" }} 496 + <span>{{.Reason}}</span> 497 + </li> 498 + {{ end }} 499 + {{ end }} 500 + </ul> 501 + {{ end }} 502 + </details> 503 + {{ else if and $isOpen .MergeCheck }} 504 + <div class="flex items-center gap-2"> 505 + {{ i "check" "w-4 h-4 text-green-600 dark:text-green-500" }} 506 + <span>no conflicts, ready to merge</span> 507 + </div> 508 + {{ end }} 509 + {{ end }} 510 + 217 511 {{ define "mergeStatus" }} 218 512 {{ if .Pull.State.IsClosed }} 219 - <div class="bg-gray-50 dark:bg-gray-700 border border-black dark:border-gray-500 rounded drop-shadow-sm px-6 py-2 relative w-fit"> 513 + <div class="bg-gray-50 dark:bg-gray-700 border border-black dark:border-gray-500 rounded drop-shadow-sm px-6 py-2 relative"> 220 514 <div class="flex items-center gap-2 text-black dark:text-white"> 221 515 {{ i "ban" "w-4 h-4" }} 222 516 <span class="font-medium">closed without merging</span ··· 224 518 </div> 225 519 </div> 226 520 {{ else if .Pull.State.IsMerged }} 227 - <div class="bg-purple-50 dark:bg-purple-900 border border-purple-500 rounded drop-shadow-sm px-6 py-2 relative w-fit"> 521 + <div class="bg-purple-50 dark:bg-purple-900 border border-purple-500 rounded drop-shadow-sm px-6 py-2 relative"> 228 522 <div class="flex items-center gap-2 text-purple-500 dark:text-purple-300"> 229 523 {{ i "git-merge" "w-4 h-4" }} 230 524 <span class="font-medium">pull request successfully merged</span ··· 232 526 </div> 233 527 </div> 234 528 {{ else if .Pull.State.IsDeleted }} 235 - <div class="bg-red-50 dark:bg-red-900 border border-red-500 rounded drop-shadow-sm px-6 py-2 relative w-fit"> 529 + <div class="bg-red-50 dark:bg-red-900 border border-red-500 rounded drop-shadow-sm px-6 py-2 relative"> 236 530 <div class="flex items-center gap-2 text-red-500 dark:text-red-300"> 237 531 {{ i "git-pull-request-closed" "w-4 h-4" }} 238 532 <span class="font-medium">This pull has been deleted (possibly by jj abandon or jj squash)</span> 239 533 </div> 240 534 </div> 241 - {{ else if and .MergeCheck .MergeCheck.Error }} 242 - <div class="bg-red-50 dark:bg-red-900 border border-red-500 rounded drop-shadow-sm px-6 py-2 relative w-fit"> 243 - <div class="flex items-center gap-2 text-red-500 dark:text-red-300"> 244 - {{ i "triangle-alert" "w-4 h-4" }} 245 - <span class="font-medium">{{ .MergeCheck.Error }}</span> 246 - </div> 247 - </div> 248 - {{ else if and .MergeCheck .MergeCheck.IsConflicted }} 249 - <div class="bg-red-50 dark:bg-red-900 border border-red-500 rounded drop-shadow-sm px-6 py-2 relative w-fit"> 250 - <div class="flex flex-col gap-2 text-red-500 dark:text-red-300"> 251 - <div class="flex items-center gap-2"> 252 - {{ i "triangle-alert" "w-4 h-4" }} 253 - <span class="font-medium">merge conflicts detected</span> 254 - </div> 255 - {{ if gt (len .MergeCheck.Conflicts) 0 }} 256 - <ul class="space-y-1"> 257 - {{ range .MergeCheck.Conflicts }} 258 - {{ if .Filename }} 259 - <li class="flex items-center"> 260 - {{ i "file-warning" "w-4 h-4 mr-1.5 text-red-500 dark:text-red-300" }} 261 - <span class="font-mono">{{ .Filename }}</span> 262 - </li> 263 - {{ else if .Reason }} 264 - <li class="flex items-center"> 265 - {{ i "file-warning" "w-4 h-4 mr-1.5 text-red-500 dark:text-red-300" }} 266 - <span>{{.Reason}}</span> 267 - </li> 268 - {{ end }} 269 - {{ end }} 270 - </ul> 271 - {{ end }} 272 - </div> 273 - </div> 274 - {{ else if .MergeCheck }} 275 - <div class="bg-green-50 dark:bg-green-900 border border-green-500 rounded drop-shadow-sm px-6 py-2 relative w-fit"> 276 - <div class="flex items-center gap-2 text-green-500 dark:text-green-300"> 277 - {{ i "circle-check-big" "w-4 h-4" }} 278 - <span class="font-medium">no conflicts, ready to merge</span> 279 - </div> 280 - </div> 281 535 {{ end }} 282 536 {{ end }} 283 537 284 538 {{ define "resubmitStatus" }} 285 539 {{ if .ResubmitCheck.Yes }} 286 - <div class="bg-amber-50 dark:bg-amber-900 border border-amber-500 rounded drop-shadow-sm px-6 py-2 relative w-fit"> 540 + <div class="bg-amber-50 dark:bg-amber-900 border border-amber-500 rounded drop-shadow-sm px-6 py-2 relative"> 287 541 <div class="flex items-center gap-2 text-amber-500 dark:text-amber-300"> 288 542 {{ i "triangle-alert" "w-4 h-4" }} 289 543 <span class="font-medium">this branch has been updated, consider resubmitting</span> ··· 299 553 {{ with $pipeline }} 300 554 {{ $id := .Id }} 301 555 {{ if .Statuses }} 302 - <div class="max-w-80 grid grid-cols-1 bg-white dark:bg-gray-800 rounded border border-gray-200 dark:border-gray-700 divide-y divide-gray-200 dark:divide-gray-700"> 303 - {{ range $name, $all := .Statuses }} 304 - <a href="/{{ $root.RepoInfo.FullName }}/pipelines/{{ $id }}/workflow/{{ $name }}" class="no-underline hover:no-underline hover:bg-gray-100/25 hover:dark:bg-gray-700/25"> 305 - <div 306 - class="flex gap-2 items-center justify-between p-2"> 307 - {{ $lastStatus := $all.Latest }} 308 - {{ $kind := $lastStatus.Status.String }} 556 + <details> 557 + <summary class="cursor-pointer list-none">{{ template "repo/pipelines/fragments/pipelineSymbol" (dict "Pipeline" $pipeline "ShortSummary" false) }}</summary> 558 + <div class="my-2 grid grid-cols-1 bg-white dark:bg-gray-800 rounded border border-gray-200 dark:border-gray-700 divide-y divide-gray-200 dark:divide-gray-700"> 559 + {{ range $name, $all := .Statuses }} 560 + <a href="/{{ $root.RepoInfo.FullName }}/pipelines/{{ $id }}/workflow/{{ $name }}" class="no-underline hover:no-underline hover:bg-gray-100/25 hover:dark:bg-gray-700/25"> 561 + <div 562 + class="flex gap-2 items-center justify-between p-2"> 563 + {{ $lastStatus := $all.Latest }} 564 + {{ $kind := $lastStatus.Status.String }} 309 565 310 - <div id="left" class="flex items-center gap-2 flex-shrink-0"> 311 - {{ template "repo/pipelines/fragments/workflowSymbol" $all }} 312 - {{ $name }} 313 - </div> 314 - <div id="right" class="flex items-center gap-2 flex-shrink-0"> 315 - <span class="font-bold">{{ $kind }}</span> 316 - {{ if .TimeTaken }} 317 - {{ template "repo/fragments/duration" .TimeTaken }} 318 - {{ else }} 319 - {{ template "repo/fragments/shortTimeAgo" $lastStatus.Created }} 320 - {{ end }} 321 - </div> 566 + <div id="left" class="flex items-center gap-2 flex-shrink-0"> 567 + {{ template "repo/pipelines/fragments/workflowSymbol" $all }} 568 + {{ $name }} 569 + </div> 570 + <div id="right" class="flex items-center gap-2 flex-shrink-0"> 571 + <span class="font-bold">{{ $kind }}</span> 572 + {{ if .TimeTaken }} 573 + {{ template "repo/fragments/duration" .TimeTaken }} 574 + {{ else }} 575 + {{ template "repo/fragments/shortTimeAgo" $lastStatus.Created }} 576 + {{ end }} 577 + </div> 578 + </div> 579 + </a> 580 + {{ end }} 322 581 </div> 323 - </a> 324 - {{ end }} 325 - </div> 582 + </details> 326 583 {{ end }} 327 584 {{ end }} 328 585 {{ end }}
+1 -1
appview/pages/templates/repo/pulls/pulls.html
··· 136 136 {{ $pipeline := index $.Pipelines .LatestSha }} 137 137 {{ if and $pipeline $pipeline.Id }} 138 138 <span class="before:content-['ยท']"></span> 139 - {{ template "repo/pipelines/fragments/pipelineSymbol" $pipeline }} 139 + {{ template "repo/pipelines/fragments/pipelineSymbol" (dict "Pipeline" $pipeline "ShortSummary" true) }} 140 140 {{ end }} 141 141 142 142 {{ $state := .Labels }}
+2 -1
appview/pages/templates/repo/settings/fragments/sidebar.html
··· 1 1 {{ define "repo/settings/fragments/sidebar" }} 2 2 {{ $active := .Tab }} 3 + {{ $tabs := .Tabs }} 3 4 <div class="sticky top-2 grid grid-cols-1 rounded border border-gray-200 dark:border-gray-700 divide-y divide-gray-200 dark:divide-gray-700 shadow-inner"> 4 5 {{ $activeTab := "bg-white dark:bg-gray-700 drop-shadow-sm" }} 5 6 {{ $inactiveTab := "bg-gray-100 dark:bg-gray-800" }} 6 - {{ range const.RepoSettingsTabs }} 7 + {{ range $tabs }} 7 8 <a href="/{{ $.RepoInfo.FullName }}/settings?tab={{.Name}}" class="no-underline hover:no-underline hover:bg-gray-100/25 hover:dark:bg-gray-700/25"> 8 9 <div class="flex gap-3 items-center p-2 {{ if eq .Name $active }} {{ $activeTab }} {{ else }} {{ $inactiveTab }} {{ end }}"> 9 10 {{ i .Icon "size-4" }}
+3 -2
appview/pages/templates/user/settings/fragments/sidebar.html
··· 1 1 {{ define "user/settings/fragments/sidebar" }} 2 2 {{ $active := .Tab }} 3 + {{ $tabs := .Tabs }} 3 4 <div class="sticky top-2 grid grid-cols-1 rounded border border-gray-200 dark:border-gray-700 divide-y divide-gray-200 dark:divide-gray-700 shadow-inner"> 4 5 {{ $activeTab := "bg-white dark:bg-gray-700 drop-shadow-sm" }} 5 6 {{ $inactiveTab := "bg-gray-100 dark:bg-gray-800" }} 6 - {{ range const.UserSettingsTabs }} 7 + {{ range $tabs }} 7 8 <a href="/settings/{{.Name}}" class="no-underline hover:no-underline hover:bg-gray-100/25 hover:dark:bg-gray-700/25"> 8 9 <div class="flex gap-3 items-center p-2 {{ if eq .Name $active }} {{ $activeTab }} {{ else }} {{ $inactiveTab }} {{ end }}"> 9 10 {{ i .Icon "size-4" }} ··· 12 13 </a> 13 14 {{ end }} 14 15 </div> 15 - {{ end }} 16 + {{ end }}
+14 -4
appview/pulls/pulls.go
··· 232 232 defs[l.AtUri().String()] = &l 233 233 } 234 234 235 - s.pages.RepoSinglePull(w, pages.RepoSinglePullParams{ 235 + patch := pull.LatestSubmission().CombinedPatch() 236 + diff := patchutil.AsNiceDiff(patch, pull.TargetBranch) 237 + var diffOpts types.DiffOpts 238 + if d := r.URL.Query().Get("diff"); d == "split" { 239 + diffOpts.Split = true 240 + } 241 + 242 + log.Println(s.pages.RepoSinglePull(w, pages.RepoSinglePullParams{ 236 243 LoggedInUser: user, 237 244 RepoInfo: s.repoResolver.GetRepoInfo(r, user), 238 245 Pull: pull, ··· 243 250 MergeCheck: mergeCheckResponse, 244 251 ResubmitCheck: resubmitResult, 245 252 Pipelines: m, 253 + Diff: &diff, 254 + DiffOpts: diffOpts, 246 255 247 - Reactions: reactionMap, 248 - UserReacted: userReactions, 256 + OrderedReactionKinds: models.OrderedReactionKinds, 257 + Reactions: reactionMap, 258 + UserReacted: userReactions, 249 259 250 260 LabelDefs: defs, 251 - }) 261 + })) 252 262 } 253 263 254 264 func (s *Pulls) mergeCheck(r *http.Request, f *models.Repo, pull *models.Pull, stack models.Stack) types.MergeCheckResponse {
+14
appview/repo/settings.go
··· 22 22 indigoxrpc "github.com/bluesky-social/indigo/xrpc" 23 23 ) 24 24 25 + type tab = map[string]any 26 + 27 + var ( 28 + // would be great to have ordered maps right about now 29 + settingsTabs []tab = []tab{ 30 + {"Name": "general", "Icon": "sliders-horizontal"}, 31 + {"Name": "access", "Icon": "users"}, 32 + {"Name": "pipelines", "Icon": "layers-2"}, 33 + } 34 + ) 35 + 25 36 func (rp *Repo) SetDefaultBranch(w http.ResponseWriter, r *http.Request) { 26 37 l := rp.logger.With("handler", "SetDefaultBranch") 27 38 ··· 251 262 DefaultLabels: defaultLabels, 252 263 SubscribedLabels: subscribedLabels, 253 264 ShouldSubscribeAll: shouldSubscribeAll, 265 + Tabs: settingsTabs, 254 266 Tab: "general", 255 267 }) 256 268 } ··· 296 308 rp.pages.RepoAccessSettings(w, pages.RepoAccessSettingsParams{ 297 309 LoggedInUser: user, 298 310 RepoInfo: rp.repoResolver.GetRepoInfo(r, user), 311 + Tabs: settingsTabs, 299 312 Tab: "access", 300 313 Collaborators: collaborators, 301 314 }) ··· 356 369 rp.pages.RepoPipelineSettings(w, pages.RepoPipelineSettingsParams{ 357 370 LoggedInUser: user, 358 371 RepoInfo: rp.repoResolver.GetRepoInfo(r, user), 372 + Tabs: settingsTabs, 359 373 Tab: "pipelines", 360 374 Spindles: spindles, 361 375 CurrentSpindle: f.Spindle,
+17
appview/settings/settings.go
··· 35 35 Config *config.Config 36 36 } 37 37 38 + type tab = map[string]any 39 + 40 + var ( 41 + settingsTabs []tab = []tab{ 42 + {"Name": "profile", "Icon": "user"}, 43 + {"Name": "keys", "Icon": "key"}, 44 + {"Name": "emails", "Icon": "mail"}, 45 + {"Name": "notifications", "Icon": "bell"}, 46 + {"Name": "knots", "Icon": "volleyball"}, 47 + {"Name": "spindles", "Icon": "spool"}, 48 + } 49 + ) 50 + 38 51 func (s *Settings) Router() http.Handler { 39 52 r := chi.NewRouter() 40 53 ··· 72 85 73 86 s.Pages.UserProfileSettings(w, pages.UserProfileSettingsParams{ 74 87 LoggedInUser: user, 88 + Tabs: settingsTabs, 75 89 Tab: "profile", 76 90 }) 77 91 } ··· 90 104 s.Pages.UserNotificationSettings(w, pages.UserNotificationSettingsParams{ 91 105 LoggedInUser: user, 92 106 Preferences: prefs, 107 + Tabs: settingsTabs, 93 108 Tab: "notifications", 94 109 }) 95 110 } ··· 131 146 s.Pages.UserKeysSettings(w, pages.UserKeysSettingsParams{ 132 147 LoggedInUser: user, 133 148 PubKeys: pubKeys, 149 + Tabs: settingsTabs, 134 150 Tab: "keys", 135 151 }) 136 152 } ··· 145 161 s.Pages.UserEmailsSettings(w, pages.UserEmailsSettingsParams{ 146 162 LoggedInUser: user, 147 163 Emails: emails, 164 + Tabs: settingsTabs, 148 165 Tab: "emails", 149 166 }) 150 167 }
+15
appview/spindles/spindles.go
··· 39 39 Logger *slog.Logger 40 40 } 41 41 42 + type tab = map[string]any 43 + 44 + var ( 45 + spindlesTabs []tab = []tab{ 46 + {"Name": "profile", "Icon": "user"}, 47 + {"Name": "keys", "Icon": "key"}, 48 + {"Name": "emails", "Icon": "mail"}, 49 + {"Name": "notifications", "Icon": "bell"}, 50 + {"Name": "knots", "Icon": "volleyball"}, 51 + {"Name": "spindles", "Icon": "spool"}, 52 + } 53 + ) 54 + 42 55 func (s *Spindles) Router() http.Handler { 43 56 r := chi.NewRouter() 44 57 ··· 70 83 s.Pages.Spindles(w, pages.SpindlesParams{ 71 84 LoggedInUser: user, 72 85 Spindles: all, 86 + Tabs: spindlesTabs, 73 87 Tab: "spindles", 74 88 }) 75 89 } ··· 129 143 Spindle: spindle, 130 144 Members: members, 131 145 Repos: repoMap, 146 + Tabs: spindlesTabs, 132 147 Tab: "spindles", 133 148 }) 134 149 }
+6 -5
docs/template.html
··· 120 120 $endif$ 121 121 $endif$ 122 122 </header> 123 - $if(abstract)$ 124 - <article class="prose dark:prose-invert max-w-none"> 125 - $abstract$ 126 - </article> 127 - $endif$ 123 + $endif$ 124 + 125 + $if(abstract)$ 126 + <article class="prose dark:prose-invert max-w-none"> 127 + $abstract$ 128 + </article> 128 129 $endif$ 129 130 130 131 <article class="prose dark:prose-invert max-w-none">
+13
input.css
··· 124 124 dark:text-gray-100 dark:before:bg-gray-800 dark:before:border-gray-700; 125 125 } 126 126 127 + .btn-flat { 128 + @apply relative z-10 inline-flex min-h-[30px] cursor-pointer items-center justify-center 129 + bg-transparent px-2 pb-[0.2rem] text-sm text-gray-900 130 + before:absolute before:inset-0 before:-z-10 before:block before:rounded 131 + before:border before:border-gray-200 before:bg-white 132 + before:content-[''] before:transition-all before:duration-150 before:ease-in-out 133 + hover:before:bg-gray-50 134 + dark:hover:before:bg-gray-700 135 + focus:outline-none focus-visible:before:outline focus-visible:before:outline-2 focus-visible:before:outline-gray-400 136 + disabled:cursor-not-allowed disabled:opacity-50 137 + dark:text-gray-100 dark:before:bg-gray-800 dark:before:border-gray-700; 138 + } 139 + 127 140 .btn-create { 128 141 @apply btn text-white 129 142 before:bg-green-600 hover:before:bg-green-700
+2 -2
spindle/models/models.go
··· 53 53 StatusKindRunning, 54 54 } 55 55 FinishStates [4]StatusKind = [4]StatusKind{ 56 - StatusKindCancelled, 57 56 StatusKindFailed, 58 - StatusKindSuccess, 59 57 StatusKindTimeout, 58 + StatusKindCancelled, 59 + StatusKindSuccess, 60 60 } 61 61 ) 62 62
+4 -2
types/diff.go
··· 27 27 } 28 28 29 29 type DiffStat struct { 30 - Insertions int64 31 - Deletions int64 30 + Insertions int64 31 + Deletions int64 32 + FilesChanged int 32 33 } 33 34 34 35 func (d *Diff) Stats() DiffStat { ··· 37 38 stats.Insertions += f.LinesAdded 38 39 stats.Deletions += f.LinesDeleted 39 40 } 41 + stats.FilesChanged = len(d.TextFragments) 40 42 return stats 41 43 } 42 44