forked from gitea/gitea
		
	Add attention blocks within quote blocks for Note and Warning (#21711)
				
					
				
			For each quote block, the first `**Note**` or `**Warning**` gets an icon prepended to it and its text is colored accordingly. GitHub does this (community/community#16925). [Initially requested on Discord.](https://discord.com/channels/322538954119184384/322538954119184384/1038816475638661181) ### Before  ### After  Signed-off-by: Yarden Shoham <hrsi88@gmail.com> Co-authored-by: delvh <dev.lh@web.de> Co-authored-by: silverwind <me@silverwind.io>
This commit is contained in:
		
							parent
							
								
									2ebab42934
								
							
						
					
					
						commit
						cb83288530
					
				| @ -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, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @ -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" | ||||
| @ -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 | ||||
| @ -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) | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		return ast.WalkContinue, nil | ||||
| 	}) | ||||
| @ -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) | ||||
| } | ||||
| @ -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) { | ||||
| 	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) | ||||
| 
 | ||||
|  | ||||
| @ -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") | ||||
| 
 | ||||
| 	// For Chroma markdown plugin | ||||
| 	policy.AllowAttrs("class").Matching(regexp.MustCompile(`^(chroma )?language-[\w-]+( display)?( is-loading)?$`)).OnElements("code") | ||||
| 
 | ||||
|  | ||||
| @ -1732,6 +1732,20 @@ a.ui.card:hover, | ||||
|   border-radius: .15em; | ||||
| } | ||||
| 
 | ||||
| .attention-icon { | ||||
|   vertical-align: text-top; | ||||
| } | ||||
| 
 | ||||
| .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); | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Yarden Shoham
						Yarden Shoham