diff --git a/docs/content/contributing/guidelines-frontend.en-us.md b/docs/content/contributing/guidelines-frontend.en-us.md index efeaf38bb2..a08098a931 100644 --- a/docs/content/contributing/guidelines-frontend.en-us.md +++ b/docs/content/contributing/guidelines-frontend.en-us.md @@ -47,7 +47,7 @@ We recommend [Google HTML/CSS Style Guide](https://google.github.io/styleguide/h 9. Avoid unnecessary `!important` in CSS, add comments to explain why it's necessary if it can't be avoided. 10. Avoid mixing different events in one event listener, prefer to use individual event listeners for every event. 11. Custom event names are recommended to use `ce-` prefix. -12. Prefer using Tailwind CSS which is available via `tw-` prefix, e.g. `tw-relative`. Gitea's helper CSS classes use `gt-` prefix (`gt-word-break`), while Gitea's own private framework-level CSS classes use `g-` prefix (`g-modal-confirm`). +12. Prefer using Tailwind CSS which is available via `tw-` prefix, e.g. `tw-relative`. Gitea's helper CSS classes use `gt-` prefix (`gt-ellipsis`), while Gitea's own private framework-level CSS classes use `g-` prefix (`g-modal-confirm`). 13. Avoid inline scripts & styles as much as possible, it's recommended to put JS code into JS files and use CSS classes. If inline scripts & styles are unavoidable, explain the reason why it can't be avoided. ### Accessibility / ARIA diff --git a/docs/content/contributing/guidelines-frontend.zh-cn.md b/docs/content/contributing/guidelines-frontend.zh-cn.md index 394097b259..198e1227e5 100644 --- a/docs/content/contributing/guidelines-frontend.zh-cn.md +++ b/docs/content/contributing/guidelines-frontend.zh-cn.md @@ -47,7 +47,7 @@ HTML 页面由[Go HTML Template](https://pkg.go.dev/html/template)渲染。 9. 避免在 CSS 中使用不必要的`!important`,如果无法避免,添加注释解释为什么需要它。 10. 避免在一个事件监听器中混合不同的事件,优先为每个事件使用独立的事件监听器。 11. 推荐使用自定义事件名称前缀`ce-`。 -12. 建议使用 Tailwind CSS,它可以通过 `tw-` 前缀获得,例如 `tw-relative`. Gitea 自身的助手类 CSS 使用 `gt-` 前缀(`gt-word-break`),Gitea 自身的私有框架级 CSS 类使用 `g-` 前缀(`g-modal-confirm`)。 +12. 建议使用 Tailwind CSS,它可以通过 `tw-` 前缀获得,例如 `tw-relative`. Gitea 自身的助手类 CSS 使用 `gt-` 前缀(`gt-ellipsis`),Gitea 自身的私有框架级 CSS 类使用 `g-` 前缀(`g-modal-confirm`)。 13. 尽量避免内联脚本和样式,建议将JS代码放入JS文件中并使用CSS类。如果内联脚本和样式不可避免,请解释无法避免的原因。 ### 可访问性 / ARIA diff --git a/modules/markup/markdown/markdown_test.go b/modules/markup/markdown/markdown_test.go index b4a7efa8dd..8c41ec12e3 100644 --- a/modules/markup/markdown/markdown_test.go +++ b/modules/markup/markdown/markdown_test.go @@ -1019,4 +1019,10 @@ func TestAttention(t *testing.T) { test(`> [!important]`, renderAttention("important", "octicon-report")+"\n") test(`> [!warning]`, renderAttention("warning", "octicon-alert")+"\n") test(`> [!caution]`, renderAttention("caution", "octicon-stop")+"\n") + + // escaped by mdformat + test(`> \[!NOTE\]`, renderAttention("note", "octicon-info")+"\n") + + // legacy GitHub style + test(`> **warning**`, renderAttention("warning", "octicon-alert")+"\n") } diff --git a/modules/markup/markdown/math/block_parser.go b/modules/markup/markdown/math/block_parser.go index 7f714d7239..37f6caf11c 100644 --- a/modules/markup/markdown/math/block_parser.go +++ b/modules/markup/markdown/math/block_parser.go @@ -31,10 +31,16 @@ func (b *blockParser) Open(parent ast.Node, reader text.Reader, pc parser.Contex return nil, parser.NoChildren } - dollars := false + var dollars bool if b.parseDollars && line[pos] == '$' && line[pos+1] == '$' { dollars = true - } else if line[pos] != '\\' || line[pos+1] != '[' { + } else if line[pos] == '\\' && line[pos+1] == '[' { + if len(line[pos:]) >= 3 && line[pos+2] == '!' && bytes.Contains(line[pos:], []byte(`\]`)) { + // do not process escaped attention block: "> \[!NOTE\]" + return nil, parser.NoChildren + } + dollars = false + } else { return nil, parser.NoChildren } diff --git a/modules/markup/markdown/transform_blockquote.go b/modules/markup/markdown/transform_blockquote.go index 933f0e5c59..d2dc025052 100644 --- a/modules/markup/markdown/transform_blockquote.go +++ b/modules/markup/markdown/transform_blockquote.go @@ -15,7 +15,7 @@ import ( "golang.org/x/text/language" ) -// renderAttention renders a quote marked with i.e. "> **Note**" or "> **Warning**" with a corresponding svg +// 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 { n := node.(*Attention) @@ -37,38 +37,93 @@ func (r *HTMLRenderer) renderAttention(w util.BufWriter, source []byte, node ast return ast.WalkContinue, nil } -func (g *ASTTransformer) transformBlockquote(v *ast.Blockquote, reader text.Reader) (ast.WalkStatus, error) { - // We only want attention blockquotes when the AST looks like: - // > Text("[") Text("!TYPE") Text("]") +func (g *ASTTransformer) extractBlockquoteAttentionEmphasis(firstParagraph ast.Node, reader text.Reader) (string, []ast.Node) { + if firstParagraph.ChildCount() < 1 { + return "", nil + } + node1, ok := firstParagraph.FirstChild().(*ast.Emphasis) + if !ok { + return "", nil + } + val1 := string(node1.Text(reader.Source())) + attentionType := strings.ToLower(val1) + if g.attentionTypes.Contains(attentionType) { + return attentionType, []ast.Node{node1} + } + return "", nil +} - // grab these nodes and make sure we adhere to the attention blockquote structure - firstParagraph := v.FirstChild() - g.applyElementDir(firstParagraph) - if firstParagraph.ChildCount() < 3 { - return ast.WalkContinue, nil +func (g *ASTTransformer) extractBlockquoteAttention2(firstParagraph ast.Node, reader text.Reader) (string, []ast.Node) { + if firstParagraph.ChildCount() < 2 { + return "", nil } node1, ok := firstParagraph.FirstChild().(*ast.Text) if !ok { - return ast.WalkContinue, nil + return "", nil } node2, ok := node1.NextSibling().(*ast.Text) if !ok { - return ast.WalkContinue, nil + return "", nil + } + val1 := string(node1.Segment.Value(reader.Source())) + val2 := string(node2.Segment.Value(reader.Source())) + if strings.HasPrefix(val1, `\[!`) && val2 == `\]` { + attentionType := strings.ToLower(val1[3:]) + if g.attentionTypes.Contains(attentionType) { + return attentionType, []ast.Node{node1, node2} + } + } + return "", nil +} + +func (g *ASTTransformer) extractBlockquoteAttention3(firstParagraph ast.Node, reader text.Reader) (string, []ast.Node) { + if firstParagraph.ChildCount() < 3 { + return "", nil + } + node1, ok := firstParagraph.FirstChild().(*ast.Text) + if !ok { + return "", nil + } + node2, ok := node1.NextSibling().(*ast.Text) + if !ok { + return "", nil } node3, ok := node2.NextSibling().(*ast.Text) if !ok { - return ast.WalkContinue, nil + return "", nil } val1 := string(node1.Segment.Value(reader.Source())) val2 := string(node2.Segment.Value(reader.Source())) val3 := string(node3.Segment.Value(reader.Source())) if val1 != "[" || val3 != "]" || !strings.HasPrefix(val2, "!") { - return ast.WalkContinue, nil + return "", nil } - // grab attention type from markdown source attentionType := strings.ToLower(val2[1:]) - if !g.attentionTypes.Contains(attentionType) { + if g.attentionTypes.Contains(attentionType) { + return attentionType, []ast.Node{node1, node2, node3} + } + return "", nil +} + +func (g *ASTTransformer) transformBlockquote(v *ast.Blockquote, reader text.Reader) (ast.WalkStatus, error) { + // We only want attention blockquotes when the AST looks like: + // > Text("[") Text("!TYPE") Text("]") + // > Text("\[!TYPE") TEXT("\]") + // > Text("**TYPE**") + + // grab these nodes and make sure we adhere to the attention blockquote structure + firstParagraph := v.FirstChild() + g.applyElementDir(firstParagraph) + + attentionType, processedNodes := g.extractBlockquoteAttentionEmphasis(firstParagraph, reader) + if attentionType == "" { + attentionType, processedNodes = g.extractBlockquoteAttention2(firstParagraph, reader) + } + if attentionType == "" { + attentionType, processedNodes = g.extractBlockquoteAttention3(firstParagraph, reader) + } + if attentionType == "" { return ast.WalkContinue, nil } @@ -88,9 +143,9 @@ func (g *ASTTransformer) transformBlockquote(v *ast.Blockquote, reader text.Read attentionParagraph.AppendChild(attentionParagraph, NewAttention(attentionType)) attentionParagraph.AppendChild(attentionParagraph, emphasis) firstParagraph.Parent().InsertBefore(firstParagraph.Parent(), firstParagraph, attentionParagraph) - firstParagraph.RemoveChild(firstParagraph, node1) - firstParagraph.RemoveChild(firstParagraph, node2) - firstParagraph.RemoveChild(firstParagraph, node3) + for _, processed := range processedNodes { + firstParagraph.RemoveChild(firstParagraph, processed) + } if firstParagraph.ChildCount() == 0 { firstParagraph.Parent().RemoveChild(firstParagraph.Parent(), firstParagraph) } diff --git a/templates/admin/repo/list.tmpl b/templates/admin/repo/list.tmpl index 4b27d87a45..69031e42eb 100644 --- a/templates/admin/repo/list.tmpl +++ b/templates/admin/repo/list.tmpl @@ -47,13 +47,13 @@ {{.ID}} - {{.Owner.Name}} + {{.Owner.Name}} {{if .Owner.Visibility.IsPrivate}} {{svg "octicon-lock"}} {{end}} - {{.Name}} + {{.Name}} {{if .IsArchived}} {{ctx.Locale.Tr "repo.desc.archived"}} {{end}} diff --git a/templates/package/content/container.tmpl b/templates/package/content/container.tmpl index fe393f4388..138fedecb3 100644 --- a/templates/package/content/container.tmpl +++ b/templates/package/content/container.tmpl @@ -54,7 +54,7 @@ {{end}} {{if .PackageDescriptor.Metadata.ImageLayers}}

{{ctx.Locale.Tr "packages.container.layers"}}

-
+
{{range .PackageDescriptor.Metadata.ImageLayers}} @@ -80,7 +80,7 @@ {{range $key, $value := .PackageDescriptor.Metadata.Labels}} - + {{end}} diff --git a/templates/package/settings.tmpl b/templates/package/settings.tmpl index 9424baf493..4b8773477b 100644 --- a/templates/package/settings.tmpl +++ b/templates/package/settings.tmpl @@ -59,7 +59,7 @@ {{ctx.Locale.Tr "packages.settings.delete"}}
-
+
{{ctx.Locale.Tr "packages.settings.delete.notice" .PackageDescriptor.Package.Name .PackageDescriptor.Version.Version}}
diff --git a/templates/projects/view.tmpl b/templates/projects/view.tmpl index 6d331caba7..584462d2a2 100644 --- a/templates/projects/view.tmpl +++ b/templates/projects/view.tmpl @@ -152,7 +152,7 @@
{{range (index $.IssuesMap .ID)}} -
+
{{template "repo/issue/card" (dict "Issue" . "Page" $)}}
{{end}} diff --git a/templates/repo/home.tmpl b/templates/repo/home.tmpl index ef76f3ed5d..ff82f2ca80 100644 --- a/templates/repo/home.tmpl +++ b/templates/repo/home.tmpl @@ -5,7 +5,7 @@ {{template "base/alert" .}} {{template "repo/code/recently_pushed_new_branches" .}} {{if and (not .HideRepoInfo) (not .IsBlame)}} -
+
{{- $description := .Repository.DescriptionHTML ctx -}} {{if $description}}{{$description | RenderCodeBlock}}{{end}} {{if .Repository.Website}}{{.Repository.Website}}{{end}} diff --git a/templates/repo/issue/list.tmpl b/templates/repo/issue/list.tmpl index 30edf825f1..01b610b39d 100644 --- a/templates/repo/issue/list.tmpl +++ b/templates/repo/issue/list.tmpl @@ -7,7 +7,7 @@ {{if .PinnedIssues}}
{{range .PinnedIssues}} -
+
{{template "repo/issue/card" (dict "Issue" . "Page" $ "isPinnedIssueCard" true)}}
{{end}} diff --git a/templates/repo/issue/view_content/conversation.tmpl b/templates/repo/issue/view_content/conversation.tmpl index 43ec9d75c4..ccea9b690d 100644 --- a/templates/repo/issue/view_content/conversation.tmpl +++ b/templates/repo/issue/view_content/conversation.tmpl @@ -8,7 +8,7 @@
- {{$comment.TreePath}} + {{$comment.TreePath}} {{if $invalid}} {{ctx.Locale.Tr "repo.issues.review.outdated"}} diff --git a/templates/repo/release/list.tmpl b/templates/repo/release/list.tmpl index 34548672b5..e5bf23faac 100644 --- a/templates/repo/release/list.tmpl +++ b/templates/repo/release/list.tmpl @@ -17,7 +17,7 @@
-

+

{{if $.PageIsSingleTag}}{{$release.Title}}{{else}}{{$release.Title}}{{end}} {{template "repo/commit_statuses" dict "Status" $info.CommitStatus "Statuses" $info.CommitStatuses "AdditionalClasses" "tw-flex"}} {{if $release.IsDraft}} diff --git a/templates/repo/settings/options.tmpl b/templates/repo/settings/options.tmpl index 6c49f00094..4f98133df3 100644 --- a/templates/repo/settings/options.tmpl +++ b/templates/repo/settings/options.tmpl @@ -217,7 +217,7 @@

{{range .PushMirrors}} - +
{{$key}}{{$value}}{{$value}}
{{.RemoteAddress}}{{.RemoteAddress}} {{ctx.Locale.Tr "repo.settings.mirror_settings.direction.push"}} {{if .LastUpdateUnix}}{{DateTime "full" .LastUpdateUnix}}{{else}}{{ctx.Locale.Tr "never"}}{{end}} {{if .LastError}}
{{ctx.Locale.Tr "error"}}
{{end}}
diff --git a/templates/repo/wiki/revision.tmpl b/templates/repo/wiki/revision.tmpl index 8e0060d4b3..7fca703843 100644 --- a/templates/repo/wiki/revision.tmpl +++ b/templates/repo/wiki/revision.tmpl @@ -8,7 +8,7 @@
{{.revision}} {{svg "octicon-home"}} {{$title}} -
+
{{$timeSince := TimeSince .Author.When ctx.Locale}} {{ctx.Locale.Tr "repo.wiki.last_commit_info" .Author.Name $timeSince}}
diff --git a/templates/shared/user/org_profile_avatar.tmpl b/templates/shared/user/org_profile_avatar.tmpl index d67f133abf..c0abcabff1 100644 --- a/templates/shared/user/org_profile_avatar.tmpl +++ b/templates/shared/user/org_profile_avatar.tmpl @@ -2,7 +2,7 @@
-
+
{{ctx.AvatarUtils.Avatar . 100}} {{.DisplayName}} diff --git a/templates/shared/user/profile_big_avatar.tmpl b/templates/shared/user/profile_big_avatar.tmpl index 868f8d5a13..29c6eb0eb0 100644 --- a/templates/shared/user/profile_big_avatar.tmpl +++ b/templates/shared/user/profile_big_avatar.tmpl @@ -11,7 +11,7 @@ {{end}}
-
+
{{if .ContextUser.FullName}}{{.ContextUser.FullName}}{{end}} {{.ContextUser.Name}} {{if .IsAdmin}} @@ -25,7 +25,7 @@ {{end}}
-
+
    {{if .UserBlocking}}
  • {{svg "octicon-circle-slash"}} {{ctx.Locale.Tr "user.block.blocked"}}
  • diff --git a/web_src/css/helpers.css b/web_src/css/helpers.css index 15df9f3a45..42d06e2e66 100644 --- a/web_src/css/helpers.css +++ b/web_src/css/helpers.css @@ -3,11 +3,6 @@ Gitea's tailwind-style CSS helper classes have `gt-` prefix. Gitea's private styles use `g-` prefix. */ -.gt-word-break { - word-wrap: break-word !important; - overflow-wrap: anywhere; -} - .gt-ellipsis { overflow: hidden !important; white-space: nowrap !important; diff --git a/web_src/js/features/admin/common.js b/web_src/js/features/admin/common.js index b35502d52f..3c90b546b8 100644 --- a/web_src/js/features/admin/common.js +++ b/web_src/js/features/admin/common.js @@ -67,39 +67,44 @@ export function initAdminCommon() { input.removeAttribute('required'); } - const provider = document.getElementById('oauth2_provider')?.value; + const provider = document.getElementById('oauth2_provider').value; switch (provider) { case 'openidConnect': - for (const input of document.querySelectorAll('.open_id_connect_auto_discovery_url input')) { - input.setAttribute('required', 'required'); - } + document.querySelector('.open_id_connect_auto_discovery_url input').setAttribute('required', 'required'); showElem('.open_id_connect_auto_discovery_url'); break; - default: - if (document.getElementById(`#${provider}_customURLSettings`)?.getAttribute('data-required')) { - document.getElementById('oauth2_use_custom_url')?.setAttribute('checked', 'checked'); + default: { + const elProviderCustomUrlSettings = document.querySelector(`#${provider}_customURLSettings`); + if (!elProviderCustomUrlSettings) break; // some providers do not have custom URL settings + const couldChangeCustomURLs = elProviderCustomUrlSettings.getAttribute('data-available') === 'true'; + const mustProvideCustomURLs = elProviderCustomUrlSettings.getAttribute('data-required') === 'true'; + if (couldChangeCustomURLs) { + showElem('.oauth2_use_custom_url'); // show the checkbox } - if (document.getElementById(`#${provider}_customURLSettings`)?.getAttribute('data-available')) { - showElem('.oauth2_use_custom_url'); + if (mustProvideCustomURLs) { + document.querySelector('#oauth2_use_custom_url').checked = true; // make the checkbox checked } + break; + } } onOAuth2UseCustomURLChange(applyDefaultValues); } function onOAuth2UseCustomURLChange(applyDefaultValues) { - const provider = document.getElementById('oauth2_provider')?.value; + const provider = document.getElementById('oauth2_provider').value; hideElem('.oauth2_use_custom_url_field'); for (const input of document.querySelectorAll('.oauth2_use_custom_url_field input[required]')) { input.removeAttribute('required'); } - if (document.getElementById('oauth2_use_custom_url')?.checked) { + const elProviderCustomUrlSettings = document.querySelector(`#${provider}_customURLSettings`); + if (elProviderCustomUrlSettings && document.getElementById('oauth2_use_custom_url').checked) { for (const custom of ['token_url', 'auth_url', 'profile_url', 'email_url', 'tenant']) { if (applyDefaultValues) { document.getElementById(`oauth2_${custom}`).value = document.getElementById(`${provider}_${custom}`).value; } const customInput = document.getElementById(`${provider}_${custom}`); - if (customInput && customInput.getAttribute('data-available')) { + if (customInput && customInput.getAttribute('data-available') === 'true') { for (const input of document.querySelectorAll(`.oauth2_${custom} input`)) { input.setAttribute('required', 'required'); } diff --git a/web_src/js/features/repo-issue.js b/web_src/js/features/repo-issue.js index 519db34934..95910e34bc 100644 --- a/web_src/js/features/repo-issue.js +++ b/web_src/js/features/repo-issue.js @@ -125,7 +125,7 @@ export function initRepoIssueSidebarList() { } filteredResponse.results.push({ name: `
    #${issue.number} ${htmlEscape(issue.title)}
    -
    ${htmlEscape(issue.repository.full_name)}
    `, +
    ${htmlEscape(issue.repository.full_name)}
    `, value: issue.id, }); });