diff --git a/models/issues/comment_code.go b/models/issues/comment_code.go
index b562aab500..28f9fd531d 100644
--- a/models/issues/comment_code.go
+++ b/models/issues/comment_code.go
@@ -18,11 +18,11 @@ import (
 type CodeComments map[string]map[int64][]*Comment
 
 // FetchCodeComments will return a 2d-map: ["Path"]["Line"] = Comments at line
-func FetchCodeComments(ctx context.Context, issue *Issue, currentUser *user_model.User, showOutdatedComments bool) (CodeComments, error) {
-	return fetchCodeCommentsByReview(ctx, issue, currentUser, nil, showOutdatedComments)
+func FetchCodeComments(ctx context.Context, issue *Issue, currentUser *user_model.User, showOutdatedComments bool, filePath *string) (CodeComments, error) {
+	return fetchCodeCommentsByReview(ctx, issue, currentUser, nil, showOutdatedComments, filePath)
 }
 
-func fetchCodeCommentsByReview(ctx context.Context, issue *Issue, currentUser *user_model.User, review *Review, showOutdatedComments bool) (CodeComments, error) {
+func fetchCodeCommentsByReview(ctx context.Context, issue *Issue, currentUser *user_model.User, review *Review, showOutdatedComments bool, filePath *string) (CodeComments, error) {
 	pathToLineToComment := make(CodeComments)
 	if review == nil {
 		review = &Review{ID: 0}
@@ -33,6 +33,15 @@ func fetchCodeCommentsByReview(ctx context.Context, issue *Issue, currentUser *u
 		ReviewID: review.ID,
 	}
 
+	if filePath != nil {
+		opts = FindCommentsOptions{
+			Type:     CommentTypeCode,
+			IssueID:  issue.ID,
+			ReviewID: review.ID,
+			TreePath: *filePath,
+		}
+	}
+
 	comments, err := findCodeComments(ctx, opts, issue, currentUser, review, showOutdatedComments)
 	if err != nil {
 		return nil, err
diff --git a/models/issues/comment_test.go b/models/issues/comment_test.go
index c08e3b970d..eb37be56e5 100644
--- a/models/issues/comment_test.go
+++ b/models/issues/comment_test.go
@@ -68,7 +68,7 @@ func TestFetchCodeComments(t *testing.T) {
 
 	issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 2})
 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
-	res, err := issues_model.FetchCodeComments(db.DefaultContext, issue, user, false)
+	res, err := issues_model.FetchCodeComments(db.DefaultContext, issue, user, false, nil)
 	assert.NoError(t, err)
 	assert.Contains(t, res, "README.md")
 	assert.Contains(t, res["README.md"], int64(4))
@@ -76,7 +76,7 @@ func TestFetchCodeComments(t *testing.T) {
 	assert.Equal(t, int64(4), res["README.md"][4][0].ID)
 
 	user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
-	res, err = issues_model.FetchCodeComments(db.DefaultContext, issue, user2, false)
+	res, err = issues_model.FetchCodeComments(db.DefaultContext, issue, user2, false, nil)
 	assert.NoError(t, err)
 	assert.Len(t, res, 1)
 }
diff --git a/models/issues/review.go b/models/issues/review.go
index 2667bdaa7b..0f8f5e6cfd 100644
--- a/models/issues/review.go
+++ b/models/issues/review.go
@@ -159,7 +159,7 @@ func (r *Review) LoadCodeComments(ctx context.Context) (err error) {
 	if err = r.LoadIssue(ctx); err != nil {
 		return err
 	}
-	r.CodeComments, err = fetchCodeCommentsByReview(ctx, r.Issue, nil, r, false)
+	r.CodeComments, err = fetchCodeCommentsByReview(ctx, r.Issue, nil, r, false, nil)
 	return err
 }
 
diff --git a/routers/web/repo/compare.go b/routers/web/repo/compare.go
index 2c36477e6a..b9d8f131fd 100644
--- a/routers/web/repo/compare.go
+++ b/routers/web/repo/compare.go
@@ -14,6 +14,7 @@ import (
 	"net/http"
 	"net/url"
 	"path/filepath"
+	"sort"
 	"strings"
 
 	"code.gitea.io/gitea/models/db"
@@ -40,6 +41,7 @@ import (
 	"code.gitea.io/gitea/services/context"
 	"code.gitea.io/gitea/services/context/upload"
 	"code.gitea.io/gitea/services/gitdiff"
+	user_service "code.gitea.io/gitea/services/user"
 )
 
 const (
@@ -876,6 +878,10 @@ func ExcerptBlob(ctx *context.Context) {
 	direction := ctx.FormString("direction")
 	filePath := ctx.FormString("path")
 	gitRepo := ctx.Repo.GitRepo
+	if ctx.FormBool("pull") {
+		ctx.Data["PageIsPullFiles"] = true
+	}
+
 	if ctx.Data["PageIsWiki"] == true {
 		var err error
 		gitRepo, err = gitrepo.OpenRepository(ctx, ctx.Repo.Repository.WikiStorageRepo())
@@ -936,7 +942,9 @@ func ExcerptBlob(ctx *context.Context) {
 				RightIdx:      idxRight,
 				LeftHunkSize:  leftHunkSize,
 				RightHunkSize: rightHunkSize,
+				HasComments:   false,
 			},
+			Comments: nil,
 		}
 		switch direction {
 		case "up":
@@ -945,10 +953,69 @@ func ExcerptBlob(ctx *context.Context) {
 			section.Lines = append(section.Lines, lineSection)
 		}
 	}
+	issueIndex := ctx.FormInt64("issue_index")
+	if ctx.FormBool("pull") && issueIndex > 0 {
+		issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, issueIndex)
+		if err != nil {
+			ctx.ServerError("GetIssueByIndex", err)
+			return
+		}
+		allComments, err := issues_model.FetchCodeComments(ctx, issue, ctx.Doer, false, &filePath)
+		if err != nil {
+			ctx.ServerError("FetchCodeComments", err)
+			return
+		}
+		lineCommits := allComments[filePath]
+		for index, line := range section.Lines {
+			if line.SectionInfo != nil && line.Type == 4 && !(line.SectionInfo.LastRightIdx == 0 && index+1 == len(section.Lines)) {
+				start := int64(line.SectionInfo.LastRightIdx + 1)
+				end := int64(line.SectionInfo.RightIdx - 1)
+				for start <= end {
+					if _, ok := lineCommits[start]; ok {
+						if !line.SectionInfo.HasComments {
+							line.SectionInfo.HasComments = true
+							break
+						}
+					}
+					start++
+				}
+			}
+			if comments, ok := lineCommits[int64(line.LeftIdx*-1)]; ok {
+				line.Comments = append(line.Comments, comments...)
+			}
+			if comments, ok := lineCommits[int64(line.RightIdx)]; ok {
+				line.Comments = append(line.Comments, comments...)
+			}
+
+			sort.SliceStable(line.Comments, func(i, j int) bool {
+				return line.Comments[i].CreatedUnix < line.Comments[j].CreatedUnix
+			})
+		}
+		for _, line := range section.Lines {
+			for _, comment := range line.Comments {
+				if err := comment.LoadAttachments(ctx); err != nil {
+					ctx.ServerError("LoadAttachments", err)
+					return
+				}
+			}
+		}
+		ctx.Data["Issue"] = issue
+		ctx.Data["IssueIndex"] = issue.Index
+	}
 	ctx.Data["section"] = section
 	ctx.Data["FileNameHash"] = git.HashFilePathForWebUI(filePath)
 	ctx.Data["AfterCommitID"] = commitID
 	ctx.Data["Anchor"] = anchor
+	ctx.Data["CanBlockUser"] = func(blocker, blockee *user_model.User) bool {
+		return user_service.CanBlockUser(ctx, ctx.Doer, blocker, blockee)
+	}
+	if ctx.Data["SignedUserID"] == nil {
+		ctx.Data["SignedUserID"] = ctx.Doer.ID
+	}
+	ctx.Data["SignedUser"] = ctx.Doer
+	ctx.Data["IsSigned"] = ctx.Doer != nil
+	ctx.Data["Repository"] = ctx.Repo.Repository
+	ctx.Data["Permission"] = &ctx.Repo.Permission
 	ctx.HTML(http.StatusOK, tplBlobExcerpt)
 }
 
@@ -977,6 +1044,7 @@ func getExcerptLines(commit *git.Commit, filePath string, idxLeft, idxRight, chu
 			RightIdx: line + 1,
 			Type:     gitdiff.DiffLinePlain,
 			Content:  " " + lineText,
+			Comments: []*issues_model.Comment{},
 		}
 		diffLines = append(diffLines, diffLine)
 	}
diff --git a/routers/web/repo/pull.go b/routers/web/repo/pull.go
index c72664f8e9..3bd7caf67c 100644
--- a/routers/web/repo/pull.go
+++ b/routers/web/repo/pull.go
@@ -127,6 +127,7 @@ func getPullInfo(ctx *context.Context) (issue *issues_model.Issue, ok bool) {
 	}
 	ctx.Data["Title"] = fmt.Sprintf("#%d - %s", issue.Index, emoji.ReplaceAliases(issue.Title))
 	ctx.Data["Issue"] = issue
+	ctx.Data["IssueIndex"] = issue.Index
 
 	if !issue.IsPull {
 		ctx.NotFound(nil)
diff --git a/services/gitdiff/gitdiff.go b/services/gitdiff/gitdiff.go
index 9ee86d9dfc..05eb3198ed 100644
--- a/services/gitdiff/gitdiff.go
+++ b/services/gitdiff/gitdiff.go
@@ -95,6 +95,7 @@ type DiffLineSectionInfo struct {
 	RightIdx      int
 	LeftHunkSize  int
 	RightHunkSize int
+	HasComments   bool
 }
 
 // DiffHTMLOperation is the HTML version of diffmatchpatch.Diff
@@ -189,6 +190,7 @@ func getDiffLineSectionInfo(treePath, line string, lastLeftIdx, lastRightIdx int
 		RightIdx:      rightLine,
 		LeftHunkSize:  leftHunk,
 		RightHunkSize: righHunk,
+		HasComments:   false,
 	}
 }
 
@@ -431,6 +433,7 @@ func (diffFile *DiffFile) GetTailSectionAndLimitedContent(leftCommit, rightCommi
 			LastRightIdx: lastLine.RightIdx,
 			LeftIdx:      leftLineCount,
 			RightIdx:     rightLineCount,
+			HasComments:  false,
 		},
 	}
 	tailSection := &DiffSection{FileName: diffFile.Name, Lines: []*DiffLine{tailDiffLine}}
@@ -501,14 +504,28 @@ type Diff struct {
 
 // LoadComments loads comments into each line
 func (diff *Diff) LoadComments(ctx context.Context, issue *issues_model.Issue, currentUser *user_model.User, showOutdatedComments bool) error {
-	allComments, err := issues_model.FetchCodeComments(ctx, issue, currentUser, showOutdatedComments)
+	allComments, err := issues_model.FetchCodeComments(ctx, issue, currentUser, showOutdatedComments, nil)
 	if err != nil {
 		return err
 	}
+
 	for _, file := range diff.Files {
 		if lineCommits, ok := allComments[file.Name]; ok {
 			for _, section := range file.Sections {
-				for _, line := range section.Lines {
+				for index, line := range section.Lines {
+					if line.SectionInfo != nil && line.Type == 4 && !(line.SectionInfo.LastRightIdx == 0 && index+1 == len(section.Lines)) {
+						start := int64(line.SectionInfo.LastRightIdx + 1)
+						end := int64(line.SectionInfo.RightIdx - 1)
+						for start <= end {
+							if _, ok := lineCommits[start]; ok {
+								if !line.SectionInfo.HasComments {
+									line.SectionInfo.HasComments = true
+									break
+								}
+							}
+							start++
+						}
+					}
 					if comments, ok := lineCommits[int64(line.LeftIdx*-1)]; ok {
 						line.Comments = append(line.Comments, comments...)
 					}
diff --git a/services/repository/files/diff_test.go b/services/repository/files/diff_test.go
index ae702e4189..fd1d7caebc 100644
--- a/services/repository/files/diff_test.go
+++ b/services/repository/files/diff_test.go
@@ -62,6 +62,7 @@ func TestGetDiffPreview(t *testing.T) {
 									RightIdx:      1,
 									LeftHunkSize:  3,
 									RightHunkSize: 4,
+									HasComments:   false,
 								},
 							},
 							{
diff --git a/templates/repo/diff/blob_excerpt.tmpl b/templates/repo/diff/blob_excerpt.tmpl
index 4089d8fb33..941ac44ae2 100644
--- a/templates/repo/diff/blob_excerpt.tmpl
+++ b/templates/repo/diff/blob_excerpt.tmpl
@@ -1,10 +1,37 @@
 {{$blobExcerptLink := print $.RepoLink (Iif $.PageIsWiki "/wiki" "") "/blob_excerpt/" (PathEscape $.AfterCommitID) (QueryBuild "?" "anchor" $.Anchor)}}
 {{if $.IsSplitStyle}}
 	{{range $k, $line := $.section.Lines}}
+	{{$inlineDiff := $.section.GetComputedInlineDiffFor $line ctx.Locale}}
 	<tr class="{{.GetHTMLDiffLineType}}-code nl-{{$k}} ol-{{$k}} line-expanded">
 		{{if eq .GetType 4}}
 			{{$expandDirection := $line.GetExpandDirection}}
 			<td class="lines-num lines-num-old" data-line-num="{{if $line.LeftIdx}}{{$line.LeftIdx}}{{end}}">
+				<div class="lines-comment">
+					<div>
+						{{if $line.SectionInfo.HasComments}}
+							<button class="section-comment-icon">
+								{{svg "octicon-comment-discussion"}}
+							</button>
+						{{end}}
+					</div>
+					<div class="code-expander-buttons" data-expand-direction="{{$expandDirection}}">
+						{{if or (eq $expandDirection 3) (eq $expandDirection 5)}}
+							<button class="code-expander-button" hx-target="closest tr" hx-get="{{$.RepoLink}}/blob_excerpt/{{PathEscape $.AfterCommitID}}?{{$line.GetBlobExcerptQuery}}&style=split&direction=down&wiki={{$.PageIsWiki}}&pull={{$.PageIsPullFiles}}&anchor={{$.Anchor}}&issue_index={{$.IssueIndex}}">
+								{{svg "octicon-fold-down"}}
+							</button>
+						{{end}}
+						{{if or (eq $expandDirection 3) (eq $expandDirection 4)}}
+							<button class="code-expander-button" hx-target="closest tr" hx-get="{{$.RepoLink}}/blob_excerpt/{{PathEscape $.AfterCommitID}}?{{$line.GetBlobExcerptQuery}}&style=split&direction=up&wiki={{$.PageIsWiki}}&pull={{$.PageIsPullFiles}}&anchor={{$.Anchor}}&issue_index={{$.IssueIndex}}">
+								{{svg "octicon-fold-up"}}
+							</button>
+						{{end}}
+						{{if eq $expandDirection 2}}
+							<button class="code-expander-button" hx-target="closest tr" hx-get="{{$.RepoLink}}/blob_excerpt/{{PathEscape $.AfterCommitID}}?{{$line.GetBlobExcerptQuery}}&style=split&direction=&wiki={{$.PageIsWiki}}&pull={{$.PageIsPullFiles}}&anchor={{$.Anchor}}}&issue_index={{$.IssueIndex}}">
+								{{svg "octicon-fold"}}
+							</button>
+						{{end}}
+					</div>
+				</div>
 				<div class="code-expander-buttons" data-expand-direction="{{$expandDirection}}">
 				{{if or (eq $expandDirection 3) (eq $expandDirection 5)}}
 					<button class="code-expander-button" hx-target="closest tr" hx-get="{{$blobExcerptLink}}&{{$line.GetBlobExcerptQuery}}&style=split&direction=down">
@@ -28,62 +55,104 @@
 				{{- template "repo/diff/section_code" dict "diff" $inlineDiff -}}
 			</td>
 		{{else}}
-			{{$inlineDiff := $.section.GetComputedInlineDiffFor $line ctx.Locale}}
 			<td class="lines-num lines-num-old" data-line-num="{{if $line.LeftIdx}}{{$line.LeftIdx}}{{end}}"><span rel="{{if $line.LeftIdx}}diff-{{$.FileNameHash}}L{{$line.LeftIdx}}{{end}}"></span></td>
 			<td class="lines-escape lines-escape-old">{{if and $line.LeftIdx $inlineDiff.EscapeStatus.Escaped}}<button class="toggle-escape-button btn interact-bg" title="{{template "repo/diff/escape_title" dict "diff" $inlineDiff}}"></button>{{end}}</td>
 			<td class="lines-type-marker lines-type-marker-old">{{if $line.LeftIdx}}<span class="tw-font-mono" data-type-marker=""></span>{{end}}</td>
-			<td class="lines-code lines-code-old">
-				{{- if $line.LeftIdx -}}
-					{{- template "repo/diff/section_code" dict "diff" $inlineDiff -}}
-				{{- else -}}
-					<code class="code-inner"></code>
-				{{- end -}}
-			</td>
+			<td class="lines-code lines-code-old">{{/*
+				*/}}{{if and $.SignedUserID $.PageIsPullFiles}}{{/*
+					*/}}<button type="button" aria-label="{{ctx.Locale.Tr "repo.diff.comment.add_line_comment"}}" class="ui primary button add-code-comment add-code-comment-{{if $line.RightIdx}}right{{else}}left{{end}}{{if (not $line.CanComment)}} tw-invisible{{end}}" data-side="{{if $line.RightIdx}}right{{else}}left{{end}}" data-idx="{{if $line.RightIdx}}{{$line.RightIdx}}{{else}}{{$line.LeftIdx}}{{end}}">{{/*
+						*/}}{{svg "octicon-plus"}}{{/*
+					*/}}</button>{{/*
+				*/}}{{end}}{{/*
+				*/}}{{if $line.LeftIdx}}{{template "repo/diff/section_code" dict "diff" $inlineDiff}}{{else}}{{/*
+					*/}}<code class="code-inner"></code>{{/*
+				*/}}{{end}}{{/*
+			*/}}</td>
 			<td class="lines-num lines-num-new" data-line-num="{{if $line.RightIdx}}{{$line.RightIdx}}{{end}}"><span rel="{{if $line.RightIdx}}diff-{{$.FileNameHash}}R{{$line.RightIdx}}{{end}}"></span></td>
 			<td class="lines-escape lines-escape-new">{{if and $line.RightIdx $inlineDiff.EscapeStatus.Escaped}}<button class="toggle-escape-button btn interact-bg" title="{{template "repo/diff/escape_title" dict "diff" $inlineDiff}}"></button>{{end}}</td>
 			<td class="lines-type-marker lines-type-marker-new">{{if $line.RightIdx}}<span class="tw-font-mono" data-type-marker=""></span>{{end}}</td>
-			<td class="lines-code lines-code-new">
-				{{- if $line.RightIdx -}}
-					{{- template "repo/diff/section_code" dict "diff" $inlineDiff -}}
-				{{- else -}}
-					<code class="code-inner"></code>
-				{{- end -}}
-			</td>
+			<td class="lines-code lines-code-new">{{/*
+				*/}}{{if and $.SignedUserID $.PageIsPullFiles}}{{/*
+					*/}}<button type="button" aria-label="{{ctx.Locale.Tr "repo.diff.comment.add_line_comment"}}" class="ui primary button add-code-comment add-code-comment-{{if $line.RightIdx}}right{{else}}left{{end}}{{if (not $line.CanComment)}} tw-invisible{{end}}" data-side="{{if $line.RightIdx}}right{{else}}left{{end}}" data-idx="{{if $line.RightIdx}}{{$line.RightIdx}}{{else}}{{$line.LeftIdx}}{{end}}">{{/*
+						*/}}{{svg "octicon-plus"}}{{/*
+					*/}}</button>{{/*
+				*/}}{{end}}{{/*
+				*/}}{{if $line.RightIdx}}{{template "repo/diff/section_code" dict "diff" $inlineDiff}}{{else}}{{/*
+					*/}}<code class="code-inner"></code>{{/*
+				*/}}{{end}}{{/*
+			*/}}</td>
 		{{end}}
 	</tr>
+	{{if $line.Comments}}
+		<tr class="add-comment" data-line-type="{{.GetHTMLDiffLineType}}">
+			<td class="add-comment-right" colspan="5">
+				{{template "repo/diff/conversation" dict "." $ "comments" $line.Comments}}}
+			</td>
+		</tr>
+	{{end}}
 	{{end}}
 {{else}}
 	{{range $k, $line := $.section.Lines}}
+	{{$inlineDiff := $.section.GetComputedInlineDiffFor $line ctx.Locale}}
 	<tr class="{{.GetHTMLDiffLineType}}-code nl-{{$k}} ol-{{$k}} line-expanded">
 		{{if eq .GetType 4}}
 			{{$expandDirection := $line.GetExpandDirection}}
 			<td colspan="2" class="lines-num">
-				<div class="code-expander-buttons" data-expand-direction="{{$expandDirection}}">
-					{{if or (eq $expandDirection 3) (eq $expandDirection 5)}}
-						<button class="code-expander-button" hx-target="closest tr" hx-get="{{$blobExcerptLink}}&{{$line.GetBlobExcerptQuery}}&style=unified&direction=down">
-							{{svg "octicon-fold-down"}}
-						</button>
-					{{end}}
-					{{if or (eq $expandDirection 3) (eq $expandDirection 4)}}
-						<button class="code-expander-button" hx-target="closest tr" hx-get="{{$blobExcerptLink}}&{{$line.GetBlobExcerptQuery}}&style=unified&direction=up">
-							{{svg "octicon-fold-up"}}
-						</button>
-					{{end}}
-					{{if eq $expandDirection 2}}
-						<button class="code-expander-button" hx-target="closest tr" hx-get="{{$blobExcerptLink}}&{{$line.GetBlobExcerptQuery}}&style=unified">
-							{{svg "octicon-fold"}}
-						</button>
-					{{end}}
+				<div class="lines-comment">
+					<div>
+						{{if $line.SectionInfo.HasComments}}
+							<button class="section-comment-icon">
+								{{svg "octicon-comment-discussion"}}
+							</button>
+						{{end}}
+					</div>
+					<div class="code-expander-buttons" data-expand-direction="{{$expandDirection}}">
+						{{if or (eq $expandDirection 3) (eq $expandDirection 5)}}
+							<button class="code-expander-button" hx-target="closest tr" hx-get="{{$.RepoLink}}/blob_excerpt/{{PathEscape $.AfterCommitID}}?{{$line.GetBlobExcerptQuery}}&style=unified&direction=down&wiki={{$.PageIsWiki}}&pull={{$.PageIsPullFiles}}&anchor={{$.Anchor}}}&issue_index={{$.IssueIndex}}">
+								{{svg "octicon-fold-down"}}
+							</button>
+						{{end}}
+						{{if or (eq $expandDirection 3) (eq $expandDirection 4)}}
+							<button class="code-expander-button" hx-target="closest tr" hx-get="{{$.RepoLink}}/blob_excerpt/{{PathEscape $.AfterCommitID}}?{{$line.GetBlobExcerptQuery}}&style=unified&direction=up&wiki={{$.PageIsWiki}}&pull={{$.PageIsPullFiles}}&anchor={{$.Anchor}}}&issue_index={{$.IssueIndex}}">
+								{{svg "octicon-fold-up"}}
+							</button>
+						{{end}}
+						{{if eq $expandDirection 2}}
+							<button class="code-expander-button" hx-target="closest tr" hx-get="{{$.RepoLink}}/blob_excerpt/{{PathEscape $.AfterCommitID}}?{{$line.GetBlobExcerptQuery}}&style=unified&direction=&wiki={{$.PageIsWiki}}&pull={{$.PageIsPullFiles}}&anchor={{$.Anchor}}}&issue_index={{$.IssueIndex}}">
+								{{svg "octicon-fold"}}
+							</button>
+						{{end}}
+					</div>
 				</div>
 			</td>
 		{{else}}
 			<td class="lines-num lines-num-old" data-line-num="{{if $line.LeftIdx}}{{$line.LeftIdx}}{{end}}"><span rel="{{if $line.LeftIdx}}diff-{{$.FileNameHash}}L{{$line.LeftIdx}}{{end}}"></span></td>
 			<td class="lines-num lines-num-new" data-line-num="{{if $line.RightIdx}}{{$line.RightIdx}}{{end}}"><span rel="{{if $line.RightIdx}}diff-{{$.FileNameHash}}R{{$line.RightIdx}}{{end}}"></span></td>
 		{{end}}
-		{{$inlineDiff := $.section.GetComputedInlineDiffFor $line ctx.Locale}}
 		<td class="lines-escape">{{if $inlineDiff.EscapeStatus.Escaped}}<button class="toggle-escape-button btn interact-bg" title="{{template "repo/diff/escape_title" dict "diff" $inlineDiff}}"></button>{{end}}</td>
 		<td class="lines-type-marker"><span class="tw-font-mono" data-type-marker="{{$line.GetLineTypeMarker}}"></span></td>
-		<td class="lines-code{{if (not $line.RightIdx)}} lines-code-old{{end}}"><code {{if $inlineDiff.EscapeStatus.Escaped}}class="code-inner has-escaped" title="{{template "repo/diff/escape_title" dict "diff" $inlineDiff}}"{{else}}class="code-inner"{{end}}>{{$inlineDiff.Content}}</code></td>
+		<td class="lines-code{{if (not $line.RightIdx)}} lines-code-old{{end}}">
+			{{if and $.SignedUserID $.PageIsPullFiles}}
+				<button type="button" aria-label="{{ctx.Locale.Tr "repo.diff.comment.add_line_comment"}}" class="ui primary button add-code-comment add-code-comment-{{if $line.RightIdx}}right{{else}}left{{end}}{{if (not $line.CanComment)}} tw-invisible{{end}}" data-side="{{if $line.RightIdx}}right{{else}}left{{end}}" data-idx="{{if $line.RightIdx}}{{$line.RightIdx}}{{else}}{{$line.LeftIdx}}{{end}}">
+					{{svg "octicon-plus"}}
+				</button>
+			{{end}}
+			<code {{if $inlineDiff.EscapeStatus.Escaped}}class="code-inner has-escaped" title="{{template "repo/diff/escape_title" dict "diff" $inlineDiff}}"{{else}}class="code-inner"{{end}}>{{$inlineDiff.Content}}</code>
+		</td>
 	</tr>
+	{{if $line.Comments}}
+		<tr class="add-comment" data-line-type="{{.GetHTMLDiffLineType}}">
+			<td class="add-comment-right" colspan="5">
+				{{template "repo/diff/conversation" dict "." $ "comments" $line.Comments}}
+			</td>
+		</tr>
+		<button class="pull-request-diff-comments hidden"></button>
+		<script>
+			var $buttons = document.querySelector('button.pull-request-diff-comments');
+			if ($buttons) {
+				$buttons.click();
+			}
+		</script>
+	{{end}}
 	{{end}}
 {{end}}
diff --git a/templates/repo/diff/section_split.tmpl b/templates/repo/diff/section_split.tmpl
index 9953db5eb2..a05ac8b836 100644
--- a/templates/repo/diff/section_split.tmpl
+++ b/templates/repo/diff/section_split.tmpl
@@ -1,4 +1,6 @@
 {{$file := .file}}
+{{$blobExcerptRepoLink := or ctx.RootData.CommitRepoLink ctx.RootData.RepoLink}}
+{{$issueIndex := or ctx.RootData.IssueIndex $.root.IssueIndex}}
 {{$blobExcerptLink := print (or ctx.RootData.CommitRepoLink ctx.RootData.RepoLink) (Iif $.root.PageIsWiki "/wiki" "") "/blob_excerpt/" (PathEscape $.root.AfterCommitID) "?"}}
 <colgroup>
 	<col width="50">
@@ -18,22 +20,31 @@
 				{{if eq .GetType 4}}
 					{{$expandDirection := $line.GetExpandDirection}}
 					<td class="lines-num lines-num-old">
-						<div class="code-expander-buttons" data-expand-direction="{{$expandDirection}}">
-						{{if or (eq $expandDirection 3) (eq $expandDirection 5)}}
-							<button class="code-expander-button" hx-target="closest tr" hx-get="{{$blobExcerptLink}}&{{$line.GetBlobExcerptQuery}}&style=split&direction=down&&anchor=diff-{{$file.NameHash}}K{{$line.SectionInfo.RightIdx}}">
-								{{svg "octicon-fold-down"}}
-							</button>
-						{{end}}
-						{{if or (eq $expandDirection 3) (eq $expandDirection 4)}}
-							<button class="code-expander-button" hx-target="closest tr" hx-get="{{$blobExcerptLink}}&{{$line.GetBlobExcerptQuery}}&style=split&direction=up&anchor=diff-{{$file.NameHash}}K{{$line.SectionInfo.RightIdx}}">
-								{{svg "octicon-fold-up"}}
-							</button>
-						{{end}}
-						{{if eq $expandDirection 2}}
-							<button class="code-expander-button" hx-target="closest tr" hx-get="{{$blobExcerptLink}}&{{$line.GetBlobExcerptQuery}}&style=split&anchor=diff-{{$file.NameHash}}K{{$line.SectionInfo.RightIdx}}">
-								{{svg "octicon-fold"}}
-							</button>
-						{{end}}
+						<div class="lines-comment">
+							<div>
+								{{if $line.SectionInfo.HasComments}}
+									<button class="section-comment-icon">
+										{{svg "octicon-comment-discussion"}}
+									</button>
+								{{end}}
+							</div>
+							<div class="code-expander-buttons" data-expand-direction="{{$expandDirection}}">
+								{{if or (eq $expandDirection 3) (eq $expandDirection 5)}}
+									<button class="code-expander-button" hx-target="closest tr" hx-get="{{$blobExcerptRepoLink}}/blob_excerpt/{{PathEscape $.root.AfterCommitID}}?{{$line.GetBlobExcerptQuery}}&style=split&direction=down&wiki={{$.root.PageIsWiki}}&pull={{$.root.PageIsPullFiles}}&anchor=diff-{{$file.NameHash}}K{{$line.SectionInfo.RightIdx}}&issue_index={{$issueIndex}}">
+										{{svg "octicon-fold-down"}}
+									</button>
+								{{end}}
+								{{if or (eq $expandDirection 3) (eq $expandDirection 4)}}
+									<button class="code-expander-button" hx-target="closest tr" hx-get="{{$blobExcerptRepoLink}}/blob_excerpt/{{PathEscape $.root.AfterCommitID}}?{{$line.GetBlobExcerptQuery}}&style=split&direction=up&wiki={{$.root.PageIsWiki}}&pull={{$.root.PageIsPullFiles}}&anchor=diff-{{$file.NameHash}}K{{$line.SectionInfo.RightIdx}}&issue_index={{$issueIndex}}">
+										{{svg "octicon-fold-up"}}
+									</button>
+								{{end}}
+								{{if eq $expandDirection 2}}
+									<button class="code-expander-button" hx-target="closest tr" hx-get="{{$blobExcerptRepoLink}}/blob_excerpt/{{PathEscape $.root.AfterCommitID}}?{{$line.GetBlobExcerptQuery}}&style=split&direction=&wiki={{$.root.PageIsWiki}}&pull={{$.root.PageIsPullFiles}}&anchor=diff-{{$file.NameHash}}K{{$line.SectionInfo.RightIdx}}&issue_index={{$issueIndex}}">
+										{{svg "octicon-fold"}}
+									</button>
+								{{end}}
+							</div>
 						</div>
 					</td>{{$inlineDiff := $section.GetComputedInlineDiffFor $line ctx.Locale}}
 					<td class="lines-escape lines-escape-old">{{if $inlineDiff.EscapeStatus.Escaped}}<button class="toggle-escape-button btn interact-bg" title="{{template "repo/diff/escape_title" dict "diff" $inlineDiff}}"></button>{{end}}</td>
diff --git a/templates/repo/diff/section_unified.tmpl b/templates/repo/diff/section_unified.tmpl
index cb612bc27c..948c67f178 100644
--- a/templates/repo/diff/section_unified.tmpl
+++ b/templates/repo/diff/section_unified.tmpl
@@ -1,4 +1,6 @@
 {{$file := .file}}
+{{$blobExcerptRepoLink := or ctx.RootData.CommitRepoLink ctx.RootData.RepoLink}}
+{{$issueIndex := or ctx.RootData.IssueIndex $.root.IssueIndex}}
 {{$repoLink := or ctx.RootData.CommitRepoLink ctx.RootData.RepoLink}}
 {{$afterCommitID := or $.root.AfterCommitID "no-after-commit-id"}}{{/* this tmpl is also used by the PR Conversation page, so the "AfterCommitID" may not exist */}}
 {{$blobExcerptLink := print $repoLink (Iif $.root.PageIsWiki "/wiki" "") "/blob_excerpt/" (PathEscape $afterCommitID) "?"}}
@@ -16,22 +18,31 @@
 				{{if $.root.AfterCommitID}}
 					{{$expandDirection := $line.GetExpandDirection}}
 					<td colspan="2" class="lines-num">
-						<div class="code-expander-buttons" data-expand-direction="{{$expandDirection}}">
-							{{if or (eq $expandDirection 3) (eq $expandDirection 5)}}
-								<button class="code-expander-button" hx-target="closest tr" hx-get="{{$blobExcerptLink}}&{{$line.GetBlobExcerptQuery}}&style=unified&direction=down&anchor=diff-{{$file.NameHash}}K{{$line.SectionInfo.RightIdx}}">
-									{{svg "octicon-fold-down"}}
-								</button>
-							{{end}}
-							{{if or (eq $expandDirection 3) (eq $expandDirection 4)}}
-								<button class="code-expander-button" hx-target="closest tr" hx-get="{{$blobExcerptLink}}&{{$line.GetBlobExcerptQuery}}&style=unified&direction=up&anchor=diff-{{$file.NameHash}}K{{$line.SectionInfo.RightIdx}}">
-									{{svg "octicon-fold-up"}}
-								</button>
-							{{end}}
-							{{if eq $expandDirection 2}}
-								<button class="code-expander-button" hx-target="closest tr" hx-get="{{$blobExcerptLink}}&{{$line.GetBlobExcerptQuery}}&style=unified&anchor=diff-{{$file.NameHash}}K{{$line.SectionInfo.RightIdx}}">
-									{{svg "octicon-fold"}}
-								</button>
-							{{end}}
+						<div class="lines-comment">
+							<div>
+								{{if $line.SectionInfo.HasComments}}
+									<button class="section-comment-icon">
+										{{svg "octicon-comment-discussion"}}
+									</button>
+								{{end}}
+							</div>
+							<div class="code-expander-buttons" data-expand-direction="{{$expandDirection}}">
+								{{if or (eq $expandDirection 3) (eq $expandDirection 5)}}
+									<button class="code-expander-button" hx-target="closest tr" hx-get="{{$blobExcerptRepoLink}}/blob_excerpt/{{PathEscape $.root.AfterCommitID}}?{{$line.GetBlobExcerptQuery}}&style=unified&direction=down&wiki={{$.root.PageIsWiki}}&pull={{$.root.PageIsPullFiles}}&anchor=diff-{{$file.NameHash}}K{{$line.SectionInfo.RightIdx}}&issue_index={{$issueIndex}}">
+										{{svg "octicon-fold-down"}}
+									</button>
+								{{end}}
+								{{if or (eq $expandDirection 3) (eq $expandDirection 4)}}
+									<button class="code-expander-button" hx-target="closest tr" hx-get="{{$blobExcerptRepoLink}}/blob_excerpt/{{PathEscape $.root.AfterCommitID}}?{{$line.GetBlobExcerptQuery}}&style=unified&direction=up&wiki={{$.root.PageIsWiki}}&pull={{$.root.PageIsPullFiles}}&anchor=diff-{{$file.NameHash}}K{{$line.SectionInfo.RightIdx}}&issue_index={{$issueIndex}}">
+										{{svg "octicon-fold-up"}}
+									</button>
+								{{end}}
+								{{if eq $expandDirection 2}}
+									<button class="code-expander-button" hx-target="closest tr" hx-get="{{$blobExcerptRepoLink}}/blob_excerpt/{{PathEscape $.root.AfterCommitID}}?{{$line.GetBlobExcerptQuery}}&style=unified&direction=&wiki={{$.root.PageIsWiki}}&pull={{$.root.PageIsPullFiles}}&anchor=diff-{{$file.NameHash}}K{{$line.SectionInfo.RightIdx}}&issue_index={{$issueIndex}}">
+										{{svg "octicon-fold"}}
+									</button>
+								{{end}}
+							</div>
 						</div>
 					</td>
 				{{else}}
diff --git a/web_src/css/base.css b/web_src/css/base.css
index 37ee7f5832..4b448aa822 100644
--- a/web_src/css/base.css
+++ b/web_src/css/base.css
@@ -907,6 +907,17 @@ overflow-menu .ui.label {
   font-family: var(--fonts-regular);
 }
 
+.lines-comment {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  gap: 1px;
+}
+
+.section-comment-icon {
+  cursor: default;
+}
+
 .lines-commit .blame-info .blame-data .blame-message {
   flex-grow: 2;
   overflow: hidden;
diff --git a/web_src/js/features/repo-diff.ts b/web_src/js/features/repo-diff.ts
index ad1da5c2fa..d37c7e90a5 100644
--- a/web_src/js/features/repo-diff.ts
+++ b/web_src/js/features/repo-diff.ts
@@ -10,6 +10,7 @@ import {POST, GET} from '../modules/fetch.ts';
 import {createTippy} from '../modules/tippy.ts';
 import {invertFileFolding} from './file-fold.ts';
 import {parseDom} from '../utils.ts';
+import {initGlobalDropdown} from './common-page.ts';
 import {registerGlobalSelectorFunc} from '../modules/observer.ts';
 
 const {i18n} = window.config;
@@ -92,6 +93,12 @@ function initRepoDiffConversationForm() {
     }
   });
 
+  addDelegatedEventListener(document, 'click', '.pull-request-diff-comments', async (el, e) => {
+    e.preventDefault();
+    initGlobalDropdown();
+    // Post initiation cleaning up the buttons and scripts
+    el.remove();
+  });
   addDelegatedEventListener(document, 'click', '.resolve-conversation', async (el, e) => {
     e.preventDefault();
     const comment_id = el.getAttribute('data-comment-id');