feat: 评论按 ThreadParentID 嵌套树展示

修复 html/template 中 {{template}} 重置 $ 导致渲染失败,页面字段随 commentItemData 下传。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-30 04:28:44 +08:00
parent f0358e0f71
commit 7f048bd9b5
8 changed files with 310 additions and 148 deletions

View File

@@ -99,7 +99,7 @@
## F. 评论 ## F. 评论
- [x] 按帖拉取评论列表(楼层、引用目标)— SSR 帖详情;扁平列表 + `reply_to` 展示(嵌套树 UI 未做) - [x] 按帖拉取评论列表(楼层、引用目标)— SSR 帖详情;`ThreadParentID` 嵌套树 + 全局 `#floor` 缩进
- [x] 发表评论(登录);支持 `reply_to`、私密评论 — `POST /post/:id/comments` - [x] 发表评论(登录);支持 `reply_to`、私密评论 — `POST /post/:id/comments`
- [ ] 游客评论(公开接口可写,字段 guest_* - [ ] 游客评论(公开接口可写,字段 guest_*
- [x] 编辑评论(时限);删除评论 — SSR 帖详情入口 + `/comments/:cid/edit|delete`;作者软删进回收站 - [x] 编辑评论(时限);删除评论 — SSR 帖详情入口 + `/comments/:cid/edit|delete`;作者软删进回收站

View File

@@ -151,7 +151,7 @@
| 投票卡 | 选选项提交;显示百分比;作者可结束 — SSR `/post/:id/poll/vote|close` | | 投票卡 | 选选项提交;显示百分比;作者可结束 — SSR `/post/:id/poll/vote|close` |
| 悬赏条 | 显示积分与状态;采纳按钮在他人评论上;退款按钮按规则禁用并提示 — SSR `/post/:id/bounty/award|refund` | | 悬赏条 | 显示积分与状态;采纳按钮在他人评论上;退款按钮按规则禁用并提示 — SSR `/post/:id/bounty/award|refund` |
| 抽奖卡 | 显示参与人数;开奖;中奖名单 — SSR `/post/:id/lottery/draw` | | 抽奖卡 | 显示参与人数;开奖;中奖名单 — SSR `/post/:id/lottery/draw` |
| 评论 | 楼层列表、`reply_to` 引用、私密开关、点赞、举报编辑(时限)、删除、回复/@ 通知 — SSR;嵌套树 UI 未做 | | 评论 | 楼层列表、`reply_to` 引用、嵌套树(`ThreadParentID` + 缩进,全局 `#floor`)、私密/赞/举报/编辑/删除、回复/@ 通知 — SSR |
| 修订 | 列表 + 单条快照 — SSR `/post/:id/revisions`(作者/管理员;无 diff | | 修订 | 列表 + 单条快照 — SSR `/post/:id/revisions`(作者/管理员;无 diff |
| 图片 | Lightbox 查看 | | 图片 | Lightbox 查看 |

View File

@@ -3,7 +3,6 @@
> **读者**:产品方 / 实现 AI > **读者**:产品方 / 实现 AI
> **分支**`rebuild/gitea-ssr``main` = SPA 对照) > **分支**`rebuild/gitea-ssr``main` = SPA 对照)
> **验收清单** [02-features.md](02-features.md) > **验收清单** [02-features.md](02-features.md)
> **架构** [08-gitea-ssr-architecture.md](08-gitea-ssr-architecture.md)
--- ---
@@ -11,15 +10,15 @@
| 项 | 值 | | 项 | 值 |
|----|-----| |----|-----|
| **上一刀** | 完整 Limits + 伪静态 Admin / 规范 URL | | **上一刀** | 嵌套评论树(全局楼层 + 缩进) |
| **下一刀** | **后置或体验**需点名OIDC、S3 热切换、编辑器、嵌套评论、Gitea…) | | **下一刀** | 需点名OIDC / S3 / 编辑器 / Gitea / 主题…) |
| **工作区** | 应干净 | | **工作区** | 应干净 |
--- ---
## 一句话状态 ## 一句话状态
核心论坛、Admin、§H、品牌图、发评限流、**完整 Limits伪静态**已齐。余量多为后置产品化与体验打磨。 核心论坛、Admin、Limits/伪静态、**嵌套评论**已齐。余量多为后置与体验打磨。
--- ---
@@ -27,22 +26,16 @@
| 域 | 状态 | | 域 | 状态 |
|----|------| |----|------|
| Admin users / badges / media | 已迁 | | 五种帖 + Admin P1 + §H | 已迁 |
| §H 等级 / 徽章 / 防刷 | 已迁 | | Limits / 伪静态 / 品牌图 | 已迁 |
| 品牌 Logo/Favicon/OG + 发评限流 | 已迁 | | 评论嵌套树 UI | 已迁 |
| 内容 Limits + 伪静态 | 已迁 |
--- ---
## 未完成 ## 未完成
### P3 体验 - P3主题、TipTip、游客评论、修订 diff
- 后置OIDC、S3 热切换、Gitea `/projects`
- 主题、侧栏折叠、虚拟滚动、TipTap、游客评论、嵌套树、修订 diff
### 后置
- OIDC Admin CRUD、S3 热切换 UI、Gitea `/projects`、WebP 产品化
--- ---

View File

@@ -412,10 +412,28 @@ body.j13-body {
} }
.j13-comments { margin-top: 2rem; } .j13-comments { margin-top: 2rem; }
.j13-comment-list { list-style: none; margin: 0; padding: 0; } .j13-comment-list { list-style: none; margin: 0; padding: 0; }
.j13-comment-list--nested {
margin: 0.5rem 0 0;
padding: 0 0 0 0.85rem;
border-left: 2px solid var(--j13-border);
}
.j13-comment { .j13-comment {
padding: 0.85rem 0; padding: 0.85rem 0;
border-bottom: 1px solid var(--j13-border); border-bottom: 1px solid var(--j13-border);
} }
.j13-comment-list--nested > .j13-comment {
border-bottom-color: transparent;
padding-left: 0.65rem;
}
.j13-comment--depth-0 { }
.j13-comment--depth-1,
.j13-comment--depth-2,
.j13-comment--depth-3,
.j13-comment--depth-4,
.j13-comment--depth-5,
.j13-comment--depth-6 {
background: transparent;
}
.j13-comment__meta { .j13-comment__meta {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
@@ -425,16 +443,26 @@ body.j13-body {
margin-bottom: 0.35rem; margin-bottom: 0.35rem;
align-items: center; align-items: center;
} }
.j13-comment__floor { font-weight: 600; color: var(--j13-fg, inherit); }
.j13-comment__reply-to a { color: var(--j13-accent); text-decoration: none; } .j13-comment__reply-to a { color: var(--j13-accent); text-decoration: none; }
.j13-comment__actions { .j13-comment__actions {
display: flex; display: flex;
gap: 0.75rem; gap: 0.75rem;
align-items: center; align-items: center;
margin-top: 0.4rem; margin-top: 0.4rem;
flex-wrap: wrap;
} }
.j13-comment-reply-hint { margin: 0 0 0.5rem; } .j13-comment-reply-hint { margin: 0 0 0.5rem; }
.j13-comment-form textarea { width: 100%; } .j13-comment-form textarea { width: 100%; }
.j13-comment-form .j13-form__row { margin-top: 0.5rem; } .j13-comment-form .j13-form__row { margin-top: 0.5rem; }
@media (max-width: 600px) {
.j13-comment-list--nested {
padding-left: 0.55rem;
}
.j13-comment-list--nested > .j13-comment {
padding-left: 0.4rem;
}
}
.j13-muted { color: var(--j13-muted); } .j13-muted { color: var(--j13-muted); }
.j13-install fieldset { .j13-install fieldset {
border: 1px solid var(--j13-border); border: 1px solid var(--j13-border);

View File

@@ -40,7 +40,7 @@ type PostPageData struct {
Favorited bool Favorited bool
BodyHTML string BodyHTML string
CommentCount int CommentCount int
Comments []CommentView Comments []commentItemData
CommentsLocked bool CommentsLocked bool
CanEdit bool CanEdit bool
CanViewRevisions bool CanViewRevisions bool
@@ -114,6 +114,9 @@ type CommentView struct {
ReplyToID uint ReplyToID uint
ReplyToFloor int ReplyToFloor int
ReplyToAuthor string ReplyToAuthor string
ThreadParentID uint
Depth int
Children []CommentView
LikeCount int LikeCount int
Liked bool Liked bool
IsPrivate bool IsPrivate bool
@@ -126,6 +129,102 @@ type CommentView struct {
IsBountyAward bool IsBountyAward bool
} }
const commentNestMaxDepth = 6
// commentItemData 嵌套评论渲染上下文({{template}} 会重置 $,需自带页面字段)
type commentItemData struct {
CommentView
PostID uint
CSRF string
LoggedIn bool
CommentsLocked bool
PermalinkSuffix string
}
// Wrap 子评论继承同一帖上下文
func (d commentItemData) Wrap(c CommentView) commentItemData {
return commentItemData{
CommentView: c,
PostID: d.PostID,
CSRF: d.CSRF,
LoggedIn: d.LoggedIn,
CommentsLocked: d.CommentsLocked,
PermalinkSuffix: d.PermalinkSuffix,
}
}
func (d Deps) commentItemsForPage(ctx *webctx.Context, postID uint, locked bool, tree []CommentView) []commentItemData {
out := make([]commentItemData, 0, len(tree))
base := commentItemData{
PostID: postID,
CSRF: ctx.EnsureCSRF(),
LoggedIn: ctx.IsSigned(),
CommentsLocked: locked,
PermalinkSuffix: d.permalink().Suffix(),
}
for _, c := range tree {
item := base
item.CommentView = c
out = append(out, item)
}
return out
}
// buildCommentTree 按 ThreadParentID 组装嵌套树(同层保持楼层顺序)
func buildCommentTree(flat []CommentView) []CommentView {
if len(flat) == 0 {
return nil
}
nodes := make([]CommentView, len(flat))
copy(nodes, flat)
byID := make(map[uint]int, len(nodes))
for i := range nodes {
byID[nodes[i].ID] = i
nodes[i].Children = nil
}
childIdx := make(map[uint][]int, len(nodes))
roots := make([]int, 0, len(nodes))
for i := range nodes {
pid := nodes[i].ThreadParentID
if pid == 0 {
roots = append(roots, i)
continue
}
if _, ok := byID[pid]; !ok {
roots = append(roots, i)
continue
}
childIdx[pid] = append(childIdx[pid], i)
}
var attach func(i, depth int) CommentView
attach = func(i, depth int) CommentView {
n := nodes[i]
if depth > commentNestMaxDepth {
depth = commentNestMaxDepth
}
n.Depth = depth
kids := childIdx[n.ID]
if len(kids) == 0 {
n.Children = nil
return n
}
n.Children = make([]CommentView, 0, len(kids))
nextDepth := depth + 1
if nextDepth > commentNestMaxDepth {
nextDepth = commentNestMaxDepth
}
for _, ci := range kids {
n.Children = append(n.Children, attach(ci, nextDepth))
}
return n
}
out := make([]CommentView, 0, len(roots))
for _, i := range roots {
out = append(out, attach(i, 0))
}
return out
}
// PostView GET /post/:id // PostView GET /post/:id
func (d Deps) PostView(c *gin.Context) { func (d Deps) PostView(c *gin.Context) {
ctx := d.ctx(c) ctx := d.ctx(c)
@@ -156,6 +255,7 @@ func (d Deps) PostView(c *gin.Context) {
(ctx.IsAdmin() || post.UserID == ctx.UserID()) (ctx.IsAdmin() || post.UserID == ctx.UserID())
comments, _ := d.Comment.ListByPost(uint(id), ctx.UserID(), ctx.IsAdmin(), post.UserID, nil) comments, _ := d.Comment.ListByPost(uint(id), ctx.UserID(), ctx.IsAdmin(), post.UserID, nil)
commentTotal := len(comments)
cv := make([]CommentView, 0, len(comments)) cv := make([]CommentView, 0, len(comments))
for _, cm := range comments { for _, cm := range comments {
an := strings.TrimSpace(cm.User.Nickname) an := strings.TrimSpace(cm.User.Nickname)
@@ -175,6 +275,9 @@ func (d Deps) PostView(c *gin.Context) {
cm.Status == models.ContentStatusPublished && cm.UserID != post.UserID && cm.UserID > 0, cm.Status == models.ContentStatusPublished && cm.UserID != post.UserID && cm.UserID > 0,
IsBountyAward: post.BountyCommentID > 0 && cm.ID == post.BountyCommentID, IsBountyAward: post.BountyCommentID > 0 && cm.ID == post.BountyCommentID,
} }
if cm.ThreadParentID != nil {
view.ThreadParentID = *cm.ThreadParentID
}
if cm.Status == models.ContentStatusPending || cm.Status == models.ContentStatusRejected { if cm.Status == models.ContentStatusPending || cm.Status == models.ContentStatusRejected {
if ctx.IsAdmin() || (cm.UserID > 0 && cm.UserID == ctx.UserID()) { if ctx.IsAdmin() || (cm.UserID > 0 && cm.UserID == ctx.UserID()) {
view.StatusLabel = contentStatusLabel(cm.Status) view.StatusLabel = contentStatusLabel(cm.Status)
@@ -196,6 +299,7 @@ func (d Deps) PostView(c *gin.Context) {
} }
cv = append(cv, view) cv = append(cv, view)
} }
cv = buildCommentTree(cv)
author := strings.TrimSpace(post.User.Nickname) author := strings.TrimSpace(post.User.Nickname)
if author == "" { if author == "" {
@@ -277,7 +381,8 @@ func (d Deps) PostView(c *gin.Context) {
PostTypeLabel: postTypeLabel(post.PostType), CreatedLabel: formatTime(post.CreatedAt), PostTypeLabel: postTypeLabel(post.PostType), CreatedLabel: formatTime(post.CreatedAt),
ViewCount: post.ViewCount, LikeCount: post.LikeCount, ViewCount: post.ViewCount, LikeCount: post.LikeCount,
Liked: d.Post.IsLiked(ctx.UserID(), post.ID), Favorited: d.Post.IsFavorited(ctx.UserID(), post.ID), Liked: d.Post.IsLiked(ctx.UserID(), post.ID), Favorited: d.Post.IsFavorited(ctx.UserID(), post.ID),
BodyHTML: body, CommentCount: len(cv), Comments: cv, BodyHTML: body, CommentCount: commentTotal,
Comments: d.commentItemsForPage(ctx, post.ID, post.CommentsLocked, cv),
CommentsLocked: post.CommentsLocked, CommentsLocked: post.CommentsLocked,
CanEdit: d.Post.CanUserEdit(post, ctx.UserID(), ctx.IsAdmin()), CanEdit: d.Post.CanUserEdit(post, ctx.UserID(), ctx.IsAdmin()),
CanViewRevisions: canViewPostRevisions(post, ctx.UserID(), ctx.IsAdmin()), CanViewRevisions: canViewPostRevisions(post, ctx.UserID(), ctx.IsAdmin()),

View File

@@ -204,73 +204,7 @@
{{else}} {{else}}
<ul class="j13-comment-list"> <ul class="j13-comment-list">
{{range .Comments}} {{range .Comments}}
<li class="j13-comment" id="floor-{{.Floor}}" data-comment-id="{{.ID}}" data-floor="{{.Floor}}" data-author="{{.AuthorName}}"> {{template "post/comment_item" .}}
<div class="j13-comment__meta">
<span class="j13-comment__floor">#{{.Floor}}</span>
{{if .AuthorID}}<a href="/user/{{.AuthorID}}">{{.AuthorName}}</a>{{else}}<span>{{.AuthorName}}</span>{{end}}
<span>{{.CreatedLabel}}</span>
{{if .StatusLabel}}<span class="j13-tag j13-tag--{{.Status}}">{{.StatusLabel}}</span>{{end}}
{{if .IsPrivate}}<span class="j13-tag">私密</span>{{end}}
{{if .IsBountyAward}}<span class="j13-tag j13-tag--resolved">已采纳</span>{{end}}
{{if .ReplyToFloor}}
<span class="j13-comment__reply-to">回复 <a href="#floor-{{.ReplyToFloor}}">#{{.ReplyToFloor}}</a>{{if .ReplyToAuthor}} {{.ReplyToAuthor}}{{end}}</span>
{{end}}
</div>
{{if .ContentHidden}}
<p class="j13-muted">(私密评论不可见)</p>
{{else}}
<div class="j13-comment__body">{{safeHTML .Content}}</div>
{{end}}
{{if $.LoggedIn}}
<div class="j13-comment__actions">
{{if .CanAwardBounty}}
<form method="post" action="/post/{{$.PostID}}/bounty/award" class="j13-inline-form">
<input type="hidden" name="_csrf" value="{{$.CSRF}}"/>
<input type="hidden" name="comment_id" value="{{.ID}}"/>
<button type="submit">采纳并发放悬赏</button>
</form>
{{end}}
<form method="post" action="/post/{{$.PostID}}/comments/{{.ID}}/like" class="j13-inline-form">
<input type="hidden" name="_csrf" value="{{$.CSRF}}"/>
<button type="submit" class="j13-linkbtn">{{if .Liked}}取消赞{{else}}赞{{end}} ({{.LikeCount}})</button>
</form>
{{if not $.CommentsLocked}}
<button type="button" class="j13-linkbtn j13-comment__reply-btn" data-reply-to="{{.ID}}" data-reply-floor="{{.Floor}}" data-reply-author="{{.AuthorName}}">回复</button>
{{end}}
{{if .CanEdit}}
<a class="j13-linkbtn" href="/post/{{$.PostID}}/comments/{{.ID}}/edit">编辑</a>
{{end}}
{{if .CanDelete}}
<form method="post" action="/post/{{$.PostID}}/comments/{{.ID}}/delete" class="j13-inline-form" onsubmit="return confirm('确定删除该评论?回复也会一并移入回收站。');">
<input type="hidden" name="_csrf" value="{{$.CSRF}}"/>
<button type="submit" class="j13-linkbtn">删除</button>
</form>
{{end}}
{{if .CanReport}}
<details class="j13-report">
<summary class="j13-linkbtn">举报</summary>
<form method="post" action="/post/{{$.PostID}}/comments/{{.ID}}/report" class="j13-report__form">
<input type="hidden" name="_csrf" value="{{$.CSRF}}"/>
<label>原因
<select name="reason" required>
<option value="">请选择</option>
<option value="spam">垃圾广告</option>
<option value="abuse">人身攻击 / 辱骂</option>
<option value="illegal">违法违规</option>
<option value="irrelevant">内容无关 / 灌水</option>
<option value="other">其他</option>
</select>
</label>
<label>补充说明(可选)
<input name="detail" maxlength="500" placeholder="可选"/>
</label>
<button type="submit" class="j13-btn j13-btn--sm">提交举报</button>
</form>
</details>
{{end}}
</div>
{{end}}
</li>
{{end}} {{end}}
</ul> </ul>
{{end}} {{end}}

View File

@@ -0,0 +1,74 @@
{{define "post/comment_item"}}
<li class="j13-comment j13-comment--depth-{{.Depth}}" id="floor-{{.Floor}}" data-comment-id="{{.ID}}" data-floor="{{.Floor}}" data-author="{{.AuthorName}}" data-depth="{{.Depth}}">
<div class="j13-comment__meta">
<span class="j13-comment__floor">#{{.Floor}}</span>
{{if .AuthorID}}<a href="/user/{{.AuthorID}}{{.PermalinkSuffix}}">{{.AuthorName}}</a>{{else}}<span>{{.AuthorName}}</span>{{end}}
<span>{{.CreatedLabel}}</span>
{{if .StatusLabel}}<span class="j13-tag j13-tag--{{.Status}}">{{.StatusLabel}}</span>{{end}}
{{if .IsPrivate}}<span class="j13-tag">私密</span>{{end}}
{{if .IsBountyAward}}<span class="j13-tag j13-tag--resolved">已采纳</span>{{end}}
{{if .ReplyToFloor}}
<span class="j13-comment__reply-to">回复 <a href="#floor-{{.ReplyToFloor}}">#{{.ReplyToFloor}}</a>{{if .ReplyToAuthor}} {{.ReplyToAuthor}}{{end}}</span>
{{end}}
</div>
{{if .ContentHidden}}
<p class="j13-muted">(私密评论不可见)</p>
{{else}}
<div class="j13-comment__body">{{safeHTML .Content}}</div>
{{end}}
{{if .LoggedIn}}
<div class="j13-comment__actions">
{{if .CanAwardBounty}}
<form method="post" action="/post/{{.PostID}}/bounty/award" class="j13-inline-form">
<input type="hidden" name="_csrf" value="{{.CSRF}}"/>
<input type="hidden" name="comment_id" value="{{.ID}}"/>
<button type="submit">采纳并发放悬赏</button>
</form>
{{end}}
<form method="post" action="/post/{{.PostID}}/comments/{{.ID}}/like" class="j13-inline-form">
<input type="hidden" name="_csrf" value="{{.CSRF}}"/>
<button type="submit" class="j13-linkbtn">{{if .Liked}}取消赞{{else}}赞{{end}} ({{.LikeCount}})</button>
</form>
{{if not .CommentsLocked}}
<button type="button" class="j13-linkbtn j13-comment__reply-btn" data-reply-to="{{.ID}}" data-reply-floor="{{.Floor}}" data-reply-author="{{.AuthorName}}">回复</button>
{{end}}
{{if .CanEdit}}
<a class="j13-linkbtn" href="/post/{{.PostID}}/comments/{{.ID}}/edit">编辑</a>
{{end}}
{{if .CanDelete}}
<form method="post" action="/post/{{.PostID}}/comments/{{.ID}}/delete" class="j13-inline-form" onsubmit="return confirm('确定删除该评论?回复也会一并移入回收站。');">
<input type="hidden" name="_csrf" value="{{.CSRF}}"/>
<button type="submit" class="j13-linkbtn">删除</button>
</form>
{{end}}
{{if .CanReport}}
<details class="j13-report">
<summary class="j13-linkbtn">举报</summary>
<form method="post" action="/post/{{.PostID}}/comments/{{.ID}}/report" class="j13-report__form">
<input type="hidden" name="_csrf" value="{{.CSRF}}"/>
<label>原因
<select name="reason" required>
<option value="">请选择</option>
<option value="spam">垃圾广告</option>
<option value="abuse">人身攻击 / 辱骂</option>
<option value="illegal">违法违规</option>
<option value="irrelevant">内容无关 / 灌水</option>
<option value="other">其他</option>
</select>
</label>
<label>补充说明(可选)
<input name="detail" maxlength="500" placeholder="可选"/>
</label>
<button type="submit" class="j13-btn j13-btn--sm">提交举报</button>
</form>
</details>
{{end}}
</div>
{{end}}
{{if .Children}}
<ul class="j13-comment-list j13-comment-list--nested">
{{range .Children}}{{template "post/comment_item" ($.Wrap .)}}{{end}}
</ul>
{{end}}
</li>
{{end}}

View File

@@ -412,10 +412,28 @@ body.j13-body {
} }
.j13-comments { margin-top: 2rem; } .j13-comments { margin-top: 2rem; }
.j13-comment-list { list-style: none; margin: 0; padding: 0; } .j13-comment-list { list-style: none; margin: 0; padding: 0; }
.j13-comment-list--nested {
margin: 0.5rem 0 0;
padding: 0 0 0 0.85rem;
border-left: 2px solid var(--j13-border);
}
.j13-comment { .j13-comment {
padding: 0.85rem 0; padding: 0.85rem 0;
border-bottom: 1px solid var(--j13-border); border-bottom: 1px solid var(--j13-border);
} }
.j13-comment-list--nested > .j13-comment {
border-bottom-color: transparent;
padding-left: 0.65rem;
}
.j13-comment--depth-0 { }
.j13-comment--depth-1,
.j13-comment--depth-2,
.j13-comment--depth-3,
.j13-comment--depth-4,
.j13-comment--depth-5,
.j13-comment--depth-6 {
background: transparent;
}
.j13-comment__meta { .j13-comment__meta {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
@@ -425,16 +443,26 @@ body.j13-body {
margin-bottom: 0.35rem; margin-bottom: 0.35rem;
align-items: center; align-items: center;
} }
.j13-comment__floor { font-weight: 600; color: var(--j13-fg, inherit); }
.j13-comment__reply-to a { color: var(--j13-accent); text-decoration: none; } .j13-comment__reply-to a { color: var(--j13-accent); text-decoration: none; }
.j13-comment__actions { .j13-comment__actions {
display: flex; display: flex;
gap: 0.75rem; gap: 0.75rem;
align-items: center; align-items: center;
margin-top: 0.4rem; margin-top: 0.4rem;
flex-wrap: wrap;
} }
.j13-comment-reply-hint { margin: 0 0 0.5rem; } .j13-comment-reply-hint { margin: 0 0 0.5rem; }
.j13-comment-form textarea { width: 100%; } .j13-comment-form textarea { width: 100%; }
.j13-comment-form .j13-form__row { margin-top: 0.5rem; } .j13-comment-form .j13-form__row { margin-top: 0.5rem; }
@media (max-width: 600px) {
.j13-comment-list--nested {
padding-left: 0.55rem;
}
.j13-comment-list--nested > .j13-comment {
padding-left: 0.4rem;
}
}
.j13-muted { color: var(--j13-muted); } .j13-muted { color: var(--j13-muted); }
.j13-install fieldset { .j13-install fieldset {
border: 1px solid var(--j13-border); border: 1px solid var(--j13-border);