Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add attention blocks within quote blocks for Note and Warning #21711

Merged
merged 11 commits into from
Nov 9, 2022
34 changes: 34 additions & 0 deletions modules/markup/markdown/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,3 +180,37 @@ func IsColorPreview(node ast.Node) bool {
_, ok := node.(*ColorPreview)
return ok
}

const (
AttentionNote string = "Note"
AttentionWarning string = "Warning"
)

// Attention is an inline for a color preview
type Attention struct {
ast.BaseInline
AttentionType string
}

// Dump implements Node.Dump.
func (n *Attention) Dump(source []byte, level int) {
m := map[string]string{}
m["AttentionType"] = n.AttentionType
ast.DumpHelper(n, source, level, m, nil)
}

// KindAttention is the NodeKind for Attention
var KindAttention = ast.NewNodeKind("Attention")

// Kind implements Node.Kind.
func (n *Attention) Kind() ast.NodeKind {
return KindAttention
}

// NewAttention returns a new Attention node.
func NewAttention(attentionType string) *Attention {
return &Attention{
BaseInline: ast.BaseInline{},
AttentionType: attentionType,
}
}
37 changes: 37 additions & 0 deletions modules/markup/markdown/goldmark.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"code.gitea.io/gitea/modules/markup"
"code.gitea.io/gitea/modules/markup/common"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/svg"
giteautil "code.gitea.io/gitea/modules/util"

"github.com/microcosm-cc/bluemonday/css"
Expand Down Expand Up @@ -46,6 +47,7 @@ func (g *ASTTransformer) Transform(node *ast.Document, reader text.Reader, pc pa
ctx.TableOfContents = make([]markup.Header, 0, 100)
}

attentionMarkedBlockquotes := make(container.Set[*ast.Blockquote])
_ = ast.Walk(node, func(n ast.Node, entering bool) (ast.WalkStatus, error) {
if !entering {
return ast.WalkContinue, nil
Expand Down Expand Up @@ -184,6 +186,18 @@ func (g *ASTTransformer) Transform(node *ast.Document, reader text.Reader, pc pa
if css.ColorHandler(strings.ToLower(string(colorContent))) {
v.AppendChild(v, NewColorPreview(colorContent))
}
case *ast.Emphasis:
// check if inside blockquote for attention, expected hierarchy is
// Emphasis < Paragraph < Blockquote
blockquote, isInBlockquote := n.Parent().Parent().(*ast.Blockquote)
if isInBlockquote && !attentionMarkedBlockquotes.Contains(blockquote) {
fullText := string(n.Text(reader.Source()))
if fullText == AttentionNote || fullText == AttentionWarning {
v.SetAttributeString("class", []byte("attention-"+strings.ToLower(fullText)))
v.Parent().InsertBefore(v.Parent(), v, NewAttention(fullText))
attentionMarkedBlockquotes.Add(blockquote)
yardenshoham marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
return ast.WalkContinue, nil
})
Expand Down Expand Up @@ -273,6 +287,7 @@ func (r *HTMLRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) {
reg.Register(KindSummary, r.renderSummary)
reg.Register(KindIcon, r.renderIcon)
reg.Register(ast.KindCodeSpan, r.renderCodeSpan)
reg.Register(KindAttention, r.renderAttention)
reg.Register(KindTaskCheckBoxListItem, r.renderTaskCheckBoxListItem)
reg.Register(east.KindTaskCheckBox, r.renderTaskCheckBox)
}
Expand Down Expand Up @@ -309,6 +324,28 @@ func (r *HTMLRenderer) renderCodeSpan(w util.BufWriter, source []byte, n ast.Nod
return ast.WalkContinue, nil
}

// renderAttention renders a quote marked with i.e. "> **Note**" or "> **Warning**" with a corresponding svg
func (r *HTMLRenderer) renderAttention(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
yardenshoham marked this conversation as resolved.
Show resolved Hide resolved
if entering {
_, _ = w.WriteString(`<span class="attention-icon attention-`)
n := node.(*Attention)
_, _ = w.WriteString(strings.ToLower(n.AttentionType))
_, _ = w.WriteString(`">`)

var octiconType string
switch n.AttentionType {
case AttentionNote:
octiconType = "info"
case AttentionWarning:
octiconType = "alert"
}
_, _ = w.WriteString(string(svg.RenderHTML("octicon-" + octiconType)))
} else {
_, _ = w.WriteString("</span>\n")
}
return ast.WalkContinue, nil
}

func (r *HTMLRenderer) renderDocument(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
n := node.(*ast.Document)

Expand Down
7 changes: 7 additions & 0 deletions modules/markup/sanitizer.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,13 @@ func createDefaultPolicy() *bluemonday.Policy {
// For color preview
policy.AllowAttrs("class").Matching(regexp.MustCompile(`^color-preview$`)).OnElements("span")

// For attention
policy.AllowAttrs("class").Matching(regexp.MustCompile(`^attention-\w+$`)).OnElements("strong")
policy.AllowAttrs("class").Matching(regexp.MustCompile(`^attention-icon attention-\w+$`)).OnElements("span", "strong")
policy.AllowAttrs("class").Matching(regexp.MustCompile(`^svg octicon-\w+$`)).OnElements("svg")
policy.AllowAttrs("viewBox", "width", "height", "aria-hidden").OnElements("svg")
policy.AllowAttrs("fill-rule", "d").OnElements("path")
yardenshoham marked this conversation as resolved.
Show resolved Hide resolved

// For Chroma markdown plugin
policy.AllowAttrs("class").Matching(regexp.MustCompile(`^(chroma )?language-[\w-]+( display)?( is-loading)?$`)).OnElements("code")

Expand Down
14 changes: 14 additions & 0 deletions web_src/less/_base.less
Original file line number Diff line number Diff line change
Expand Up @@ -1732,6 +1732,20 @@ a.ui.card:hover,
border-radius: .15em;
}

.attention-icon {
vertical-align: middle;
yardenshoham marked this conversation as resolved.
Show resolved Hide resolved
}

.attention-note {
font-weight: unset;
color: var(--color-info-text);
}

.attention-warning {
font-weight: unset;
color: var(--color-warning-text);
}

footer {
background-color: var(--color-footer);
border-top: 1px solid var(--color-secondary);
Expand Down