Merge remote-tracking branch 'origin/main' into zzc/dev/agit_2

This commit is contained in:
a1012112796 2024-06-05 00:32:13 +00:00
commit 695bafe882
No known key found for this signature in database
GPG Key ID: E5FB19032C2C2A64
20 changed files with 122 additions and 55 deletions

View File

@ -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

View File

@ -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

View File

@ -1019,4 +1019,10 @@ func TestAttention(t *testing.T) {
test(`> [!important]`, renderAttention("important", "octicon-report")+"\n</blockquote>")
test(`> [!warning]`, renderAttention("warning", "octicon-alert")+"\n</blockquote>")
test(`> [!caution]`, renderAttention("caution", "octicon-stop")+"\n</blockquote>")
// escaped by mdformat
test(`> \[!NOTE\]`, renderAttention("note", "octicon-info")+"\n</blockquote>")
// legacy GitHub style
test(`> **warning**`, renderAttention("warning", "octicon-alert")+"\n</blockquote>")
}

View File

@ -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
}

View File

@ -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)
}

View File

@ -47,13 +47,13 @@
<tr>
<td>{{.ID}}</td>
<td>
<a class="gt-word-break" href="{{.Owner.HomeLink}}">{{.Owner.Name}}</a>
<a class="tw-break-anywhere" href="{{.Owner.HomeLink}}">{{.Owner.Name}}</a>
{{if .Owner.Visibility.IsPrivate}}
<span class="text gold">{{svg "octicon-lock"}}</span>
{{end}}
</td>
<td>
<a class="gt-word-break" href="{{.Link}}">{{.Name}}</a>
<a class="tw-break-anywhere" href="{{.Link}}">{{.Name}}</a>
{{if .IsArchived}}
<span class="ui basic label">{{ctx.Locale.Tr "repo.desc.archived"}}</span>
{{end}}

View File

@ -54,7 +54,7 @@
{{end}}
{{if .PackageDescriptor.Metadata.ImageLayers}}
<h4 class="ui top attached header">{{ctx.Locale.Tr "packages.container.layers"}}</h4>
<div class="ui attached segment gt-word-break">
<div class="ui attached segment tw-break-anywhere">
<table class="ui very basic compact table">
<tbody>
{{range .PackageDescriptor.Metadata.ImageLayers}}
@ -80,7 +80,7 @@
{{range $key, $value := .PackageDescriptor.Metadata.Labels}}
<tr>
<td class="top aligned">{{$key}}</td>
<td class="gt-word-break">{{$value}}</td>
<td class="tw-break-anywhere">{{$value}}</td>
</tr>
{{end}}
</tbody>

View File

@ -59,7 +59,7 @@
{{ctx.Locale.Tr "packages.settings.delete"}}
</div>
<div class="content">
<div class="ui warning message gt-word-break">
<div class="ui warning message tw-break-anywhere">
{{ctx.Locale.Tr "packages.settings.delete.notice" .PackageDescriptor.Package.Name .PackageDescriptor.Version.Version}}
</div>
<form class="ui form" action="{{.Link}}" method="post">

View File

@ -152,7 +152,7 @@
<div class="divider"{{if .Color}} style="color: {{ContrastColor .Color}} !important"{{end}}></div>
<div class="ui cards" data-url="{{$.Link}}/{{.ID}}" data-project="{{$.Project.ID}}" data-board="{{.ID}}" id="board_{{.ID}}">
{{range (index $.IssuesMap .ID)}}
<div class="issue-card gt-word-break {{if $canWriteProject}}tw-cursor-grab{{end}}" data-issue="{{.ID}}">
<div class="issue-card tw-break-anywhere {{if $canWriteProject}}tw-cursor-grab{{end}}" data-issue="{{.ID}}">
{{template "repo/issue/card" (dict "Issue" . "Page" $)}}
</div>
{{end}}

View File

@ -5,7 +5,7 @@
{{template "base/alert" .}}
{{template "repo/code/recently_pushed_new_branches" .}}
{{if and (not .HideRepoInfo) (not .IsBlame)}}
<div class="repo-description gt-word-break">
<div class="repo-description tw-break-anywhere">
{{- $description := .Repository.DescriptionHTML ctx -}}
{{if $description}}{{$description | RenderCodeBlock}}{{end}}
{{if .Repository.Website}}<a href="{{.Repository.Website}}">{{.Repository.Website}}</a>{{end}}

View File

@ -7,7 +7,7 @@
{{if .PinnedIssues}}
<div id="issue-pins" {{if .IsRepoAdmin}}data-is-repo-admin{{end}}>
{{range .PinnedIssues}}
<div class="issue-card gt-word-break {{if $.IsRepoAdmin}}tw-cursor-grab{{end}}" data-move-url="{{$.Link}}/move_pin" data-issue-id="{{.ID}}">
<div class="issue-card tw-break-anywhere {{if $.IsRepoAdmin}}tw-cursor-grab{{end}}" data-move-url="{{$.Link}}/move_pin" data-issue-id="{{.ID}}">
{{template "repo/issue/card" (dict "Issue" . "Page" $ "isPinnedIssueCard" true)}}
</div>
{{end}}

View File

@ -8,7 +8,7 @@
<div class="ui segments conversation-holder">
<div class="ui segment collapsible-comment-box tw-py-2 tw-flex tw-items-center tw-justify-between">
<div class="tw-flex tw-items-center">
<a href="{{$comment.CodeCommentLink ctx}}" class="file-comment tw-ml-2 gt-word-break">{{$comment.TreePath}}</a>
<a href="{{$comment.CodeCommentLink ctx}}" class="file-comment tw-ml-2 tw-break-anywhere">{{$comment.TreePath}}</a>
{{if $invalid}}
<span class="ui label basic small tw-ml-2" data-tooltip-content="{{ctx.Locale.Tr "repo.issues.review.outdated_description"}}">
{{ctx.Locale.Tr "repo.issues.review.outdated"}}

View File

@ -17,7 +17,7 @@
</div>
<div class="ui segment detail">
<div class="tw-flex tw-items-center tw-justify-between tw-flex-wrap tw-mb-2">
<h4 class="release-list-title gt-word-break">
<h4 class="release-list-title tw-break-anywhere">
{{if $.PageIsSingleTag}}{{$release.Title}}{{else}}<a class="muted" href="{{$.RepoLink}}/releases/tag/{{$release.TagName | PathEscapeSegments}}">{{$release.Title}}</a>{{end}}
{{template "repo/commit_statuses" dict "Status" $info.CommitStatus "Statuses" $info.CommitStatuses "AdditionalClasses" "tw-flex"}}
{{if $release.IsDraft}}

View File

@ -217,7 +217,7 @@
<tbody>
{{range .PushMirrors}}
<tr>
<td class="gt-word-break">{{.RemoteAddress}}</td>
<td class="tw-break-anywhere">{{.RemoteAddress}}</td>
<td>{{ctx.Locale.Tr "repo.settings.mirror_settings.direction.push"}}</td>
<td>{{if .LastUpdateUnix}}{{DateTime "full" .LastUpdateUnix}}{{else}}{{ctx.Locale.Tr "never"}}{{end}} {{if .LastError}}<div class="ui red label" data-tooltip-content="{{.LastError}}">{{ctx.Locale.Tr "error"}}</div>{{end}}</td>
<td class="right aligned">

View File

@ -8,7 +8,7 @@
<div class="ui header">
<a class="file-revisions-btn ui basic button" title="{{ctx.Locale.Tr "repo.wiki.back_to_wiki"}}" href="{{.RepoLink}}/wiki/{{.PageURL}}"><span>{{.revision}}</span> {{svg "octicon-home"}}</a>
{{$title}}
<div class="ui sub header gt-word-break">
<div class="ui sub header tw-break-anywhere">
{{$timeSince := TimeSince .Author.When ctx.Locale}}
{{ctx.Locale.Tr "repo.wiki.last_commit_info" .Author.Name $timeSince}}
</div>

View File

@ -2,7 +2,7 @@
<div class="ui container">
<div class="ui vertically grid head">
<div class="column">
<div class="ui header tw-flex tw-items-center gt-word-break">
<div class="ui header tw-flex tw-items-center tw-break-anywhere">
{{ctx.AvatarUtils.Avatar . 100}}
<span class="text grey"><a class="muted" href="{{.HomeLink}}">{{.DisplayName}}</a></span>
<span class="org-visibility">

View File

@ -11,7 +11,7 @@
</span>
{{end}}
</div>
<div class="content gt-word-break profile-avatar-name">
<div class="content tw-break-anywhere profile-avatar-name">
{{if .ContextUser.FullName}}<span class="header text center">{{.ContextUser.FullName}}</span>{{end}}
<span class="username text center">{{.ContextUser.Name}} {{if .IsAdmin}}
<a class="muted" href="{{AppSubUrl}}/admin/users/{{.ContextUser.ID}}" data-tooltip-content="{{ctx.Locale.Tr "admin.users.details"}}">
@ -25,7 +25,7 @@
{{end}}
</div>
</div>
<div class="extra content gt-word-break">
<div class="extra content tw-break-anywhere">
<ul>
{{if .UserBlocking}}
<li class="text red">{{svg "octicon-circle-slash"}} {{ctx.Locale.Tr "user.block.blocked"}}</li>

View File

@ -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;

View File

@ -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');
}

View File

@ -125,7 +125,7 @@ export function initRepoIssueSidebarList() {
}
filteredResponse.results.push({
name: `<div class="gt-ellipsis">#${issue.number} ${htmlEscape(issue.title)}</div>
<div class="text small gt-word-break">${htmlEscape(issue.repository.full_name)}</div>`,
<div class="text small tw-break-anywhere">${htmlEscape(issue.repository.full_name)}</div>`,
value: issue.id,
});
});