mirror of
https://github.com/go-gitea/gitea.git
synced 2026-10-30 20:24:01 +00:00
enhance(markup): align markdown rendering details with GitHub (#39955)
Fixes markdown rendering details where Gitea deviated from GitHub unintentionally, and adds a `/devtest/markdown` page to review markdown rendering. - Paragraphs get the intended 1.5 line height - Blockquotes, alerts and definition lists no longer inherit browser default margins - Loose task lists keep their spacing, the checkbox renders inside the paragraph like on GitHub - Footnote references render as `[1]` so adjacent ones stay apart without CSS - Code in headings, headings in summaries, nested ordered lists, tables, `<mark>`, alert icons and `<hr>` follow GitHub Co-authored-by: bircni <bircni@icloud.com> Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
13 files changed
+401
-169
No files matched your search
@@ -401,8 +401,10 @@ func (r *FootnoteHTMLRenderer) renderFootnoteLink(w util.BufWriter, source []byt
|
||||
_, _ = w.WriteString(`"><a href="#fn:user-content-`)
|
||||
_, _ = w.Write(n.Name)
|
||||
_, _ = w.WriteString(`" class="footnote-ref" role="doc-noteref">`) // FIXME: here and below, need to keep the classes
|
||||
// literal brackets separate adjacent refs, also where no CSS applies like in mails
|
||||
_, _ = w.WriteString("[")
|
||||
_, _ = w.WriteString(is)
|
||||
_, _ = w.WriteString(` </a></sup>`) // the style doesn't work at the moment, so add a space to separate the names
|
||||
_, _ = w.WriteString("]</a></sup>")
|
||||
}
|
||||
return ast.WalkContinue, nil
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ func TestRender_IssueList(t *testing.T) {
|
||||
</ul>`},
|
||||
{
|
||||
"IssueFootnote", "foo[^1][^2]\n\n[^1]: bar\n[^2]: baz",
|
||||
`<p>foo<sup id="fnref:user-content-1-12345"><a href="#fn:user-content-1-12345" rel="nofollow">1 </a></sup><sup id="fnref:user-content-2-12345"><a href="#fn:user-content-2-12345" rel="nofollow">2 </a></sup></p>
|
||||
`<p>foo<sup id="fnref:user-content-1-12345"><a href="#fn:user-content-1-12345" rel="nofollow">[1]</a></sup><sup id="fnref:user-content-2-12345"><a href="#fn:user-content-2-12345" rel="nofollow">[2]</a></sup></p>
|
||||
<div>
|
||||
<hr/>
|
||||
<ol>
|
||||
|
||||
@@ -5,7 +5,6 @@ package markdown
|
||||
|
||||
import (
|
||||
"html/template"
|
||||
"strconv"
|
||||
|
||||
"github.com/yuin/goldmark/ast"
|
||||
)
|
||||
@@ -56,36 +55,6 @@ func NewSummary() *Summary {
|
||||
return &Summary{}
|
||||
}
|
||||
|
||||
// TaskCheckBoxListItem is a block that represents a list item of a markdown block with a checkbox
|
||||
type TaskCheckBoxListItem struct {
|
||||
*ast.ListItem
|
||||
IsChecked bool
|
||||
SourcePosition int
|
||||
}
|
||||
|
||||
// KindTaskCheckBoxListItem is the NodeKind for TaskCheckBoxListItem
|
||||
var KindTaskCheckBoxListItem = ast.NewNodeKind("TaskCheckBoxListItem")
|
||||
|
||||
// Dump implements Node.Dump .
|
||||
func (n *TaskCheckBoxListItem) Dump(source []byte, level int) {
|
||||
m := map[string]string{}
|
||||
m["IsChecked"] = strconv.FormatBool(n.IsChecked)
|
||||
m["SourcePosition"] = strconv.FormatInt(int64(n.SourcePosition), 10)
|
||||
ast.DumpHelper(n, source, level, m, nil)
|
||||
}
|
||||
|
||||
// Kind implements Node.Kind.
|
||||
func (n *TaskCheckBoxListItem) Kind() ast.NodeKind {
|
||||
return KindTaskCheckBoxListItem
|
||||
}
|
||||
|
||||
// NewTaskCheckBoxListItem returns a new TaskCheckBoxListItem node.
|
||||
func NewTaskCheckBoxListItem(listItem *ast.ListItem) *TaskCheckBoxListItem {
|
||||
return &TaskCheckBoxListItem{
|
||||
ListItem: listItem,
|
||||
}
|
||||
}
|
||||
|
||||
// Icon is an inline for a Fomantic UI icon
|
||||
type Icon struct {
|
||||
ast.BaseInline
|
||||
|
||||
@@ -146,7 +146,6 @@ func (r *HTMLRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) {
|
||||
reg.Register(ast.KindCodeSpan, r.renderCodeSpan)
|
||||
reg.Register(ast.KindCodeBlock, r.renderCodeBlock)
|
||||
reg.Register(KindAttention, r.renderAttention)
|
||||
reg.Register(KindTaskCheckBoxListItem, r.renderTaskCheckBoxListItem)
|
||||
reg.Register(east.KindTaskCheckBox, r.renderTaskCheckBox)
|
||||
reg.Register(KindRawHTML, r.renderRawHTML)
|
||||
}
|
||||
|
||||
@@ -125,6 +125,10 @@ Here are some links to the most important topics. You can find the full list of
|
||||
- [x] checked
|
||||
- [ ] still unchecked
|
||||
|
||||
* [x] loose
|
||||
|
||||
second paragraph
|
||||
|
||||
### Definition list
|
||||
|
||||
First Term
|
||||
@@ -209,6 +213,12 @@ This PR has been generated by [Renovate Bot](https://github.com/renovatebot/reno
|
||||
<li class="task-list-item"><input type="checkbox" disabled="" data-source-position="450" checked=""/>checked</li>
|
||||
<li class="task-list-item"><input type="checkbox" disabled="" data-source-position="464"/>still unchecked</li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li class="task-list-item">
|
||||
<p><input type="checkbox" disabled="" data-source-position="487" checked=""/>loose</p>
|
||||
<p>second paragraph</p>
|
||||
</li>
|
||||
</ul>
|
||||
<h3 id="user-content-definition-list">Definition list</h3>
|
||||
<dl>
|
||||
<dt>First Term</dt>
|
||||
@@ -218,7 +228,7 @@ This PR has been generated by [Renovate Bot](https://github.com/renovatebot/reno
|
||||
<dd>This is another definition of the second term.</dd>
|
||||
</dl>
|
||||
<h3 id="user-content-footnotes">Footnotes</h3>
|
||||
<p>Here is a simple footnote,<sup id="fnref:user-content-1"><a href="#fn:user-content-1" rel="nofollow">1 </a></sup> and here is a longer one.<sup id="fnref:user-content-bignote"><a href="#fn:user-content-bignote" rel="nofollow">2 </a></sup></p>
|
||||
<p>Here is a simple footnote,<sup id="fnref:user-content-1"><a href="#fn:user-content-1" rel="nofollow">[1]</a></sup> and here is a longer one.<sup id="fnref:user-content-bignote"><a href="#fn:user-content-bignote" rel="nofollow">[2]</a></sup></p>
|
||||
<div>
|
||||
<hr/>
|
||||
<ol>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
package markdown
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"gitea.dev/modules/markup"
|
||||
|
||||
@@ -14,71 +14,37 @@ import (
|
||||
"github.com/yuin/goldmark/util"
|
||||
)
|
||||
|
||||
func (r *HTMLRenderer) renderTaskCheckBoxListItem(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
|
||||
n := node.(*TaskCheckBoxListItem) //nolint:forcetypeassert // registered for KindTaskCheckBoxListItem only
|
||||
if entering {
|
||||
if n.Attributes() != nil {
|
||||
_, _ = w.WriteString("<li")
|
||||
html.RenderAttributes(w, n, html.ListItemAttributeFilter)
|
||||
_ = w.WriteByte('>')
|
||||
} else {
|
||||
_, _ = w.WriteString("<li>")
|
||||
}
|
||||
fmt.Fprintf(w, `<input type="checkbox" disabled="" data-source-position="%d"`, n.SourcePosition)
|
||||
if n.IsChecked {
|
||||
_, _ = w.WriteString(` checked=""`)
|
||||
}
|
||||
if r.XHTML {
|
||||
_, _ = w.WriteString(` />`)
|
||||
} else {
|
||||
_ = w.WriteByte('>')
|
||||
}
|
||||
fc := n.FirstChild()
|
||||
if fc != nil {
|
||||
if _, ok := fc.(*ast.TextBlock); !ok {
|
||||
_ = w.WriteByte('\n')
|
||||
}
|
||||
}
|
||||
func (r *HTMLRenderer) renderTaskCheckBox(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
|
||||
checkBox, ok := node.(*east.TaskCheckBox)
|
||||
if !entering || !ok {
|
||||
return ast.WalkContinue, nil
|
||||
}
|
||||
_, _ = w.WriteString(`<input type="checkbox" disabled=""`)
|
||||
html.RenderAttributes(w, checkBox, nil)
|
||||
if checkBox.IsChecked {
|
||||
_, _ = w.WriteString(` checked=""`)
|
||||
}
|
||||
if r.XHTML {
|
||||
_, _ = w.WriteString(` />`)
|
||||
} else {
|
||||
_, _ = w.WriteString("</li>\n")
|
||||
_ = w.WriteByte('>')
|
||||
}
|
||||
return ast.WalkContinue, nil
|
||||
}
|
||||
|
||||
func (r *HTMLRenderer) renderTaskCheckBox(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
|
||||
return ast.WalkContinue, nil
|
||||
}
|
||||
|
||||
func (g *ASTTransformer) transformList(_ *markup.RenderContext, v *ast.List, rc *RenderConfig) {
|
||||
if v.HasChildren() {
|
||||
children := make([]ast.Node, 0, v.ChildCount())
|
||||
child := v.FirstChild()
|
||||
for child != nil {
|
||||
children = append(children, child)
|
||||
child = child.NextSibling()
|
||||
for listItem := v.FirstChild(); listItem != nil; listItem = listItem.NextSibling() {
|
||||
block := listItem.FirstChild()
|
||||
if block == nil {
|
||||
continue
|
||||
}
|
||||
v.RemoveChildren(v)
|
||||
|
||||
for _, child := range children {
|
||||
listItem := child.(*ast.ListItem) //nolint:forcetypeassert // a list only holds list items
|
||||
if !child.HasChildren() || !child.FirstChild().HasChildren() {
|
||||
v.AppendChild(v, child)
|
||||
continue
|
||||
}
|
||||
taskCheckBox, ok := child.FirstChild().FirstChild().(*east.TaskCheckBox)
|
||||
if !ok {
|
||||
v.AppendChild(v, child)
|
||||
continue
|
||||
}
|
||||
newChild := NewTaskCheckBoxListItem(listItem)
|
||||
newChild.IsChecked = taskCheckBox.IsChecked
|
||||
newChild.SetAttributeString(g.renderInternal.SafeAttr("class"), []byte(g.renderInternal.SafeValue("task-list-item")))
|
||||
segments := newChild.FirstChild().Lines()
|
||||
if segments.Len() > 0 {
|
||||
segment := segments.At(0)
|
||||
newChild.SourcePosition = rc.metaLength + segment.Start
|
||||
}
|
||||
v.AppendChild(v, newChild)
|
||||
checkBox, ok := block.FirstChild().(*east.TaskCheckBox)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
listItem.SetAttributeString(g.renderInternal.SafeAttr("class"), []byte(g.renderInternal.SafeValue("task-list-item")))
|
||||
if lines := block.Lines(); lines.Len() > 0 {
|
||||
checkBox.SetAttributeString("data-source-position", []byte(strconv.Itoa(rc.metaLength+lines.At(0).Start)))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,8 @@ import (
|
||||
"gitea.dev/modules/charset"
|
||||
"gitea.dev/modules/git"
|
||||
"gitea.dev/modules/indexer/code"
|
||||
"gitea.dev/modules/markup"
|
||||
"gitea.dev/modules/markup/markdown"
|
||||
"gitea.dev/modules/templates"
|
||||
"gitea.dev/modules/util"
|
||||
"gitea.dev/services/context"
|
||||
@@ -35,8 +37,8 @@ func List(ctx *context.Context) {
|
||||
}
|
||||
var subNames []string
|
||||
for _, tmplName := range templateNames {
|
||||
subName := strings.TrimSuffix(tmplName, ".tmpl")
|
||||
if !strings.HasPrefix(subName, "devtest-") {
|
||||
subName, isTmpl := strings.CutSuffix(tmplName, ".tmpl")
|
||||
if isTmpl && !strings.HasPrefix(subName, "devtest-") {
|
||||
subNames = append(subNames, subName)
|
||||
}
|
||||
}
|
||||
@@ -213,6 +215,19 @@ func prepareMockDataAvatarStack(ctx *context.Context) {
|
||||
*/
|
||||
}
|
||||
|
||||
func prepareMockDataMarkdown(ctx *context.Context) {
|
||||
content, err := templates.AssetFS().ReadFile("devtest/markdown.md")
|
||||
if err != nil {
|
||||
ctx.ServerError("ReadFile", err)
|
||||
return
|
||||
}
|
||||
rctx := markup.NewRenderContext(ctx).WithMetas(markup.ComposeSimpleDocumentMetas()).WithEnableHeadingIDGeneration(true)
|
||||
ctx.Data["MarkdownHTML"], err = markdown.RenderString(rctx, string(content))
|
||||
if err != nil {
|
||||
ctx.ServerError("RenderString", err)
|
||||
}
|
||||
}
|
||||
|
||||
func prepareMockDataRelativeTime(ctx *context.Context) {
|
||||
now := time.Now()
|
||||
ctx.Data["TimeNow"] = now
|
||||
@@ -244,6 +259,8 @@ func prepareMockData(ctx *context.Context) {
|
||||
prepareMockDataBadgeCommitSign(ctx)
|
||||
case "/devtest/badge-actions-svg":
|
||||
prepareMockDataBadgeActionsSvg(ctx)
|
||||
case "/devtest/markdown":
|
||||
prepareMockDataMarkdown(ctx)
|
||||
case "/devtest/relative-time":
|
||||
prepareMockDataRelativeTime(ctx)
|
||||
case "/devtest/toast-and-message":
|
||||
|
||||
@@ -0,0 +1,237 @@
|
||||
# Markdown
|
||||
|
||||
## Text
|
||||
|
||||
### Inline elements
|
||||
|
||||
Text with **bold**, *italic*, ~~strikethrough~~, `inline code`, [a link](https://gitea.com), an autolink https://gitea.com and <kbd>Ctrl</kbd>+<kbd>C</kbd>.
|
||||
|
||||
H<sub>2</sub>O, x<sup>2</sup>, <mark>marked</mark>, <ins>inserted</ins>, <abbr title="HyperText Markup Language">HTML</abbr>, <samp>sample output</samp>, <var>variable</var>, <small>small</small>, <q>quote</q> and emoji :+1: :tada:.
|
||||
|
||||
### Paragraphs
|
||||
|
||||
A paragraph with a soft line break
|
||||
continues on the same line, while a hard break\
|
||||
starts a new line.
|
||||
|
||||
A second paragraph that is long enough to wrap onto multiple lines, so that the line height of paragraphs can be compared with the line height of list items further below.
|
||||
|
||||
### Headings
|
||||
|
||||
# Heading 1 with `code`
|
||||
## Heading 2 with `code`
|
||||
### Heading 3 with `code`
|
||||
#### Heading 4 with `code`
|
||||
##### Heading 5 with `code`
|
||||
###### Heading 6 with `code`
|
||||
|
||||
### Horizontal rule
|
||||
|
||||
Text above the rule.
|
||||
|
||||
---
|
||||
|
||||
Text below the rule.
|
||||
|
||||
## Quotes
|
||||
|
||||
### Blockquote
|
||||
|
||||
> A blockquote with a paragraph.
|
||||
>
|
||||
> > A nested blockquote.
|
||||
>
|
||||
> - A list in a blockquote
|
||||
> - Another item
|
||||
>
|
||||
> ```
|
||||
> code in a blockquote
|
||||
> ```
|
||||
|
||||
### Alerts
|
||||
|
||||
> [!NOTE]
|
||||
> Useful information that users should know, even when skimming content.
|
||||
|
||||
> [!TIP]
|
||||
> Helpful advice for doing things better or more easily.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Key information users need to know to achieve their goal.
|
||||
|
||||
> [!WARNING]
|
||||
> Urgent info that needs immediate user attention to avoid problems.
|
||||
|
||||
> [!CAUTION]
|
||||
> Advises about risks or negative outcomes of certain actions.
|
||||
>
|
||||
> A second paragraph in an alert.
|
||||
|
||||
## Lists
|
||||
|
||||
### Tight list
|
||||
|
||||
- Item one
|
||||
- Item two
|
||||
- Nested item
|
||||
- Deeper nested item
|
||||
- Item three
|
||||
|
||||
### Loose list
|
||||
|
||||
- Item one
|
||||
|
||||
- Item two with a second paragraph
|
||||
|
||||
The second paragraph.
|
||||
|
||||
- Item three
|
||||
|
||||
### Ordered list
|
||||
|
||||
1. Item one
|
||||
2. Item two
|
||||
1. Nested item
|
||||
1. Deeper nested item
|
||||
3. Item three
|
||||
|
||||
### Ordered list with type
|
||||
|
||||
<ol>
|
||||
<li>Item one
|
||||
<ol type="a">
|
||||
<li>Nested item with <code>type="a"</code></li>
|
||||
<li>Another nested item</li>
|
||||
</ol>
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
### Ordered list with start
|
||||
|
||||
5. Item five
|
||||
6. Item six
|
||||
|
||||
### Code block in list
|
||||
|
||||
1. A tight item with a code block:
|
||||
```js
|
||||
const a = 1;
|
||||
```
|
||||
2. Item after the code block
|
||||
|
||||
### Task list
|
||||
|
||||
- [ ] Unchecked task
|
||||
- [x] Checked task
|
||||
- [ ] Nested task
|
||||
- Normal item
|
||||
|
||||
1. [ ] Ordered task
|
||||
2. [x] Ordered checked task
|
||||
|
||||
### Loose task list
|
||||
|
||||
- [ ] Loose task
|
||||
|
||||
- [x] Loose checked task
|
||||
|
||||
A second paragraph in a loose task.
|
||||
|
||||
### Right-to-left task list
|
||||
|
||||
- [ ] משימה
|
||||
|
||||
- [x] משימה שהושלמה
|
||||
|
||||
## Code
|
||||
|
||||
### Code blocks
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
func main() {
|
||||
println("hello")
|
||||
}
|
||||
```
|
||||
|
||||
```
|
||||
A plain code block with a long line that needs to scroll horizontally: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
|
||||
```
|
||||
|
||||
An indented code block
|
||||
|
||||
### Math
|
||||
|
||||
Inline math $E = mc^2$ in a sentence.
|
||||
|
||||
$$
|
||||
\sum_{i=1}^n x_i
|
||||
$$
|
||||
|
||||
## Tables
|
||||
|
||||
### Table
|
||||
|
||||
| Left | Center | Right |
|
||||
| :--- | :---: | ---: |
|
||||
| `code` | 1 | 1.00 |
|
||||
| text | 22 | 22.00 |
|
||||
| longer cell content | 333 | 333.00 |
|
||||
| <img src="/assets/img/logo.svg" alt="logo" width="24"> image | 4444 | 4444.00 |
|
||||
|
||||
### Table with paragraphs
|
||||
|
||||
<table>
|
||||
<tr><th>Header</th><th>Header</th></tr>
|
||||
<tr><td><p>Paragraph in a cell</p></td><td>Text in a cell</td></tr>
|
||||
</table>
|
||||
|
||||
## Other
|
||||
|
||||
### Details
|
||||
|
||||
<details>
|
||||
<summary>Closed details</summary>
|
||||
|
||||
Hidden content.
|
||||
|
||||
</details>
|
||||
|
||||
<details open>
|
||||
<summary>Open details</summary>
|
||||
|
||||
Visible content.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><h3>Heading in summary</h3></summary>
|
||||
|
||||
Hidden content.
|
||||
|
||||
</details>
|
||||
|
||||
### Definition list
|
||||
|
||||
<dl>
|
||||
<dt>Term</dt>
|
||||
<dd>Definition</dd>
|
||||
<dt>First term</dt>
|
||||
<dt>Second term</dt>
|
||||
<dd>Shared definition</dd>
|
||||
</dl>
|
||||
|
||||
### Images
|
||||
|
||||
An inline image  in text.
|
||||
|
||||
<p align="center"><img src="/assets/img/logo.svg" alt="centered logo" width="64"></p>
|
||||
|
||||
### Footnotes
|
||||
|
||||
A sentence with a footnote[^1] and another one[^2][^3].
|
||||
|
||||
[^1]: The first footnote.
|
||||
[^2]: The second footnote with **bold** text.
|
||||
[^3]: The third footnote.
|
||||
@@ -0,0 +1,9 @@
|
||||
{{template "devtest/devtest-header"}}
|
||||
<div class="page-content devtest ui container">
|
||||
<div class="ui segment tw-p-0">
|
||||
<div class="file-view markup markdown">
|
||||
{{.MarkdownHTML}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{template "devtest/devtest-footer"}}
|
||||
+5
-47
@@ -108,6 +108,11 @@ samp,
|
||||
font-size: 0.95em; /* compensate for monospace fonts being usually slightly larger */
|
||||
}
|
||||
|
||||
mark {
|
||||
background: var(--color-highlight-bg);
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: gitea-math-book; /* the Book weight shares its family name with the thinner Regular */
|
||||
src: local("NewComputerModernMath-Book"), local("NewCMMath-Book");
|
||||
@@ -456,53 +461,6 @@ a.label,
|
||||
color: var(--color-text) !important;
|
||||
}
|
||||
|
||||
.color-preview {
|
||||
display: inline-block;
|
||||
margin-left: 0.4em;
|
||||
height: 0.67em;
|
||||
width: 0.67em;
|
||||
border-radius: var(--border-radius);
|
||||
}
|
||||
|
||||
.attention-icon {
|
||||
margin: 2px 6px 0 0;
|
||||
}
|
||||
|
||||
blockquote.attention-note {
|
||||
border-left-color: var(--color-info-text);
|
||||
}
|
||||
strong.attention-note, svg.attention-note {
|
||||
color: var(--color-info-text);
|
||||
}
|
||||
|
||||
blockquote.attention-tip {
|
||||
border-left-color: var(--color-success-text);
|
||||
}
|
||||
strong.attention-tip, svg.attention-tip {
|
||||
color: var(--color-success-text);
|
||||
}
|
||||
|
||||
blockquote.attention-important {
|
||||
border-left-color: var(--color-priority-text);
|
||||
}
|
||||
strong.attention-important, svg.attention-important {
|
||||
color: var(--color-priority-text);
|
||||
}
|
||||
|
||||
blockquote.attention-warning {
|
||||
border-left-color: var(--color-warning-text);
|
||||
}
|
||||
strong.attention-warning, svg.attention-warning {
|
||||
color: var(--color-warning-text);
|
||||
}
|
||||
|
||||
blockquote.attention-caution {
|
||||
border-left-color: var(--color-error-text);
|
||||
}
|
||||
strong.attention-caution, svg.attention-caution {
|
||||
color: var(--color-error-text);
|
||||
}
|
||||
|
||||
/* FIXME: this is a longstanding dirty patch since 2015, it only makes the pages more messy and shouldn't be used */
|
||||
.center {
|
||||
text-align: center;
|
||||
|
||||
@@ -49,6 +49,7 @@
|
||||
@import "./features/console.css";
|
||||
@import "./features/captcha.css";
|
||||
|
||||
@import "./markup/attention.css";
|
||||
@import "./markup/content.css";
|
||||
@import "./markup/codeblock.css";
|
||||
@import "./markup/codepreview.css";
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
.attention-header > p:first-child {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.attention-icon {
|
||||
margin-inline-end: 6px;
|
||||
}
|
||||
|
||||
blockquote.attention-note {
|
||||
border-left-color: var(--color-info-text);
|
||||
}
|
||||
strong.attention-note, svg.attention-note {
|
||||
color: var(--color-info-text);
|
||||
}
|
||||
|
||||
blockquote.attention-tip {
|
||||
border-left-color: var(--color-success-text);
|
||||
}
|
||||
strong.attention-tip, svg.attention-tip {
|
||||
color: var(--color-success-text);
|
||||
}
|
||||
|
||||
blockquote.attention-important {
|
||||
border-left-color: var(--color-priority-text);
|
||||
}
|
||||
strong.attention-important, svg.attention-important {
|
||||
color: var(--color-priority-text);
|
||||
}
|
||||
|
||||
blockquote.attention-warning {
|
||||
border-left-color: var(--color-warning-text);
|
||||
}
|
||||
strong.attention-warning, svg.attention-warning {
|
||||
color: var(--color-warning-text);
|
||||
}
|
||||
|
||||
blockquote.attention-caution {
|
||||
border-left-color: var(--color-error-text);
|
||||
}
|
||||
strong.attention-caution, svg.attention-caution {
|
||||
color: var(--color-error-text);
|
||||
}
|
||||
@@ -86,6 +86,7 @@
|
||||
.markup h5 code,
|
||||
.markup h6 tt,
|
||||
.markup h6 code {
|
||||
padding: 0 0.2em;
|
||||
font-size: inherit;
|
||||
}
|
||||
|
||||
@@ -118,6 +119,16 @@
|
||||
color: var(--color-text-light-2);
|
||||
}
|
||||
|
||||
.markup summary :is(h1, h2, h3, h4, h5, h6) {
|
||||
display: inline-block;
|
||||
padding-bottom: 0;
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
.markup summary .anchor {
|
||||
margin-inline-start: -40px; /* clear the disclosure marker */
|
||||
}
|
||||
|
||||
.markup p,
|
||||
.markup blockquote,
|
||||
.markup details,
|
||||
@@ -138,8 +149,12 @@ In markup content, we always use bottom margin for all elements */
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.markup p {
|
||||
line-height: inherit; /* override base.css */
|
||||
}
|
||||
|
||||
.markup hr {
|
||||
height: 4px;
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
margin: 16px 0;
|
||||
background-color: var(--color-secondary);
|
||||
@@ -159,10 +174,6 @@ In markup content, we always use bottom margin for all elements */
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.markup .task-list-item p + ul {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.markup .task-list-item input[type="checkbox"] {
|
||||
margin-bottom: 0.25em;
|
||||
margin-inline: -1.4em 0.6em;
|
||||
@@ -170,13 +181,10 @@ In markup content, we always use bottom margin for all elements */
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.markup .task-list-item input[type="checkbox"] + p {
|
||||
margin-inline-start: -0.2em;
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.markup .task-list-item > p {
|
||||
margin-inline: 16px;
|
||||
/* a paragraph with another direction than its list has no list padding to hang the checkbox into */
|
||||
.markup :is(ul, ol):dir(ltr) > .task-list-item > p:dir(rtl) > input[type="checkbox"]:first-child,
|
||||
.markup :is(ul, ol):dir(rtl) > .task-list-item > p:dir(ltr) > input[type="checkbox"]:first-child {
|
||||
margin-inline-start: 0;
|
||||
}
|
||||
|
||||
.markup .task-list-item + .task-list-item {
|
||||
@@ -200,11 +208,14 @@ In markup content, we always use bottom margin for all elements */
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.markup ol ol,
|
||||
.markup ul ol {
|
||||
.markup :is(ol, ul) ol:not([type]) {
|
||||
list-style-type: lower-roman;
|
||||
}
|
||||
|
||||
.markup :is(ol, ul) :is(ol, ul) ol:not([type]) {
|
||||
list-style-type: lower-alpha;
|
||||
}
|
||||
|
||||
.markup li > p {
|
||||
margin-top: 16px;
|
||||
}
|
||||
@@ -225,13 +236,18 @@ In markup content, we always use bottom margin for all elements */
|
||||
font-weight: var(--font-weight-semibold);
|
||||
}
|
||||
|
||||
.markup dl dt + dt {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.markup dl dd {
|
||||
padding: 0 16px;
|
||||
margin-inline-start: 0;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.markup blockquote {
|
||||
margin-inline-start: 0;
|
||||
margin-inline: 0;
|
||||
padding: 0 15px;
|
||||
color: var(--color-text-light-2);
|
||||
border-left: 0.25em solid var(--color-secondary);
|
||||
@@ -250,6 +266,11 @@ In markup content, we always use bottom margin for all elements */
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
overflow: auto;
|
||||
font-variant: tabular-nums;
|
||||
}
|
||||
|
||||
.markup table td > :last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.markup table th {
|
||||
@@ -297,17 +318,8 @@ html[data-gitea-theme-dark="false"] .markup img[src*="#gh-dark-mode-only"] {
|
||||
padding: 0; /* let the file-not-rendered-prompt layout itself */
|
||||
}
|
||||
|
||||
/* this background ensures images can break <hr>. We can only do this on
|
||||
cases where the background is known and not transparent. */
|
||||
.file-view.markup img,
|
||||
.file-view.markup video,
|
||||
.comment-body .markup img, /* regular comment */
|
||||
.comment-body .markup video,
|
||||
.comment-content .markup img, /* code comment */
|
||||
.comment-content .markup video,
|
||||
.wiki .markup img,
|
||||
.wiki .markup video {
|
||||
background: var(--color-box-body);
|
||||
.markup :is(h1, h2, hr) {
|
||||
display: flow-root; /* keep borders clear of floated images */
|
||||
}
|
||||
|
||||
.markup img[align="right"],
|
||||
@@ -401,6 +413,7 @@ html[data-gitea-theme-dark="false"] .markup img[src*="#gh-dark-mode-only"] {
|
||||
background-color: var(--color-markup-code-inline);
|
||||
border: 1px solid var(--color-secondary);
|
||||
border-radius: var(--border-radius);
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.markup details.frontmatter-content summary {
|
||||
@@ -511,3 +524,11 @@ the manually written code-block elements on "packages" pages don't have the cont
|
||||
.markup .auto-hide-control .ui.button:active {
|
||||
background: var(--color-secondary-dark-1) !important;
|
||||
}
|
||||
|
||||
.markup .color-preview {
|
||||
display: inline-block;
|
||||
margin-left: 0.4em;
|
||||
height: 0.67em;
|
||||
width: 0.67em;
|
||||
border-radius: var(--border-radius);
|
||||
}
|
||||
Reference in new issue
Block a user