Mirror of https://git.jolheiser.com/ugit
2
fork

Configure Feed

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

feat: grep search

Signed-off-by: jolheiser <john.olheiser@gmail.com>

+407 -21
+150
internal/git/grep.go
··· 1 + package git 2 + 3 + import ( 4 + "regexp" 5 + "strings" 6 + 7 + "github.com/go-git/go-git/v5" 8 + "github.com/go-git/go-git/v5/plumbing/object" 9 + ) 10 + 11 + // GrepResult is the result of a search 12 + type GrepResult struct { 13 + File string 14 + StartLine int 15 + Line int 16 + Content string 17 + } 18 + 19 + // Grep performs a naive "code search" via git grep 20 + func (r Repo) Grep(search string) ([]GrepResult, error) { 21 + // Plain-text search only 22 + re, err := regexp.Compile(regexp.QuoteMeta(search)) 23 + if err != nil { 24 + return nil, err 25 + } 26 + 27 + repo, err := r.Git() 28 + if err != nil { 29 + return nil, err 30 + } 31 + 32 + // Loosely modifed from 33 + // https://github.com/go-git/go-git/blob/fb04aa392c8d4c259cb5b21c1cb4c6f8076e600b/options.go#L736-L740 34 + // https://github.com/go-git/go-git/blob/fb04aa392c8d4c259cb5b21c1cb4c6f8076e600b/worktree.go#L753-L760 35 + ref, err := repo.Head() 36 + if err != nil { 37 + return nil, err 38 + } 39 + 40 + commit, err := repo.CommitObject(ref.Hash()) 41 + if err != nil { 42 + return nil, err 43 + } 44 + 45 + tree, err := commit.Tree() 46 + if err != nil { 47 + return nil, err 48 + } 49 + 50 + return findMatchInFiles(tree.Files(), ref.Hash().String(), &git.GrepOptions{ 51 + Patterns: []*regexp.Regexp{re}, 52 + }) 53 + } 54 + 55 + // Lines below are copied and modifed from https://github.com/go-git/go-git/blob/fb04aa392c8d4c259cb5b21c1cb4c6f8076e600b/worktree.go#L961-L1045 56 + 57 + // findMatchInFiles takes a FileIter, worktree name and GrepOptions, and 58 + // returns a slice of GrepResult containing the result of regex pattern matching 59 + // in content of all the files. 60 + func findMatchInFiles(fileiter *object.FileIter, treeName string, opts *git.GrepOptions) ([]GrepResult, error) { 61 + var results []GrepResult 62 + 63 + err := fileiter.ForEach(func(file *object.File) error { 64 + var fileInPathSpec bool 65 + 66 + // When no pathspecs are provided, search all the files. 67 + if len(opts.PathSpecs) == 0 { 68 + fileInPathSpec = true 69 + } 70 + 71 + // Check if the file name matches with the pathspec. Break out of the 72 + // loop once a match is found. 73 + for _, pathSpec := range opts.PathSpecs { 74 + if pathSpec != nil && pathSpec.MatchString(file.Name) { 75 + fileInPathSpec = true 76 + break 77 + } 78 + } 79 + 80 + // If the file does not match with any of the pathspec, skip it. 81 + if !fileInPathSpec { 82 + return nil 83 + } 84 + 85 + grepResults, err := findMatchInFile(file, treeName, opts) 86 + if err != nil { 87 + return err 88 + } 89 + results = append(results, grepResults...) 90 + 91 + return nil 92 + }) 93 + 94 + return results, err 95 + } 96 + 97 + // findMatchInFile takes a single File, worktree name and GrepOptions, 98 + // and returns a slice of GrepResult containing the result of regex pattern 99 + // matching in the given file. 100 + func findMatchInFile(file *object.File, treeName string, opts *git.GrepOptions) ([]GrepResult, error) { 101 + var grepResults []GrepResult 102 + 103 + content, err := file.Contents() 104 + if err != nil { 105 + return grepResults, err 106 + } 107 + 108 + // Split the file content and parse line-by-line. 109 + contentByLine := strings.Split(content, "\n") 110 + for lineNum, cnt := range contentByLine { 111 + addToResult := false 112 + 113 + // Match the patterns and content. Break out of the loop once a 114 + // match is found. 115 + for _, pattern := range opts.Patterns { 116 + if pattern != nil && pattern.MatchString(cnt) { 117 + // Add to result only if invert match is not enabled. 118 + if !opts.InvertMatch { 119 + addToResult = true 120 + break 121 + } 122 + } else if opts.InvertMatch { 123 + // If matching fails, and invert match is enabled, add to 124 + // results. 125 + addToResult = true 126 + break 127 + } 128 + } 129 + 130 + if addToResult { 131 + startLine := lineNum + 1 132 + ctx := []string{cnt} 133 + if lineNum != 0 { 134 + startLine -= 1 135 + ctx = append([]string{contentByLine[lineNum-1]}, ctx...) 136 + } 137 + if lineNum != len(contentByLine)-1 { 138 + ctx = append(ctx, contentByLine[lineNum+1]) 139 + } 140 + grepResults = append(grepResults, GrepResult{ 141 + File: file.Name, 142 + StartLine: startLine, 143 + Line: lineNum + 1, 144 + Content: strings.Join(ctx, "\n"), 145 + }) 146 + } 147 + } 148 + 149 + return grepResults, nil 150 + }
+1 -1
internal/html/generate.css
··· 43 43 background: rgb(var(--ctp-surface0)) !important; 44 44 } 45 45 46 - .commit .chroma { 46 + .code>.chroma { 47 47 border-radius: .25rem; 48 48 padding: 1em; 49 49 }
+18 -3
internal/html/markup/chroma.go
··· 26 26 27 27 type code struct{} 28 28 29 - func (c code) setup(source []byte, fileName string) (chroma.Iterator, *chroma.Style, error) { 29 + func setup(source []byte, fileName string) (chroma.Iterator, *chroma.Style, error) { 30 30 lexer := lexers.Match(fileName) 31 31 if lexer == nil { 32 32 lexer = lexers.Fallback ··· 48 48 49 49 // Basic formats code without any extras 50 50 func (c code) Basic(source []byte, fileName string, writer io.Writer) error { 51 - iter, style, err := c.setup(source, fileName) 51 + iter, style, err := setup(source, fileName) 52 52 if err != nil { 53 53 return err 54 54 } ··· 57 57 58 58 // Convert formats code with line numbers, links, etc. 59 59 func (c code) Convert(source []byte, fileName string, writer io.Writer) error { 60 - iter, style, err := c.setup(source, fileName) 60 + iter, style, err := setup(source, fileName) 61 61 if err != nil { 62 62 return err 63 63 } 64 64 return Formatter.Format(writer, style, iter) 65 65 } 66 + 67 + // Snippet formats code with line numbers starting at a specific line 68 + func Snippet(source []byte, fileName string, line int, writer io.Writer) error { 69 + iter, style, err := setup(source, fileName) 70 + if err != nil { 71 + return err 72 + } 73 + formatter := html.New( 74 + html.WithLineNumbers(true), 75 + html.WithClasses(true), 76 + html.LineNumbersInTable(true), 77 + html.BaseLineNumber(line), 78 + ) 79 + return formatter.Format(writer, style, iter) 80 + }
+2
internal/html/repo.templ
··· 21 21 { " - " } 22 22 <a class="underline decoration-text/50 decoration-dashed hover:decoration-solid" href={ templ.SafeURL(fmt.Sprintf("/%s/log/%s", rhcc.Name, rhcc.Ref)) }>log</a> 23 23 { " - " } 24 + <a class="underline decoration-text/50 decoration-dashed hover:decoration-solid" href={ templ.SafeURL(fmt.Sprintf("/%s/search", rhcc.Name)) }>search</a> 25 + { " - " } 24 26 <pre class="text-text inline select-all bg-base dark:bg-base/50 p-1 rounded">{ fmt.Sprintf("%s/%s.git", rhcc.CloneURL, rhcc.Name) }</pre> 25 27 </div> 26 28 <div class="text-text/80 mb-1">{ rhcc.Description }</div>
+1 -1
internal/html/repo_commit.templ
··· 34 34 <a class="underline decoration-text/50 decoration-dashed hover:decoration-solid" href={ templ.SafeURL(fmt.Sprintf("/%s/tree/%s/%s", rcc.RepoHeaderComponentContext.Name, file.To.Commit, file.To.Path)) }>{ file.To.Path }</a> 35 35 } 36 36 </div> 37 - <div class="whitespace-pre commit">@templ.Raw(file.Patch)</div> 37 + <div class="whitespace-pre code">@templ.Raw(file.Patch)</div> 38 38 } 39 39 } 40 40 }
+1 -1
internal/html/repo_commit_templ.go
··· 324 324 return templ_7745c5c3_Err 325 325 } 326 326 } 327 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div><div class=\"whitespace-pre commit\">") 327 + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div><div class=\"whitespace-pre code\">") 328 328 if templ_7745c5c3_Err != nil { 329 329 return templ_7745c5c3_Err 330 330 }
+5 -1
internal/html/repo_file.templ
··· 10 10 templ RepoFile(rfc RepoFileContext) { 11 11 @base(rfc.BaseContext) { 12 12 @repoHeaderComponent(rfc.RepoHeaderComponentContext) 13 - <div class="mt-2 text-text"><a class="text-text underline decoration-text/50 decoration-dashed hover:decoration-solid" href="?raw">Raw</a><span>{ " - " }{ rfc.Path }</span>@templ.Raw(rfc.Code)</div> 13 + <div class="mt-2 text-text"> 14 + <a class="text-text underline decoration-text/50 decoration-dashed hover:decoration-solid" href="?raw">Raw</a> 15 + <span>{ " - " }{ rfc.Path }</span> 16 + <div class="code">@templ.Raw(rfc.Code)</div> 17 + </div> 14 18 } 15 19 <script> 16 20 const lineRe = /#L(\d+)(?:-L(\d+))?/g
+5 -5
internal/html/repo_file_templ.go
··· 49 49 if templ_7745c5c3_Err != nil { 50 50 return templ_7745c5c3_Err 51 51 } 52 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</a><span>") 52 + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</a> <span>") 53 53 if templ_7745c5c3_Err != nil { 54 54 return templ_7745c5c3_Err 55 55 } 56 56 var templ_7745c5c3_Var4 string 57 57 templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(" - ") 58 58 if templ_7745c5c3_Err != nil { 59 - return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo_file.templ`, Line: 12, Col: 153} 59 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo_file.templ`, Line: 14, Col: 16} 60 60 } 61 61 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4)) 62 62 if templ_7745c5c3_Err != nil { ··· 65 65 var templ_7745c5c3_Var5 string 66 66 templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(rfc.Path) 67 67 if templ_7745c5c3_Err != nil { 68 - return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo_file.templ`, Line: 12, Col: 165} 68 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo_file.templ`, Line: 14, Col: 28} 69 69 } 70 70 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5)) 71 71 if templ_7745c5c3_Err != nil { 72 72 return templ_7745c5c3_Err 73 73 } 74 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</span>") 74 + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</span><div class=\"code\">") 75 75 if templ_7745c5c3_Err != nil { 76 76 return templ_7745c5c3_Err 77 77 } ··· 79 79 if templ_7745c5c3_Err != nil { 80 80 return templ_7745c5c3_Err 81 81 } 82 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div>") 82 + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div></div>") 83 83 if templ_7745c5c3_Err != nil { 84 84 return templ_7745c5c3_Err 85 85 }
+26
internal/html/repo_search.templ
··· 1 + package html 2 + 3 + import "fmt" 4 + import "go.jolheiser.com/ugit/internal/git" 5 + 6 + type SearchContext struct { 7 + BaseContext 8 + RepoHeaderComponentContext 9 + Results []git.GrepResult 10 + } 11 + 12 + templ RepoSearch(sc SearchContext) { 13 + @base(sc.BaseContext) { 14 + @repoHeaderComponent(sc.RepoHeaderComponentContext) 15 + <form method="get"><label class="text-text">Search <input class="rounded p-1 mt-2 bg-mantle focus:border-lavender" id="search" type="text" name="q" placeholder="search"/></label></form> 16 + for _, result := range sc.Results { 17 + <div class="text-text mt-5"><a class="underline decoration-text/50 decoration-dashed hover:decoration-solid" href={ templ.SafeURL(fmt.Sprintf("/%s/tree/%s/%s#L%d", sc.RepoHeaderComponentContext.Name, sc.RepoHeaderComponentContext.Ref, result.File, result.Line)) }>{ result.File }</a></div> 18 + <div class="code">@templ.Raw(result.Content)</div> 19 + } 20 + } 21 + <script> 22 + const search = new URLSearchParams(window.location.search).get("q"); 23 + if (search !== "") document.querySelector("#search").value = search; 24 + </script> 25 + } 26 +
+124
internal/html/repo_search_templ.go
··· 1 + // Code generated by templ - DO NOT EDIT. 2 + 3 + // templ: version: v0.2.501 4 + package html 5 + 6 + //lint:file-ignore SA4006 This context is only used if a nested component is present. 7 + 8 + import "github.com/a-h/templ" 9 + import "context" 10 + import "io" 11 + import "bytes" 12 + 13 + import "fmt" 14 + import "go.jolheiser.com/ugit/internal/git" 15 + 16 + type SearchContext struct { 17 + BaseContext 18 + RepoHeaderComponentContext 19 + Results []git.GrepResult 20 + } 21 + 22 + func RepoSearch(sc SearchContext) templ.Component { 23 + return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) { 24 + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer) 25 + if !templ_7745c5c3_IsBuffer { 26 + templ_7745c5c3_Buffer = templ.GetBuffer() 27 + defer templ.ReleaseBuffer(templ_7745c5c3_Buffer) 28 + } 29 + ctx = templ.InitializeContext(ctx) 30 + templ_7745c5c3_Var1 := templ.GetChildren(ctx) 31 + if templ_7745c5c3_Var1 == nil { 32 + templ_7745c5c3_Var1 = templ.NopComponent 33 + } 34 + ctx = templ.ClearChildren(ctx) 35 + templ_7745c5c3_Var2 := templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) { 36 + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer) 37 + if !templ_7745c5c3_IsBuffer { 38 + templ_7745c5c3_Buffer = templ.GetBuffer() 39 + defer templ.ReleaseBuffer(templ_7745c5c3_Buffer) 40 + } 41 + templ_7745c5c3_Err = repoHeaderComponent(sc.RepoHeaderComponentContext).Render(ctx, templ_7745c5c3_Buffer) 42 + if templ_7745c5c3_Err != nil { 43 + return templ_7745c5c3_Err 44 + } 45 + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" <form method=\"get\"><label class=\"text-text\">") 46 + if templ_7745c5c3_Err != nil { 47 + return templ_7745c5c3_Err 48 + } 49 + templ_7745c5c3_Var3 := `Search ` 50 + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var3) 51 + if templ_7745c5c3_Err != nil { 52 + return templ_7745c5c3_Err 53 + } 54 + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<input class=\"rounded p-1 mt-2 bg-mantle focus:border-lavender\" id=\"search\" type=\"text\" name=\"q\" placeholder=\"search\"></label></form>") 55 + if templ_7745c5c3_Err != nil { 56 + return templ_7745c5c3_Err 57 + } 58 + for _, result := range sc.Results { 59 + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"text-text mt-5\"><a class=\"underline decoration-text/50 decoration-dashed hover:decoration-solid\" href=\"") 60 + if templ_7745c5c3_Err != nil { 61 + return templ_7745c5c3_Err 62 + } 63 + var templ_7745c5c3_Var4 templ.SafeURL = templ.SafeURL(fmt.Sprintf("/%s/tree/%s/%s#L%d", sc.RepoHeaderComponentContext.Name, sc.RepoHeaderComponentContext.Ref, result.File, result.Line)) 64 + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var4))) 65 + if templ_7745c5c3_Err != nil { 66 + return templ_7745c5c3_Err 67 + } 68 + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">") 69 + if templ_7745c5c3_Err != nil { 70 + return templ_7745c5c3_Err 71 + } 72 + var templ_7745c5c3_Var5 string 73 + templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(result.File) 74 + if templ_7745c5c3_Err != nil { 75 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo_search.templ`, Line: 16, Col: 280} 76 + } 77 + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5)) 78 + if templ_7745c5c3_Err != nil { 79 + return templ_7745c5c3_Err 80 + } 81 + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</a></div><div class=\"code\">") 82 + if templ_7745c5c3_Err != nil { 83 + return templ_7745c5c3_Err 84 + } 85 + templ_7745c5c3_Err = templ.Raw(result.Content).Render(ctx, templ_7745c5c3_Buffer) 86 + if templ_7745c5c3_Err != nil { 87 + return templ_7745c5c3_Err 88 + } 89 + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div>") 90 + if templ_7745c5c3_Err != nil { 91 + return templ_7745c5c3_Err 92 + } 93 + } 94 + if !templ_7745c5c3_IsBuffer { 95 + _, templ_7745c5c3_Err = io.Copy(templ_7745c5c3_W, templ_7745c5c3_Buffer) 96 + } 97 + return templ_7745c5c3_Err 98 + }) 99 + templ_7745c5c3_Err = base(sc.BaseContext).Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer) 100 + if templ_7745c5c3_Err != nil { 101 + return templ_7745c5c3_Err 102 + } 103 + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<script>") 104 + if templ_7745c5c3_Err != nil { 105 + return templ_7745c5c3_Err 106 + } 107 + templ_7745c5c3_Var6 := ` 108 + const search = new URLSearchParams(window.location.search).get("q"); 109 + if (search !== "") document.querySelector("#search").value = search; 110 + ` 111 + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var6) 112 + if templ_7745c5c3_Err != nil { 113 + return templ_7745c5c3_Err 114 + } 115 + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</script>") 116 + if templ_7745c5c3_Err != nil { 117 + return templ_7745c5c3_Err 118 + } 119 + if !templ_7745c5c3_IsBuffer { 120 + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W) 121 + } 122 + return templ_7745c5c3_Err 123 + }) 124 + }
+39 -8
internal/html/repo_templ.go
··· 166 166 if templ_7745c5c3_Err != nil { 167 167 return templ_7745c5c3_Err 168 168 } 169 + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" <a class=\"underline decoration-text/50 decoration-dashed hover:decoration-solid\" href=\"") 170 + if templ_7745c5c3_Err != nil { 171 + return templ_7745c5c3_Err 172 + } 173 + var templ_7745c5c3_Var14 templ.SafeURL = templ.SafeURL(fmt.Sprintf("/%s/search", rhcc.Name)) 174 + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var14))) 175 + if templ_7745c5c3_Err != nil { 176 + return templ_7745c5c3_Err 177 + } 178 + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">") 179 + if templ_7745c5c3_Err != nil { 180 + return templ_7745c5c3_Err 181 + } 182 + templ_7745c5c3_Var15 := `search` 183 + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var15) 184 + if templ_7745c5c3_Err != nil { 185 + return templ_7745c5c3_Err 186 + } 187 + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</a> ") 188 + if templ_7745c5c3_Err != nil { 189 + return templ_7745c5c3_Err 190 + } 191 + var templ_7745c5c3_Var16 string 192 + templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(" - ") 193 + if templ_7745c5c3_Err != nil { 194 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo.templ`, Line: 24, Col: 9} 195 + } 196 + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16)) 197 + if templ_7745c5c3_Err != nil { 198 + return templ_7745c5c3_Err 199 + } 169 200 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<pre class=\"text-text inline select-all bg-base dark:bg-base/50 p-1 rounded\">") 170 201 if templ_7745c5c3_Err != nil { 171 202 return templ_7745c5c3_Err 172 203 } 173 - var templ_7745c5c3_Var14 string 174 - templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%s/%s.git", rhcc.CloneURL, rhcc.Name)) 204 + var templ_7745c5c3_Var17 string 205 + templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%s/%s.git", rhcc.CloneURL, rhcc.Name)) 175 206 if templ_7745c5c3_Err != nil { 176 - return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo.templ`, Line: 23, Col: 131} 207 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo.templ`, Line: 25, Col: 131} 177 208 } 178 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14)) 209 + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17)) 179 210 if templ_7745c5c3_Err != nil { 180 211 return templ_7745c5c3_Err 181 212 } ··· 183 214 if templ_7745c5c3_Err != nil { 184 215 return templ_7745c5c3_Err 185 216 } 186 - var templ_7745c5c3_Var15 string 187 - templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(rhcc.Description) 217 + var templ_7745c5c3_Var18 string 218 + templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(rhcc.Description) 188 219 if templ_7745c5c3_Err != nil { 189 - return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo.templ`, Line: 25, Col: 50} 220 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo.templ`, Line: 27, Col: 50} 190 221 } 191 - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15)) 222 + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var18)) 192 223 if templ_7745c5c3_Err != nil { 193 224 return templ_7745c5c3_Err 194 225 }
+1 -1
internal/html/tailwind.go
··· 5 5 6 6 func TailwindHandler(w http.ResponseWriter, r *http.Request) { 7 7 w.Header().Set("Content-Type", "text/css") 8 - w.Write([]byte("/*! tailwindcss v3.3.3 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border:0 solid #e5e7eb}:after,:before{--tw-content:\"\"}html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:initial}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button;background-color:initial;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:initial}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]{display:none}.latte{--ctp-rosewater:220,138,120;--ctp-flamingo:221,120,120;--ctp-pink:234,118,203;--ctp-mauve:136,57,239;--ctp-red:210,15,57;--ctp-maroon:230,69,83;--ctp-peach:254,100,11;--ctp-yellow:223,142,29;--ctp-green:64,160,43;--ctp-teal:23,146,153;--ctp-sky:4,165,229;--ctp-sapphire:32,159,181;--ctp-blue:30,102,245;--ctp-lavender:114,135,253;--ctp-text:76,79,105;--ctp-subtext1:92,95,119;--ctp-subtext0:108,111,133;--ctp-overlay2:124,127,147;--ctp-overlay1:140,143,161;--ctp-overlay0:156,160,176;--ctp-surface2:172,176,190;--ctp-surface1:188,192,204;--ctp-surface0:204,208,218;--ctp-base:239,241,245;--ctp-mantle:230,233,239;--ctp-crust:220,224,232}*,::backdrop,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#3b82f680;--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }.col-span-1{grid-column:span 1/span 1}.col-span-2{grid-column:span 2/span 2}.col-span-4{grid-column:span 4/span 4}.col-span-5{grid-column:span 5/span 5}.col-span-6{grid-column:span 6/span 6}.col-span-7{grid-column:span 7/span 7}.col-span-8{grid-column:span 8/span 8}.mx-auto{margin-left:auto;margin-right:auto}.my-10{margin-top:2.5rem;margin-bottom:2.5rem}.mb-1{margin-bottom:.25rem}.mb-3{margin-bottom:.75rem}.mr-1{margin-right:.25rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-5{margin-top:1.25rem}.inline-block{display:inline-block}.inline{display:inline}.grid{display:grid}.h-5{height:1.25rem}.w-5{width:1.25rem}.max-w-7xl{max-width:80rem}.cursor-pointer{cursor:pointer}.select-all{-webkit-user-select:all;-moz-user-select:all;user-select:all}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-8{grid-template-columns:repeat(8,minmax(0,1fr))}.gap-1{gap:.25rem}.gap-5{gap:1.25rem}.whitespace-pre{white-space:pre}.rounded{border-radius:.25rem}.bg-base{--tw-bg-opacity:1;background-color:rgba(var(--ctp-base),var(--tw-bg-opacity))}.bg-base\\/50{background-color:rgba(var(--ctp-base),.5)}.stroke-mauve{stroke:rgb(var(--ctp-mauve))}.p-1{padding:.25rem}.p-5{padding:1.25rem}.px-5{padding-left:1.25rem;padding-right:1.25rem}.py-5{padding-top:1.25rem;padding-bottom:1.25rem}.align-middle{vertical-align:middle}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.font-bold{font-weight:700}.text-blue{--tw-text-opacity:1;color:rgba(var(--ctp-blue),var(--tw-text-opacity))}.text-mauve{--tw-text-opacity:1;color:rgba(var(--ctp-mauve),var(--tw-text-opacity))}.text-subtext0{--tw-text-opacity:1;color:rgba(var(--ctp-subtext0),var(--tw-text-opacity))}.text-subtext1{--tw-text-opacity:1;color:rgba(var(--ctp-subtext1),var(--tw-text-opacity))}.text-text{--tw-text-opacity:1;color:rgba(var(--ctp-text),var(--tw-text-opacity))}.text-text\\/80{color:rgba(var(--ctp-text),.8)}.underline{text-decoration-line:underline}.decoration-blue\\/50{text-decoration-color:rgba(var(--ctp-blue),.5)}.decoration-mauve\\/50{text-decoration-color:rgba(var(--ctp-mauve),.5)}.decoration-text\\/50{text-decoration-color:rgba(var(--ctp-text),.5)}.decoration-dashed{text-decoration-style:dashed}.markdown *{all:revert-layer;color:rgb(var(--ctp-text))}.markdown a{color:rgb(var(--ctp-blue));text-decoration-line:underline;text-decoration-style:dashed}.markdown a:hover{text-decoration-style:solid}.chroma{font-size:small}.chroma *{background-color:rgb(var(--ctp-base))!important}.chroma table{border-spacing:5px 0!important}.chroma .lnt{color:rgb(var(--ctp-subtext1))!important}.chroma .lnt:focus,.chroma .lnt:target{color:rgb(var(--ctp-subtext0))!important}.chroma .line{white-space:break-spaces}.chroma .line.active,.chroma .line.active *{background:rgb(var(--ctp-surface0))!important}.commit .chroma{border-radius:.25rem;padding:1em}.bg,.chroma{color:#4c4f69;background-color:#eff1f5}.chroma .lntd:last-child{width:100%}.chroma .ln:target,.chroma .lnt:target{color:#bcc0cc;background-color:#eff1f5}.chroma .err{color:#d20f39}.chroma .lnlinks{outline:none;text-decoration:none;color:inherit}.chroma .lntd{vertical-align:top;padding:0;margin:0;border:0}.chroma .lntable{border-spacing:0;padding:0;margin:0;border:0}.chroma .hl{color:#bcc0cc}.chroma .ln,.chroma .lnt{white-space:pre;-webkit-user-select:none;-moz-user-select:none;user-select:none;margin-right:.4em;padding:0 .4em;color:#8c8fa1}.chroma .line{display:flex}.chroma .k{color:#8839ef}.chroma .kc{color:#fe640b}.chroma .kd{color:#d20f39}.chroma .kn{color:#179299}.chroma .kp,.chroma .kr{color:#8839ef}.chroma .kt{color:#d20f39}.chroma .na{color:#1e66f5}.chroma .bp,.chroma .nb{color:#04a5e5}.chroma .nc,.chroma .no{color:#df8e1d}.chroma .nd{color:#1e66f5;font-weight:700}.chroma .ni{color:#179299}.chroma .ne{color:#fe640b}.chroma .fm,.chroma .nf{color:#1e66f5}.chroma .nl{color:#04a5e5}.chroma .nn,.chroma .py{color:#fe640b}.chroma .nt{color:#8839ef}.chroma .nv,.chroma .vc,.chroma .vg,.chroma .vi,.chroma .vm{color:#dc8a78}.chroma .s{color:#40a02b}.chroma .sa{color:#d20f39}.chroma .sb,.chroma .sc{color:#40a02b}.chroma .dl{color:#1e66f5}.chroma .sd{color:#9ca0b0}.chroma .s2{color:#40a02b}.chroma .se{color:#1e66f5}.chroma .sh{color:#9ca0b0}.chroma .si,.chroma .sx{color:#40a02b}.chroma .sr{color:#179299}.chroma .s1,.chroma .ss{color:#40a02b}.chroma .il,.chroma .m,.chroma .mb,.chroma .mf,.chroma .mh,.chroma .mi,.chroma .mo{color:#fe640b}.chroma .o,.chroma .ow{color:#04a5e5;font-weight:700}.chroma .c,.chroma .c1,.chroma .ch,.chroma .cm,.chroma .cp,.chroma .cpf,.chroma .cs{color:#9ca0b0;font-style:italic}.chroma .cpf{font-weight:700}.chroma .gd{color:#d20f39;background-color:#ccd0da}.chroma .ge{font-style:italic}.chroma .gr{color:#d20f39}.chroma .gh{color:#fe640b;font-weight:700}.chroma .gi{color:#40a02b;background-color:#ccd0da}.chroma .gs,.chroma .gu{font-weight:700}.chroma .gu{color:#fe640b}.chroma .gt{color:#d20f39}.chroma .gl{text-decoration:underline}@media (prefers-color-scheme:dark){.bg,.chroma{color:#cdd6f4;background-color:#1e1e2e}.chroma .lntd:last-child{width:100%}.chroma .ln:target,.chroma .lnt:target{color:#45475a;background-color:#1e1e2e}.chroma .err{color:#f38ba8}.chroma .lnlinks{outline:none;text-decoration:none;color:inherit}.chroma .lntd{vertical-align:top;padding:0;margin:0;border:0}.chroma .lntable{border-spacing:0;padding:0;margin:0;border:0}.chroma .hl{color:#45475a}.chroma .ln,.chroma .lnt{white-space:pre;-webkit-user-select:none;-moz-user-select:none;user-select:none;margin-right:.4em;padding:0 .4em;color:#7f849c}.chroma .line{display:flex}.chroma .k{color:#cba6f7}.chroma .kc{color:#fab387}.chroma .kd{color:#f38ba8}.chroma .kn{color:#94e2d5}.chroma .kp,.chroma .kr{color:#cba6f7}.chroma .kt{color:#f38ba8}.chroma .na{color:#89b4fa}.chroma .bp,.chroma .nb{color:#89dceb}.chroma .nc,.chroma .no{color:#f9e2af}.chroma .nd{color:#89b4fa;font-weight:700}.chroma .ni{color:#94e2d5}.chroma .ne{color:#fab387}.chroma .fm,.chroma .nf{color:#89b4fa}.chroma .nl{color:#89dceb}.chroma .nn,.chroma .py{color:#fab387}.chroma .nt{color:#cba6f7}.chroma .nv,.chroma .vc,.chroma .vg,.chroma .vi,.chroma .vm{color:#f5e0dc}.chroma .s{color:#a6e3a1}.chroma .sa{color:#f38ba8}.chroma .sb,.chroma .sc{color:#a6e3a1}.chroma .dl{color:#89b4fa}.chroma .sd{color:#6c7086}.chroma .s2{color:#a6e3a1}.chroma .se{color:#89b4fa}.chroma .sh{color:#6c7086}.chroma .si,.chroma .sx{color:#a6e3a1}.chroma .sr{color:#94e2d5}.chroma .s1,.chroma .ss{color:#a6e3a1}.chroma .il,.chroma .m,.chroma .mb,.chroma .mf,.chroma .mh,.chroma .mi,.chroma .mo{color:#fab387}.chroma .o,.chroma .ow{color:#89dceb;font-weight:700}.chroma .c,.chroma .c1,.chroma .ch,.chroma .cm,.chroma .cp,.chroma .cpf,.chroma .cs{color:#6c7086;font-style:italic}.chroma .cpf{font-weight:700}.chroma .gd{color:#f38ba8;background-color:#313244}.chroma .ge{font-style:italic}.chroma .gr{color:#f38ba8}.chroma .gh{color:#fab387;font-weight:700}.chroma .gi{color:#a6e3a1;background-color:#313244}.chroma .gs,.chroma .gu{font-weight:700}.chroma .gu{color:#fab387}.chroma .gt{color:#f38ba8}.chroma .gl{text-decoration:underline}.dark\\:mocha{--ctp-rosewater:245,224,220;--ctp-flamingo:242,205,205;--ctp-pink:245,194,231;--ctp-mauve:203,166,247;--ctp-red:243,139,168;--ctp-maroon:235,160,172;--ctp-peach:250,179,135;--ctp-yellow:249,226,175;--ctp-green:166,227,161;--ctp-teal:148,226,213;--ctp-sky:137,220,235;--ctp-sapphire:116,199,236;--ctp-blue:137,180,250;--ctp-lavender:180,190,254;--ctp-text:205,214,244;--ctp-subtext1:186,194,222;--ctp-subtext0:166,173,200;--ctp-overlay2:147,153,178;--ctp-overlay1:127,132,156;--ctp-overlay0:108,112,134;--ctp-surface2:88,91,112;--ctp-surface1:69,71,90;--ctp-surface0:49,50,68;--ctp-base:30,30,46;--ctp-mantle:24,24,37;--ctp-crust:17,17,27}}.hover\\:decoration-solid:hover{text-decoration-style:solid}@media (prefers-color-scheme:dark){.dark\\:bg-base\\/50{background-color:rgba(var(--ctp-base),.5)}.dark\\:bg-base\\/95{background-color:rgba(var(--ctp-base),.95)}.dark\\:text-lavender{--tw-text-opacity:1;color:rgba(var(--ctp-lavender),var(--tw-text-opacity))}.dark\\:decoration-lavender\\/50{text-decoration-color:rgba(var(--ctp-lavender),.5)}}@media (min-width:640px){.sm\\:grid-cols-8{grid-template-columns:repeat(8,minmax(0,1fr))}}")) 8 + w.Write([]byte("/*! tailwindcss v3.3.3 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border:0 solid #e5e7eb}:after,:before{--tw-content:\"\"}html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:initial}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button;background-color:initial;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:initial}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]{display:none}.latte{--ctp-rosewater:220,138,120;--ctp-flamingo:221,120,120;--ctp-pink:234,118,203;--ctp-mauve:136,57,239;--ctp-red:210,15,57;--ctp-maroon:230,69,83;--ctp-peach:254,100,11;--ctp-yellow:223,142,29;--ctp-green:64,160,43;--ctp-teal:23,146,153;--ctp-sky:4,165,229;--ctp-sapphire:32,159,181;--ctp-blue:30,102,245;--ctp-lavender:114,135,253;--ctp-text:76,79,105;--ctp-subtext1:92,95,119;--ctp-subtext0:108,111,133;--ctp-overlay2:124,127,147;--ctp-overlay1:140,143,161;--ctp-overlay0:156,160,176;--ctp-surface2:172,176,190;--ctp-surface1:188,192,204;--ctp-surface0:204,208,218;--ctp-base:239,241,245;--ctp-mantle:230,233,239;--ctp-crust:220,224,232}*,::backdrop,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#3b82f680;--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }.col-span-1{grid-column:span 1/span 1}.col-span-2{grid-column:span 2/span 2}.col-span-4{grid-column:span 4/span 4}.col-span-5{grid-column:span 5/span 5}.col-span-6{grid-column:span 6/span 6}.col-span-7{grid-column:span 7/span 7}.col-span-8{grid-column:span 8/span 8}.mx-auto{margin-left:auto;margin-right:auto}.my-10{margin-top:2.5rem;margin-bottom:2.5rem}.mb-1{margin-bottom:.25rem}.mb-3{margin-bottom:.75rem}.mr-1{margin-right:.25rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-5{margin-top:1.25rem}.inline-block{display:inline-block}.inline{display:inline}.grid{display:grid}.h-5{height:1.25rem}.w-5{width:1.25rem}.max-w-7xl{max-width:80rem}.cursor-pointer{cursor:pointer}.select-all{-webkit-user-select:all;-moz-user-select:all;user-select:all}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-8{grid-template-columns:repeat(8,minmax(0,1fr))}.gap-1{gap:.25rem}.gap-5{gap:1.25rem}.whitespace-pre{white-space:pre}.rounded{border-radius:.25rem}.bg-base{--tw-bg-opacity:1;background-color:rgba(var(--ctp-base),var(--tw-bg-opacity))}.bg-base\\/50{background-color:rgba(var(--ctp-base),.5)}.bg-mantle{--tw-bg-opacity:1;background-color:rgba(var(--ctp-mantle),var(--tw-bg-opacity))}.stroke-mauve{stroke:rgb(var(--ctp-mauve))}.p-1{padding:.25rem}.p-5{padding:1.25rem}.px-5{padding-left:1.25rem;padding-right:1.25rem}.py-5{padding-top:1.25rem;padding-bottom:1.25rem}.align-middle{vertical-align:middle}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.font-bold{font-weight:700}.text-blue{--tw-text-opacity:1;color:rgba(var(--ctp-blue),var(--tw-text-opacity))}.text-mauve{--tw-text-opacity:1;color:rgba(var(--ctp-mauve),var(--tw-text-opacity))}.text-subtext0{--tw-text-opacity:1;color:rgba(var(--ctp-subtext0),var(--tw-text-opacity))}.text-subtext1{--tw-text-opacity:1;color:rgba(var(--ctp-subtext1),var(--tw-text-opacity))}.text-text{--tw-text-opacity:1;color:rgba(var(--ctp-text),var(--tw-text-opacity))}.text-text\\/80{color:rgba(var(--ctp-text),.8)}.underline{text-decoration-line:underline}.decoration-blue\\/50{text-decoration-color:rgba(var(--ctp-blue),.5)}.decoration-mauve\\/50{text-decoration-color:rgba(var(--ctp-mauve),.5)}.decoration-text\\/50{text-decoration-color:rgba(var(--ctp-text),.5)}.decoration-dashed{text-decoration-style:dashed}.markdown *{all:revert-layer;color:rgb(var(--ctp-text))}.markdown a{color:rgb(var(--ctp-blue));text-decoration-line:underline;text-decoration-style:dashed}.markdown a:hover{text-decoration-style:solid}.chroma{font-size:small}.chroma *{background-color:rgb(var(--ctp-base))!important}.chroma table{border-spacing:5px 0!important}.chroma .lnt{color:rgb(var(--ctp-subtext1))!important}.chroma .lnt:focus,.chroma .lnt:target{color:rgb(var(--ctp-subtext0))!important}.chroma .line{white-space:break-spaces}.chroma .line.active,.chroma .line.active *{background:rgb(var(--ctp-surface0))!important}.code>.chroma{border-radius:.25rem;padding:1em}.bg,.chroma{color:#4c4f69;background-color:#eff1f5}.chroma .lntd:last-child{width:100%}.chroma .ln:target,.chroma .lnt:target{color:#bcc0cc;background-color:#eff1f5}.chroma .err{color:#d20f39}.chroma .lnlinks{outline:none;text-decoration:none;color:inherit}.chroma .lntd{vertical-align:top;padding:0;margin:0;border:0}.chroma .lntable{border-spacing:0;padding:0;margin:0;border:0}.chroma .hl{color:#bcc0cc}.chroma .ln,.chroma .lnt{white-space:pre;-webkit-user-select:none;-moz-user-select:none;user-select:none;margin-right:.4em;padding:0 .4em;color:#8c8fa1}.chroma .line{display:flex}.chroma .k{color:#8839ef}.chroma .kc{color:#fe640b}.chroma .kd{color:#d20f39}.chroma .kn{color:#179299}.chroma .kp,.chroma .kr{color:#8839ef}.chroma .kt{color:#d20f39}.chroma .na{color:#1e66f5}.chroma .bp,.chroma .nb{color:#04a5e5}.chroma .nc,.chroma .no{color:#df8e1d}.chroma .nd{color:#1e66f5;font-weight:700}.chroma .ni{color:#179299}.chroma .ne{color:#fe640b}.chroma .fm,.chroma .nf{color:#1e66f5}.chroma .nl{color:#04a5e5}.chroma .nn,.chroma .py{color:#fe640b}.chroma .nt{color:#8839ef}.chroma .nv,.chroma .vc,.chroma .vg,.chroma .vi,.chroma .vm{color:#dc8a78}.chroma .s{color:#40a02b}.chroma .sa{color:#d20f39}.chroma .sb,.chroma .sc{color:#40a02b}.chroma .dl{color:#1e66f5}.chroma .sd{color:#9ca0b0}.chroma .s2{color:#40a02b}.chroma .se{color:#1e66f5}.chroma .sh{color:#9ca0b0}.chroma .si,.chroma .sx{color:#40a02b}.chroma .sr{color:#179299}.chroma .s1,.chroma .ss{color:#40a02b}.chroma .il,.chroma .m,.chroma .mb,.chroma .mf,.chroma .mh,.chroma .mi,.chroma .mo{color:#fe640b}.chroma .o,.chroma .ow{color:#04a5e5;font-weight:700}.chroma .c,.chroma .c1,.chroma .ch,.chroma .cm,.chroma .cp,.chroma .cpf,.chroma .cs{color:#9ca0b0;font-style:italic}.chroma .cpf{font-weight:700}.chroma .gd{color:#d20f39;background-color:#ccd0da}.chroma .ge{font-style:italic}.chroma .gr{color:#d20f39}.chroma .gh{color:#fe640b;font-weight:700}.chroma .gi{color:#40a02b;background-color:#ccd0da}.chroma .gs,.chroma .gu{font-weight:700}.chroma .gu{color:#fe640b}.chroma .gt{color:#d20f39}.chroma .gl{text-decoration:underline}@media (prefers-color-scheme:dark){.bg,.chroma{color:#cdd6f4;background-color:#1e1e2e}.chroma .lntd:last-child{width:100%}.chroma .ln:target,.chroma .lnt:target{color:#45475a;background-color:#1e1e2e}.chroma .err{color:#f38ba8}.chroma .lnlinks{outline:none;text-decoration:none;color:inherit}.chroma .lntd{vertical-align:top;padding:0;margin:0;border:0}.chroma .lntable{border-spacing:0;padding:0;margin:0;border:0}.chroma .hl{color:#45475a}.chroma .ln,.chroma .lnt{white-space:pre;-webkit-user-select:none;-moz-user-select:none;user-select:none;margin-right:.4em;padding:0 .4em;color:#7f849c}.chroma .line{display:flex}.chroma .k{color:#cba6f7}.chroma .kc{color:#fab387}.chroma .kd{color:#f38ba8}.chroma .kn{color:#94e2d5}.chroma .kp,.chroma .kr{color:#cba6f7}.chroma .kt{color:#f38ba8}.chroma .na{color:#89b4fa}.chroma .bp,.chroma .nb{color:#89dceb}.chroma .nc,.chroma .no{color:#f9e2af}.chroma .nd{color:#89b4fa;font-weight:700}.chroma .ni{color:#94e2d5}.chroma .ne{color:#fab387}.chroma .fm,.chroma .nf{color:#89b4fa}.chroma .nl{color:#89dceb}.chroma .nn,.chroma .py{color:#fab387}.chroma .nt{color:#cba6f7}.chroma .nv,.chroma .vc,.chroma .vg,.chroma .vi,.chroma .vm{color:#f5e0dc}.chroma .s{color:#a6e3a1}.chroma .sa{color:#f38ba8}.chroma .sb,.chroma .sc{color:#a6e3a1}.chroma .dl{color:#89b4fa}.chroma .sd{color:#6c7086}.chroma .s2{color:#a6e3a1}.chroma .se{color:#89b4fa}.chroma .sh{color:#6c7086}.chroma .si,.chroma .sx{color:#a6e3a1}.chroma .sr{color:#94e2d5}.chroma .s1,.chroma .ss{color:#a6e3a1}.chroma .il,.chroma .m,.chroma .mb,.chroma .mf,.chroma .mh,.chroma .mi,.chroma .mo{color:#fab387}.chroma .o,.chroma .ow{color:#89dceb;font-weight:700}.chroma .c,.chroma .c1,.chroma .ch,.chroma .cm,.chroma .cp,.chroma .cpf,.chroma .cs{color:#6c7086;font-style:italic}.chroma .cpf{font-weight:700}.chroma .gd{color:#f38ba8;background-color:#313244}.chroma .ge{font-style:italic}.chroma .gr{color:#f38ba8}.chroma .gh{color:#fab387;font-weight:700}.chroma .gi{color:#a6e3a1;background-color:#313244}.chroma .gs,.chroma .gu{font-weight:700}.chroma .gu{color:#fab387}.chroma .gt{color:#f38ba8}.chroma .gl{text-decoration:underline}.dark\\:mocha{--ctp-rosewater:245,224,220;--ctp-flamingo:242,205,205;--ctp-pink:245,194,231;--ctp-mauve:203,166,247;--ctp-red:243,139,168;--ctp-maroon:235,160,172;--ctp-peach:250,179,135;--ctp-yellow:249,226,175;--ctp-green:166,227,161;--ctp-teal:148,226,213;--ctp-sky:137,220,235;--ctp-sapphire:116,199,236;--ctp-blue:137,180,250;--ctp-lavender:180,190,254;--ctp-text:205,214,244;--ctp-subtext1:186,194,222;--ctp-subtext0:166,173,200;--ctp-overlay2:147,153,178;--ctp-overlay1:127,132,156;--ctp-overlay0:108,112,134;--ctp-surface2:88,91,112;--ctp-surface1:69,71,90;--ctp-surface0:49,50,68;--ctp-base:30,30,46;--ctp-mantle:24,24,37;--ctp-crust:17,17,27}}.hover\\:decoration-solid:hover{text-decoration-style:solid}.focus\\:border-lavender:focus{--tw-border-opacity:1;border-color:rgba(var(--ctp-lavender),var(--tw-border-opacity))}@media (prefers-color-scheme:dark){.dark\\:bg-base\\/50{background-color:rgba(var(--ctp-base),.5)}.dark\\:bg-base\\/95{background-color:rgba(var(--ctp-base),.95)}.dark\\:text-lavender{--tw-text-opacity:1;color:rgba(var(--ctp-lavender),var(--tw-text-opacity))}.dark\\:decoration-lavender\\/50{text-decoration-color:rgba(var(--ctp-lavender),.5)}}@media (min-width:640px){.sm\\:grid-cols-8{grid-template-columns:repeat(8,minmax(0,1fr))}}")) 9 9 }
+1
internal/http/http.go
··· 85 85 r.Get("/log/{ref}", httperr.Handler(rh.repoLog)) 86 86 r.Get("/commit/{commit}", httperr.Handler(rh.repoCommit)) 87 87 r.Get("/commit/{commit}.patch", httperr.Handler(rh.repoPatch)) 88 + r.Get("/search", httperr.Handler(rh.repoSearch)) 88 89 89 90 // Protocol 90 91 r.Get("/info/refs", httperr.Handler(rh.infoRefs))
+33
internal/http/repo.go
··· 6 6 "mime" 7 7 "net/http" 8 8 "path/filepath" 9 + "strings" 9 10 10 11 "go.jolheiser.com/ugit/internal/html/markup" 11 12 ··· 182 183 183 184 return nil 184 185 } 186 + 187 + func (rh repoHandler) repoSearch(w http.ResponseWriter, r *http.Request) error { 188 + repo := r.Context().Value(repoCtxKey).(*git.Repo) 189 + 190 + var results []git.GrepResult 191 + search := r.URL.Query().Get("q") 192 + if q := strings.TrimSpace(search); q != "" { 193 + var err error 194 + results, err = repo.Grep(q) 195 + if err != nil { 196 + return httperr.Error(err) 197 + } 198 + for idx, result := range results { 199 + var buf bytes.Buffer 200 + if err := markup.Snippet([]byte(result.Content), filepath.Base(result.File), result.StartLine, &buf); err != nil { 201 + return httperr.Error(err) 202 + } 203 + results[idx].Content = buf.String() 204 + } 205 + 206 + } 207 + 208 + if err := html.RepoSearch(html.SearchContext{ 209 + BaseContext: rh.baseContext(), 210 + RepoHeaderComponentContext: rh.repoHeaderContext(repo, r), 211 + Results: results, 212 + }).Render(r.Context(), w); err != nil { 213 + return httperr.Error(err) 214 + } 215 + 216 + return nil 217 + }