From df439b6a983865ba559e517e5e93f5f1a53a97a0 Mon Sep 17 00:00:00 2001
From: yp05327 <576951401@qq.com>
Date: Thu, 15 Feb 2024 20:30:11 +0900
Subject: [PATCH 01/28] Fix can not select team reviewers when reviewers is
 empty (#29174)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Before:

![image](https://github.com/go-gitea/gitea/assets/18380374/b29e9c0c-f0fc-454f-b82d-ff9688d9e871)

After:

![image](https://github.com/go-gitea/gitea/assets/18380374/a982f7c6-4911-4951-91a5-4bb347e866f9)

Is this a bug? Maybe we don't need to fix this, as it only occurs when
there's only one user in the organization. 🤔

(cherry picked from commit 78c48d8fdde70a2874a7ed42b7762f797f432b03)
---
 templates/repo/issue/view_content/sidebar.tmpl | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/templates/repo/issue/view_content/sidebar.tmpl b/templates/repo/issue/view_content/sidebar.tmpl
index 6c13eef023..22f67ade7b 100644
--- a/templates/repo/issue/view_content/sidebar.tmpl
+++ b/templates/repo/issue/view_content/sidebar.tmpl
@@ -2,7 +2,7 @@
 	{{template "repo/issue/branch_selector_field" .}}
 	{{if .Issue.IsPull}}
 		<input id="reviewer_id" name="reviewer_id" type="hidden" value="{{.reviewer_id}}">
-		<div class="ui {{if or (not .Reviewers) (not .CanChooseReviewer) .Repository.IsArchived}}disabled{{end}} floating jump select-reviewers-modify dropdown">
+		<div class="ui {{if or (and (not .Reviewers) (not .TeamReviewers)) (not .CanChooseReviewer) .Repository.IsArchived}}disabled{{end}} floating jump select-reviewers-modify dropdown">
 			<a class="text gt-df gt-ac muted">
 				<strong>{{ctx.Locale.Tr "repo.issues.review.reviewers"}}</strong>
 				{{if and .CanChooseReviewer (not .Repository.IsArchived)}}
@@ -29,7 +29,9 @@
 					{{end}}
 				{{end}}
 				{{if .TeamReviewers}}
-					<div class="divider"></div>
+					{{if .Reviewers}}
+						<div class="divider"></div>
+					{{end}}
 					{{range .TeamReviewers}}
 						{{if .Team}}
 							<a class="{{if not .CanChange}}ui{{end}} item {{if .Checked}}checked{{end}} {{if not .CanChange}}ban-change{{end}}" href="#" data-id="{{.ItemID}}" data-id-selector="#review_request_team_{{.Team.ID}}" {{if not .CanChange}} data-tooltip-content="{{ctx.Locale.Tr "repo.issues.remove_request_review_block"}}"{{end}}>

From c2d4c2fca76baddc78d33502d365c780ab7445e6 Mon Sep 17 00:00:00 2001
From: Yarden Shoham <git@yardenshoham.com>
Date: Thu, 15 Feb 2024 15:27:07 +0200
Subject: [PATCH 02/28] Remove jQuery from the comment task list (#29170)

- Switched to plain JavaScript
- Tested the task list functionality and it works as before

---------

Signed-off-by: Yarden Shoham <git@yardenshoham.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: Giteabot <teabot@gitea.io>
Co-authored-by: silverwind <me@silverwind.io>
(cherry picked from commit 542480a9b0d5cdb497dbfa99752d59fd016df0d6)
---
 web_src/js/markup/tasklist.js | 13 ++++++-------
 1 file changed, 6 insertions(+), 7 deletions(-)

diff --git a/web_src/js/markup/tasklist.js b/web_src/js/markup/tasklist.js
index ad1c6964a7..00076bce58 100644
--- a/web_src/js/markup/tasklist.js
+++ b/web_src/js/markup/tasklist.js
@@ -1,4 +1,4 @@
-import $ from 'jquery';
+import {POST} from '../modules/fetch.js';
 
 const preventListener = (e) => e.preventDefault();
 
@@ -55,12 +55,11 @@ export function initMarkupTasklist() {
           const updateUrl = editContentZone.getAttribute('data-update-url');
           const context = editContentZone.getAttribute('data-context');
 
-          await $.post(updateUrl, {
-            ignore_attachments: true,
-            _csrf: window.config.csrfToken,
-            content: newContent,
-            context
-          });
+          const requestBody = new FormData();
+          requestBody.append('ignore_attachments', 'true');
+          requestBody.append('content', newContent);
+          requestBody.append('context', context);
+          await POST(updateUrl, {data: requestBody});
 
           rawContent.textContent = newContent;
         } catch (err) {

From 7263b3effe34a8f0de337365d659e48f389ea1c7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Tim-Nicas=20Oelschl=C3=A4ger?=
 <72873130+zokkis@users.noreply.github.com>
Date: Thu, 15 Feb 2024 14:59:48 +0100
Subject: [PATCH 03/28] Change webhook-type in create-view (#29114)

It's now possible to change webhook-type in create-view.

before:

![image](https://github.com/go-gitea/gitea/assets/72873130/9ee1b9fb-843b-4f28-b8d6-6361e5d184f1)

after:

![image](https://github.com/go-gitea/gitea/assets/72873130/9dbf058f-5912-43af-9acd-487271212f2d)

---------

Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: Giteabot <teabot@gitea.io>
(cherry picked from commit 374e886f5113a996e1e927a60d1775e77262c364)

Conflicts:
	templates/repo/settings/webhook/base_list.tmpl
	templates/shared/webhook/icon.tmpl
---
 routers/web/repo/setting/webhook.go           |  1 +
 .../repo/settings/webhook/base_list.tmpl      | 51 +-----------------
 .../repo/settings/webhook/link_menu.tmpl      | 54 +++++++++++++++++++
 templates/shared/webhook/icon.tmpl            |  2 +-
 templates/webhook/new.tmpl                    |  9 +++-
 web_src/css/base.css                          |  7 +++
 6 files changed, 71 insertions(+), 53 deletions(-)
 create mode 100644 templates/repo/settings/webhook/link_menu.tmpl

diff --git a/routers/web/repo/setting/webhook.go b/routers/web/repo/setting/webhook.go
index 2ae4cf9f16..7bbe4d81a9 100644
--- a/routers/web/repo/setting/webhook.go
+++ b/routers/web/repo/setting/webhook.go
@@ -151,6 +151,7 @@ func WebhooksNew(ctx *context.Context) {
 		}
 	}
 	ctx.Data["BaseLink"] = orCtx.LinkNew
+	ctx.Data["BaseLinkNew"] = orCtx.LinkNew
 
 	ctx.HTML(http.StatusOK, orCtx.NewTemplate)
 }
diff --git a/templates/repo/settings/webhook/base_list.tmpl b/templates/repo/settings/webhook/base_list.tmpl
index 1f38c035cd..5a3fc0e7b8 100644
--- a/templates/repo/settings/webhook/base_list.tmpl
+++ b/templates/repo/settings/webhook/base_list.tmpl
@@ -3,56 +3,7 @@
 	<div class="ui right">
 		<div class="ui jump dropdown">
 			<div class="ui primary tiny button">{{ctx.Locale.Tr "repo.settings.add_webhook"}}</div>
-			<div class="menu">
-				<a class="item" href="{{.BaseLinkNew}}/forgejo/new">
-					{{template "shared/webhook/icon" (dict "HookType" "forgejo" "Size" 20)}}
-					{{ctx.Locale.Tr "repo.settings.web_hook_name_forgejo"}}
-				</a>
-				<a class="item" href="{{.BaseLinkNew}}/gitea/new">
-					{{template "shared/webhook/icon" (dict "HookType" "gitea" "Size" 20)}}
-					{{ctx.Locale.Tr "repo.settings.web_hook_name_gitea"}}
-				</a>
-				<a class="item" href="{{.BaseLinkNew}}/gogs/new">
-					{{template "shared/webhook/icon" (dict "HookType" "gogs" "Size" 20)}}
-					{{ctx.Locale.Tr "repo.settings.web_hook_name_gogs"}}
-				</a>
-				<a class="item" href="{{.BaseLinkNew}}/slack/new">
-					{{template "shared/webhook/icon" (dict "HookType" "slack" "Size" 20)}}
-					{{ctx.Locale.Tr "repo.settings.web_hook_name_slack"}}
-				</a>
-				<a class="item" href="{{.BaseLinkNew}}/discord/new">
-					{{template "shared/webhook/icon" (dict "HookType" "discord" "Size" 20)}}
-					{{ctx.Locale.Tr "repo.settings.web_hook_name_discord"}}
-				</a>
-				<a class="item" href="{{.BaseLinkNew}}/dingtalk/new">
-					{{template "shared/webhook/icon" (dict "HookType" "dingtalk" "Size" 20)}}
-					{{ctx.Locale.Tr "repo.settings.web_hook_name_dingtalk"}}
-				</a>
-				<a class="item" href="{{.BaseLinkNew}}/telegram/new">
-					{{template "shared/webhook/icon" (dict "HookType" "telegram" "Size" 20)}}
-					{{ctx.Locale.Tr "repo.settings.web_hook_name_telegram"}}
-				</a>
-				<a class="item" href="{{.BaseLinkNew}}/msteams/new">
-					{{template "shared/webhook/icon" (dict "HookType" "msteams" "Size" 20)}}
-					{{ctx.Locale.Tr "repo.settings.web_hook_name_msteams"}}
-				</a>
-				<a class="item" href="{{.BaseLinkNew}}/feishu/new">
-					{{template "shared/webhook/icon" (dict "HookType" "feishu" "Size" 20)}}
-					{{ctx.Locale.Tr "repo.settings.web_hook_name_feishu_or_larksuite"}}
-				</a>
-				<a class="item" href="{{.BaseLinkNew}}/matrix/new">
-					{{template "shared/webhook/icon" (dict "HookType" "matrix" "Size" 20)}}
-					{{ctx.Locale.Tr "repo.settings.web_hook_name_matrix"}}
-				</a>
-				<a class="item" href="{{.BaseLinkNew}}/wechatwork/new">
-					{{template "shared/webhook/icon" (dict "HookType" "wechatwork" "Size" 20)}}
-					{{ctx.Locale.Tr "repo.settings.web_hook_name_wechatwork"}}
-				</a>
-				<a class="item" href="{{.BaseLinkNew}}/packagist/new">
-					{{template "shared/webhook/icon" (dict "HookType" "packagist" "Size" 20)}}
-					{{ctx.Locale.Tr "repo.settings.web_hook_name_packagist"}}
-				</a>
-			</div>
+			{{template "repo/settings/webhook/link_menu" .}}
 		</div>
 	</div>
 </h4>
diff --git a/templates/repo/settings/webhook/link_menu.tmpl b/templates/repo/settings/webhook/link_menu.tmpl
new file mode 100644
index 0000000000..811e262db6
--- /dev/null
+++ b/templates/repo/settings/webhook/link_menu.tmpl
@@ -0,0 +1,54 @@
+{{$size := 20}}
+{{if .Size}}
+	{{$size = .Size}}
+{{end}}
+<div class="menu">
+	<a class="item" href="{{.BaseLinkNew}}/forgejo/new">
+		{{template "shared/webhook/icon" (dict "HookType" "forgejo" "Size" $size)}}
+		{{ctx.Locale.Tr "repo.settings.web_hook_name_forgejo"}}
+	</a>
+	<a class="item" href="{{.BaseLinkNew}}/gitea/new">
+		{{template "shared/webhook/icon" (dict "HookType" "gitea" "Size" $size)}}
+		{{ctx.Locale.Tr "repo.settings.web_hook_name_gitea"}}
+	</a>
+	<a class="item" href="{{.BaseLinkNew}}/gogs/new">
+		{{template "shared/webhook/icon" (dict "HookType" "gogs" "Size" $size)}}
+		{{ctx.Locale.Tr "repo.settings.web_hook_name_gogs"}}
+	</a>
+	<a class="item" href="{{.BaseLinkNew}}/slack/new">
+		{{template "shared/webhook/icon" (dict "HookType" "slack" "Size" $size)}}
+		{{ctx.Locale.Tr "repo.settings.web_hook_name_slack"}}
+	</a>
+	<a class="item" href="{{.BaseLinkNew}}/discord/new">
+		{{template "shared/webhook/icon" (dict "HookType" "discord" "Size" $size)}}
+		{{ctx.Locale.Tr "repo.settings.web_hook_name_discord"}}
+	</a>
+	<a class="item" href="{{.BaseLinkNew}}/dingtalk/new">
+		{{template "shared/webhook/icon" (dict "HookType" "dingtalk" "Size" $size)}}
+		{{ctx.Locale.Tr "repo.settings.web_hook_name_dingtalk"}}
+	</a>
+	<a class="item" href="{{.BaseLinkNew}}/telegram/new">
+		{{template "shared/webhook/icon" (dict "HookType" "telegram" "Size" $size)}}
+		{{ctx.Locale.Tr "repo.settings.web_hook_name_telegram"}}
+	</a>
+	<a class="item" href="{{.BaseLinkNew}}/msteams/new">
+		{{template "shared/webhook/icon" (dict "HookType" "msteams" "Size" $size)}}
+		{{ctx.Locale.Tr "repo.settings.web_hook_name_msteams"}}
+	</a>
+	<a class="item" href="{{.BaseLinkNew}}/feishu/new">
+		{{template "shared/webhook/icon" (dict "HookType" "feishu" "Size" $size)}}
+		{{ctx.Locale.Tr "repo.settings.web_hook_name_feishu_or_larksuite"}}
+	</a>
+	<a class="item" href="{{.BaseLinkNew}}/matrix/new">
+		{{template "shared/webhook/icon" (dict "HookType" "matrix" "Size" $size)}}
+		{{ctx.Locale.Tr "repo.settings.web_hook_name_matrix"}}
+	</a>
+	<a class="item" href="{{.BaseLinkNew}}/wechatwork/new">
+		{{template "shared/webhook/icon" (dict "HookType" "wechatwork" "Size" $size)}}
+		{{ctx.Locale.Tr "repo.settings.web_hook_name_wechatwork"}}
+	</a>
+	<a class="item" href="{{.BaseLinkNew}}/packagist/new">
+		{{template "shared/webhook/icon" (dict "HookType" "packagist" "Size" $size)}}
+		{{ctx.Locale.Tr "repo.settings.web_hook_name_packagist"}}
+	</a>
+</div>
diff --git a/templates/shared/webhook/icon.tmpl b/templates/shared/webhook/icon.tmpl
index a6507bab5b..3c84f117ea 100644
--- a/templates/shared/webhook/icon.tmpl
+++ b/templates/shared/webhook/icon.tmpl
@@ -5,7 +5,7 @@
 {{if eq .HookType "forgejo"}}
 	<img width="{{$size}}" height="{{$size}}" src="{{AssetUrlPrefix}}/img/forgejo.svg">
 {{else if eq .HookType "gitea"}}
-	<img width="{{$size}}" height="{{$size}}" src="{{AssetUrlPrefix}}/img/gitea-original.svg">
+	{{svg "gitea-gitea" $size "img"}}
 {{else if eq .HookType "gogs"}}
 	<img width="{{$size}}" height="{{$size}}" src="{{AssetUrlPrefix}}/img/gogs.ico">
 {{else if eq .HookType "slack"}}
diff --git a/templates/webhook/new.tmpl b/templates/webhook/new.tmpl
index 60c729eee3..63bd8363b4 100644
--- a/templates/webhook/new.tmpl
+++ b/templates/webhook/new.tmpl
@@ -1,7 +1,12 @@
 <h4 class="ui top attached header">
 	{{.CustomHeaderTitle}}
-	<div class="ui right">
-		{{template "shared/webhook/icon" .ctxData}}
+	<div class="ui right type dropdown">
+		<div class="text gt-df gt-ac">
+			{{template "shared/webhook/icon" (dict "Size" 20 "HookType" .ctxData.HookType)}}
+			{{ctx.Locale.Tr (print "repo.settings.web_hook_name_" .ctxData.HookType)}}
+		</div>
+		{{svg "octicon-triangle-down" 14 "dropdown icon"}}
+		{{template "repo/settings/webhook/link_menu" .ctxData}}
 	</div>
 </h4>
 <div class="ui attached segment">
diff --git a/web_src/css/base.css b/web_src/css/base.css
index 198e87c0e2..ff72376a36 100644
--- a/web_src/css/base.css
+++ b/web_src/css/base.css
@@ -413,6 +413,13 @@ ol.ui.list li,
   color: var(--color-text-light-2);
 }
 
+/* extend fomantic style '.ui.dropdown > .text > img' to include svg.img */
+.ui.dropdown > .text > .img {
+  margin-left: 0;
+  float: none;
+  margin-right: 0.78571429rem;
+}
+
 .ui.dropdown > .text > .description,
 .ui.dropdown .menu > .item > .description {
   color: var(--color-text-light-2);

From 7474f6285796109906c0de6870fd92e865fd8196 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Thu, 15 Feb 2024 17:52:21 +0100
Subject: [PATCH 04/28] Tweak repo header (#29134)

- Tweak colors, remove link color from repo name and make text use
inherited color
- Downsize repo icon from 32px to 24px

Before:
<img width="255" alt="Screenshot 2024-02-11 at 15 31 00"
src="https://github.com/go-gitea/gitea/assets/115237/f65c1d02-d8a3-4171-ad3d-4c95871fb2ba">

After:
<img width="260" alt="Screenshot 2024-02-11 at 15 30 48"
src="https://github.com/go-gitea/gitea/assets/115237/a9b25b56-8d3f-4910-af60-2513d44f6d81">

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
(cherry picked from commit 363b5f0b595df4c703d80878d2f2a1bafd647291)
---
 templates/repo/header.tmpl | 6 +++---
 templates/repo/icon.tmpl   | 8 ++++----
 2 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/templates/repo/header.tmpl b/templates/repo/header.tmpl
index 6fe0b39b52..086ffd85ff 100644
--- a/templates/repo/header.tmpl
+++ b/templates/repo/header.tmpl
@@ -5,9 +5,9 @@
 			<div class="flex-item gt-ac">
 				<div class="flex-item-leading">{{template "repo/icon" .}}</div>
 				<div class="flex-item-main">
-					<div class="flex-item-title">
-						<a class="text light thin" href="{{.Owner.HomeLink}}">{{.Owner.Name}}</a>/
-						<a class="text primary" href="{{$.RepoLink}}">{{.Name}}</a></div>
+					<div class="flex-item-title gt-font-18">
+						<a class="muted gt-font-normal" href="{{.Owner.HomeLink}}">{{.Owner.Name}}</a>/
+						<a class="muted" href="{{$.RepoLink}}">{{.Name}}</a></div>
 				</div>
 				<div class="flex-item-trailing">
 					{{if .IsArchived}}
diff --git a/templates/repo/icon.tmpl b/templates/repo/icon.tmpl
index 5a80b959d0..a001f81891 100644
--- a/templates/repo/icon.tmpl
+++ b/templates/repo/icon.tmpl
@@ -1,10 +1,10 @@
 {{$avatarLink := (.RelAvatarLink ctx)}}
 {{if $avatarLink}}
-	<img class="ui avatar gt-vm" src="{{$avatarLink}}" width="32" height="32" alt="{{.FullName}}">
+	<img class="ui avatar gt-vm" src="{{$avatarLink}}" width="24" height="24" alt="{{.FullName}}">
 {{else if $.IsMirror}}
-	{{svg "octicon-mirror" 32}}
+	{{svg "octicon-mirror" 24}}
 {{else if $.IsFork}}
-	{{svg "octicon-repo-forked" 32}}
+	{{svg "octicon-repo-forked" 24}}
 {{else}}
-	{{svg "octicon-repo" 32}}
+	{{svg "octicon-repo" 24}}
 {{end}}

From 1f8ad34e4391673a2eda434ea5e48ea084cdc814 Mon Sep 17 00:00:00 2001
From: KN4CK3R <admin@oldschoolhack.me>
Date: Thu, 15 Feb 2024 21:39:50 +0100
Subject: [PATCH 05/28] Add support for action artifact serve direct (#29120)

Fixes #29093

(cherry picked from commit 07597c71a4b6642beae7589c678603f4846f7920)
---
 routers/api/actions/artifacts.go | 15 ++++++++++++++-
 1 file changed, 14 insertions(+), 1 deletion(-)

diff --git a/routers/api/actions/artifacts.go b/routers/api/actions/artifacts.go
index 3363c4c0e8..9fbd3f045d 100644
--- a/routers/api/actions/artifacts.go
+++ b/routers/api/actions/artifacts.go
@@ -63,6 +63,7 @@ package actions
 
 import (
 	"crypto/md5"
+	"errors"
 	"fmt"
 	"net/http"
 	"strconv"
@@ -426,7 +427,19 @@ func (ar artifactRoutes) getDownloadArtifactURL(ctx *ArtifactContext) {
 
 	var items []downloadArtifactResponseItem
 	for _, artifact := range artifacts {
-		downloadURL := ar.buildArtifactURL(runID, strconv.FormatInt(artifact.ID, 10), "download")
+		var downloadURL string
+		if setting.Actions.ArtifactStorage.MinioConfig.ServeDirect {
+			u, err := ar.fs.URL(artifact.StoragePath, artifact.ArtifactName)
+			if err != nil && !errors.Is(err, storage.ErrURLNotSupported) {
+				log.Error("Error getting serve direct url: %v", err)
+			}
+			if u != nil {
+				downloadURL = u.String()
+			}
+		}
+		if downloadURL == "" {
+			downloadURL = ar.buildArtifactURL(runID, strconv.FormatInt(artifact.ID, 10), "download")
+		}
 		item := downloadArtifactResponseItem{
 			Path:            util.PathJoinRel(itemPath, artifact.ArtifactPath),
 			ItemType:        "file",

From e9be8b25ae57189c4b29eaa393a397cf634d21d7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C5=9Eahin=20Akkaya?= <sahin@sahinakkaya.dev>
Date: Fri, 16 Feb 2024 01:21:13 +0300
Subject: [PATCH 06/28] Implement contributors graph (#27882)

Continuation of https://github.com/go-gitea/gitea/pull/25439. Fixes #847

Before:
<img width="1296" alt="image"
src="https://github.com/go-gitea/gitea/assets/32161460/24571ac8-b254-43c9-b178-97340f0dc8a9">

----
After:
<img width="1296" alt="image"
src="https://github.com/go-gitea/gitea/assets/32161460/c60b2459-9d10-4d42-8d83-d5ef0f45bf94">

---
#### Overview
This is the implementation of a requested feature: Contributors graph
(#847)

It makes Activity page a multi-tab page and adds a new tab called
Contributors. Contributors tab shows the contribution graphs over time
since the repository existed. It also shows per user contribution graphs
for top 100 contributors. Top 100 is calculated based on the selected
contribution type (commits, additions or deletions).

---
#### Demo
(The demo is a bit old but still a good example to show off the main
features)

<video src="https://github.com/go-gitea/gitea/assets/32161460/9f68103f-8145-4cc2-94bc-5546daae7014" controls width="320" height="240">
  <a href="https://github.com/go-gitea/gitea/assets/32161460/9f68103f-8145-4cc2-94bc-5546daae7014">Download</a>
</video>

#### Features:

- Select contribution type (commits, additions or deletions)
- See overall and per user contribution graphs for the selected
contribution type
- Zoom and pan on graphs to see them in detail
- See top 100 contributors based on the selected contribution type and
selected time range
- Go directly to users' profile by clicking their name if they are
registered gitea users
- Cache the results so that when the same repository is visited again
fetching data will be faster

---------

Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: hiifong <i@hiif.ong>
Co-authored-by: delvh <dev.lh@web.de>
Co-authored-by: 6543 <6543@obermui.de>
Co-authored-by: yp05327 <576951401@qq.com>
(cherry picked from commit 21331be30cb8f6c2d8b9dd99f1061623900632b9)
---
 options/locale/locale_en-US.ini               |  12 +
 package-lock.json                             |  67 ++-
 package.json                                  |   5 +
 routers/web/repo/activity.go                  |   2 +
 routers/web/repo/contributors.go              |  44 ++
 routers/web/web.go                            |   4 +
 services/repository/contributors_graph.go     | 319 +++++++++++++
 .../repository/contributors_graph_test.go     |  87 ++++
 templates/repo/activity.tmpl                  | 234 +--------
 templates/repo/contributors.tmpl              |  13 +
 templates/repo/navbar.tmpl                    |   8 +
 templates/repo/pulse.tmpl                     | 227 +++++++++
 web_src/js/components/.eslintrc.yaml          |   4 +
 web_src/js/components/RepoContributors.vue    | 443 ++++++++++++++++++
 web_src/js/features/contributors.js           |  28 ++
 web_src/js/index.js                           |   2 +
 web_src/js/utils/time.js                      |  46 ++
 web_src/js/utils/time.test.js                 |  15 +
 18 files changed, 1330 insertions(+), 230 deletions(-)
 create mode 100644 routers/web/repo/contributors.go
 create mode 100644 services/repository/contributors_graph.go
 create mode 100644 services/repository/contributors_graph_test.go
 create mode 100644 templates/repo/contributors.tmpl
 create mode 100644 templates/repo/navbar.tmpl
 create mode 100644 templates/repo/pulse.tmpl
 create mode 100644 web_src/js/components/RepoContributors.vue
 create mode 100644 web_src/js/features/contributors.js
 create mode 100644 web_src/js/utils/time.js
 create mode 100644 web_src/js/utils/time.test.js

diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini
index e3707243a5..7446aea4c0 100644
--- a/options/locale/locale_en-US.ini
+++ b/options/locale/locale_en-US.ini
@@ -1957,6 +1957,8 @@ wiki.page_name_desc = Enter a name for this Wiki page. Some special names are: '
 wiki.original_git_entry_tooltip = View original Git file instead of using friendly link.
 
 activity = Activity
+activity.navbar.pulse = Pulse
+activity.navbar.contributors = Contributors
 activity.period.filter_label = Period:
 activity.period.daily = 1 day
 activity.period.halfweekly = 3 days
@@ -2022,6 +2024,16 @@ activity.git_stats_and_deletions = and
 activity.git_stats_deletion_1 = %d deletion
 activity.git_stats_deletion_n = %d deletions
 
+contributors = Contributors
+contributors.contribution_type.filter_label = Contribution type:
+contributors.contribution_type.commits = Commits
+contributors.contribution_type.additions = Additions
+contributors.contribution_type.deletions = Deletions
+contributors.loading_title = Loading contributions...
+contributors.loading_title_failed = Could not load contributions
+contributors.loading_info = This might take a bit…
+contributors.component_failed_to_load = An unexpected error happened.
+
 search = Search
 search.search_repo = Search repository
 search.type.tooltip = Search type
diff --git a/package-lock.json b/package-lock.json
index 62bf36e7b7..764ae51f9d 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -19,8 +19,12 @@
         "add-asset-webpack-plugin": "2.0.1",
         "ansi_up": "6.0.2",
         "asciinema-player": "3.6.3",
+        "chart.js": "4.3.0",
+        "chartjs-adapter-dayjs-4": "1.0.4",
+        "chartjs-plugin-zoom": "2.0.1",
         "clippie": "4.0.6",
         "css-loader": "6.10.0",
+        "dayjs": "1.11.10",
         "dropzone": "6.0.0-beta.2",
         "easymde": "2.18.0",
         "esbuild-loader": "4.0.3",
@@ -47,6 +51,7 @@
         "uint8-to-base64": "0.2.0",
         "vue": "3.4.18",
         "vue-bar-graph": "2.0.0",
+        "vue-chartjs": "5.3.0",
         "vue-loader": "17.4.2",
         "vue3-calendar-heatmap": "2.0.5",
         "webpack": "5.90.1",
@@ -1278,6 +1283,11 @@
         "jsep": "^0.4.0||^1.0.0"
       }
     },
+    "node_modules/@kurkle/color": {
+      "version": "0.3.2",
+      "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.2.tgz",
+      "integrity": "sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw=="
+    },
     "node_modules/@mcaptcha/core-glue": {
       "version": "0.1.0-alpha-5",
       "resolved": "https://registry.npmjs.org/@mcaptcha/core-glue/-/core-glue-0.1.0-alpha-5.tgz",
@@ -3329,6 +3339,40 @@
         "url": "https://github.com/sponsors/wooorm"
       }
     },
+    "node_modules/chart.js": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.3.0.tgz",
+      "integrity": "sha512-ynG0E79xGfMaV2xAHdbhwiPLczxnNNnasrmPEXriXsPJGjmhOBYzFVEsB65w2qMDz+CaBJJuJD0inE/ab/h36g==",
+      "dependencies": {
+        "@kurkle/color": "^0.3.0"
+      },
+      "engines": {
+        "pnpm": ">=7"
+      }
+    },
+    "node_modules/chartjs-adapter-dayjs-4": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/chartjs-adapter-dayjs-4/-/chartjs-adapter-dayjs-4-1.0.4.tgz",
+      "integrity": "sha512-yy9BAYW4aNzPVrCWZetbILegTRb7HokhgospPoC3b5iZ5qdlqNmXts2KdSp6AqnjkPAp/YWyHDxLvIvwt5x81w==",
+      "engines": {
+        "node": ">=10"
+      },
+      "peerDependencies": {
+        "chart.js": ">=4.0.1",
+        "dayjs": "^1.9.7"
+      }
+    },
+    "node_modules/chartjs-plugin-zoom": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/chartjs-plugin-zoom/-/chartjs-plugin-zoom-2.0.1.tgz",
+      "integrity": "sha512-ogOmLu6e+Q7E1XWOCOz9YwybMslz9qNfGV2a+qjfmqJYpsw5ZMoRHZBUyW+NGhkpQ5PwwPA/+rikHpBZb7PZuA==",
+      "dependencies": {
+        "hammerjs": "^2.0.8"
+      },
+      "peerDependencies": {
+        "chart.js": ">=3.2.0"
+      }
+    },
     "node_modules/check-error": {
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz",
@@ -5868,9 +5912,17 @@
       "dev": true
     },
     "node_modules/gsap": {
-      "version": "3.12.5",
-      "resolved": "https://registry.npmjs.org/gsap/-/gsap-3.12.5.tgz",
-      "integrity": "sha512-srBfnk4n+Oe/ZnMIOXt3gT605BX9x5+rh/prT2F1SsNJsU1XuMiP0E2aptW481OnonOGACZWBqseH5Z7csHxhQ=="
+      "version": "3.12.2",
+      "resolved": "https://registry.npmjs.org/gsap/-/gsap-3.12.2.tgz",
+      "integrity": "sha512-EkYnpG8qHgYBFAwsgsGEqvT1WUidX0tt/ijepx7z8EUJHElykg91RvW1XbkT59T0gZzzszOpjQv7SE41XuIXyQ=="
+    },
+    "node_modules/hammerjs": {
+      "version": "2.0.8",
+      "resolved": "https://registry.npmjs.org/hammerjs/-/hammerjs-2.0.8.tgz",
+      "integrity": "sha512-tSQXBXS/MWQOn/RKckawJ61vvsDpCom87JgxiYdGwHdOa0ht0vzUWDlfioofFCRU0L+6NGDt6XzbgoJvZkMeRQ==",
+      "engines": {
+        "node": ">=0.8.0"
+      }
     },
     "node_modules/has-bigints": {
       "version": "1.0.2",
@@ -10934,6 +10986,15 @@
         "vue": "^3.2.37"
       }
     },
+    "node_modules/vue-chartjs": {
+      "version": "5.3.0",
+      "resolved": "https://registry.npmjs.org/vue-chartjs/-/vue-chartjs-5.3.0.tgz",
+      "integrity": "sha512-8XqX0JU8vFZ+WA2/knz4z3ThClduni2Nm0BMe2u0mXgTfd9pXrmJ07QBI+WAij5P/aPmPMX54HCE1seWL37ZdQ==",
+      "peerDependencies": {
+        "chart.js": "^4.1.1",
+        "vue": "^3.0.0-0 || ^2.7.0"
+      }
+    },
     "node_modules/vue-eslint-parser": {
       "version": "9.4.2",
       "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.4.2.tgz",
diff --git a/package.json b/package.json
index 46dfdd1055..dbb57b1624 100644
--- a/package.json
+++ b/package.json
@@ -18,8 +18,12 @@
     "add-asset-webpack-plugin": "2.0.1",
     "ansi_up": "6.0.2",
     "asciinema-player": "3.6.3",
+    "chart.js": "4.3.0",
+    "chartjs-adapter-dayjs-4": "1.0.4",
+    "chartjs-plugin-zoom": "2.0.1",
     "clippie": "4.0.6",
     "css-loader": "6.10.0",
+    "dayjs": "1.11.10",
     "dropzone": "6.0.0-beta.2",
     "easymde": "2.18.0",
     "esbuild-loader": "4.0.3",
@@ -46,6 +50,7 @@
     "uint8-to-base64": "0.2.0",
     "vue": "3.4.18",
     "vue-bar-graph": "2.0.0",
+    "vue-chartjs": "5.3.0",
     "vue-loader": "17.4.2",
     "vue3-calendar-heatmap": "2.0.5",
     "webpack": "5.90.1",
diff --git a/routers/web/repo/activity.go b/routers/web/repo/activity.go
index 3d030edaca..af99c4ed98 100644
--- a/routers/web/repo/activity.go
+++ b/routers/web/repo/activity.go
@@ -22,6 +22,8 @@ func Activity(ctx *context.Context) {
 	ctx.Data["Title"] = ctx.Tr("repo.activity")
 	ctx.Data["PageIsActivity"] = true
 
+	ctx.Data["PageIsPulse"] = true
+
 	ctx.Data["Period"] = ctx.Params("period")
 
 	timeUntil := time.Now()
diff --git a/routers/web/repo/contributors.go b/routers/web/repo/contributors.go
new file mode 100644
index 0000000000..f7dedc0b34
--- /dev/null
+++ b/routers/web/repo/contributors.go
@@ -0,0 +1,44 @@
+// Copyright 2023 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package repo
+
+import (
+	"errors"
+	"net/http"
+
+	"code.gitea.io/gitea/modules/base"
+	"code.gitea.io/gitea/modules/context"
+	contributors_service "code.gitea.io/gitea/services/repository"
+)
+
+const (
+	tplContributors base.TplName = "repo/activity"
+)
+
+// Contributors render the page to show repository contributors graph
+func Contributors(ctx *context.Context) {
+	ctx.Data["Title"] = ctx.Tr("repo.contributors")
+
+	ctx.Data["PageIsActivity"] = true
+	ctx.Data["PageIsContributors"] = true
+
+	ctx.PageData["contributionType"] = "commits"
+
+	ctx.PageData["repoLink"] = ctx.Repo.RepoLink
+
+	ctx.HTML(http.StatusOK, tplContributors)
+}
+
+// ContributorsData renders JSON of contributors along with their weekly commit statistics
+func ContributorsData(ctx *context.Context) {
+	if contributorStats, err := contributors_service.GetContributorStats(ctx, ctx.Cache, ctx.Repo.Repository, ctx.Repo.CommitID); err != nil {
+		if errors.Is(err, contributors_service.ErrAwaitGeneration) {
+			ctx.Status(http.StatusAccepted)
+			return
+		}
+		ctx.ServerError("GetContributorStats", err)
+	} else {
+		ctx.JSON(http.StatusOK, contributorStats)
+	}
+}
diff --git a/routers/web/web.go b/routers/web/web.go
index caea7bdd1e..cdec6759fd 100644
--- a/routers/web/web.go
+++ b/routers/web/web.go
@@ -1431,6 +1431,10 @@ func registerRoutes(m *web.Route) {
 		m.Group("/activity", func() {
 			m.Get("", repo.Activity)
 			m.Get("/{period}", repo.Activity)
+			m.Group("/contributors", func() {
+				m.Get("", repo.Contributors)
+				m.Get("/data", repo.ContributorsData)
+			})
 		}, context.RepoRef(), repo.MustBeNotEmpty, context.RequireRepoReaderOr(unit.TypePullRequests, unit.TypeIssues, unit.TypeReleases))
 
 		m.Group("/activity_author_data", func() {
diff --git a/services/repository/contributors_graph.go b/services/repository/contributors_graph.go
new file mode 100644
index 0000000000..8421df8e3a
--- /dev/null
+++ b/services/repository/contributors_graph.go
@@ -0,0 +1,319 @@
+// Copyright 2023 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package repository
+
+import (
+	"bufio"
+	"context"
+	"errors"
+	"fmt"
+	"os"
+	"strconv"
+	"strings"
+	"sync"
+	"time"
+
+	"code.gitea.io/gitea/models/avatars"
+	repo_model "code.gitea.io/gitea/models/repo"
+	user_model "code.gitea.io/gitea/models/user"
+	"code.gitea.io/gitea/modules/git"
+	"code.gitea.io/gitea/modules/gitrepo"
+	"code.gitea.io/gitea/modules/graceful"
+	"code.gitea.io/gitea/modules/log"
+	api "code.gitea.io/gitea/modules/structs"
+
+	"gitea.com/go-chi/cache"
+)
+
+const (
+	contributorStatsCacheKey           = "GetContributorStats/%s/%s"
+	contributorStatsCacheTimeout int64 = 60 * 10
+)
+
+var (
+	ErrAwaitGeneration  = errors.New("generation took longer than ")
+	awaitGenerationTime = time.Second * 5
+	generateLock        = sync.Map{}
+)
+
+type WeekData struct {
+	Week      int64 `json:"week"`      // Starting day of the week as Unix timestamp
+	Additions int   `json:"additions"` // Number of additions in that week
+	Deletions int   `json:"deletions"` // Number of deletions in that week
+	Commits   int   `json:"commits"`   // Number of commits in that week
+}
+
+// ContributorData represents statistical git commit count data
+type ContributorData struct {
+	Name         string              `json:"name"`  // Display name of the contributor
+	Login        string              `json:"login"` // Login name of the contributor in case it exists
+	AvatarLink   string              `json:"avatar_link"`
+	HomeLink     string              `json:"home_link"`
+	TotalCommits int64               `json:"total_commits"`
+	Weeks        map[int64]*WeekData `json:"weeks"`
+}
+
+// ExtendedCommitStats contains information for commit stats with author data
+type ExtendedCommitStats struct {
+	Author *api.CommitUser  `json:"author"`
+	Stats  *api.CommitStats `json:"stats"`
+}
+
+const layout = time.DateOnly
+
+func findLastSundayBeforeDate(dateStr string) (string, error) {
+	date, err := time.Parse(layout, dateStr)
+	if err != nil {
+		return "", err
+	}
+
+	weekday := date.Weekday()
+	daysToSubtract := int(weekday) - int(time.Sunday)
+	if daysToSubtract < 0 {
+		daysToSubtract += 7
+	}
+
+	lastSunday := date.AddDate(0, 0, -daysToSubtract)
+	return lastSunday.Format(layout), nil
+}
+
+// GetContributorStats returns contributors stats for git commits for given revision or default branch
+func GetContributorStats(ctx context.Context, cache cache.Cache, repo *repo_model.Repository, revision string) (map[string]*ContributorData, error) {
+	// as GetContributorStats is resource intensive we cache the result
+	cacheKey := fmt.Sprintf(contributorStatsCacheKey, repo.FullName(), revision)
+	if !cache.IsExist(cacheKey) {
+		genReady := make(chan struct{})
+
+		// dont start multible async generations
+		_, run := generateLock.Load(cacheKey)
+		if run {
+			return nil, ErrAwaitGeneration
+		}
+
+		generateLock.Store(cacheKey, struct{}{})
+		// run generation async
+		go generateContributorStats(genReady, cache, cacheKey, repo, revision)
+
+		select {
+		case <-time.After(awaitGenerationTime):
+			return nil, ErrAwaitGeneration
+		case <-genReady:
+			// we got generation ready before timeout
+			break
+		}
+	}
+	// TODO: renew timeout of cache cache.UpdateTimeout(cacheKey, contributorStatsCacheTimeout)
+
+	switch v := cache.Get(cacheKey).(type) {
+	case error:
+		return nil, v
+	case map[string]*ContributorData:
+		return v, nil
+	default:
+		return nil, fmt.Errorf("unexpected type in cache detected")
+	}
+}
+
+// getExtendedCommitStats return the list of *ExtendedCommitStats for the given revision
+func getExtendedCommitStats(repo *git.Repository, revision string /*, limit int */) ([]*ExtendedCommitStats, error) {
+	baseCommit, err := repo.GetCommit(revision)
+	if err != nil {
+		return nil, err
+	}
+	stdoutReader, stdoutWriter, err := os.Pipe()
+	if err != nil {
+		return nil, err
+	}
+	defer func() {
+		_ = stdoutReader.Close()
+		_ = stdoutWriter.Close()
+	}()
+
+	gitCmd := git.NewCommand(repo.Ctx, "log", "--shortstat", "--no-merges", "--pretty=format:---%n%aN%n%aE%n%as", "--reverse")
+	// AddOptionFormat("--max-count=%d", limit)
+	gitCmd.AddDynamicArguments(baseCommit.ID.String())
+
+	var extendedCommitStats []*ExtendedCommitStats
+	stderr := new(strings.Builder)
+	err = gitCmd.Run(&git.RunOpts{
+		Dir:    repo.Path,
+		Stdout: stdoutWriter,
+		Stderr: stderr,
+		PipelineFunc: func(ctx context.Context, cancel context.CancelFunc) error {
+			_ = stdoutWriter.Close()
+			scanner := bufio.NewScanner(stdoutReader)
+			scanner.Split(bufio.ScanLines)
+
+			for scanner.Scan() {
+				line := strings.TrimSpace(scanner.Text())
+				if line != "---" {
+					continue
+				}
+				scanner.Scan()
+				authorName := strings.TrimSpace(scanner.Text())
+				scanner.Scan()
+				authorEmail := strings.TrimSpace(scanner.Text())
+				scanner.Scan()
+				date := strings.TrimSpace(scanner.Text())
+				scanner.Scan()
+				stats := strings.TrimSpace(scanner.Text())
+				if authorName == "" || authorEmail == "" || date == "" || stats == "" {
+					// FIXME: find a better way to parse the output so that we will handle this properly
+					log.Warn("Something is wrong with git log output, skipping...")
+					log.Warn("authorName: %s,  authorEmail: %s,  date: %s,  stats: %s", authorName, authorEmail, date, stats)
+					continue
+				}
+				//  1 file changed, 1 insertion(+), 1 deletion(-)
+				fields := strings.Split(stats, ",")
+
+				commitStats := api.CommitStats{}
+				for _, field := range fields[1:] {
+					parts := strings.Split(strings.TrimSpace(field), " ")
+					value, contributionType := parts[0], parts[1]
+					amount, _ := strconv.Atoi(value)
+
+					if strings.HasPrefix(contributionType, "insertion") {
+						commitStats.Additions = amount
+					} else {
+						commitStats.Deletions = amount
+					}
+				}
+				commitStats.Total = commitStats.Additions + commitStats.Deletions
+				scanner.Scan()
+				scanner.Text() // empty line at the end
+
+				res := &ExtendedCommitStats{
+					Author: &api.CommitUser{
+						Identity: api.Identity{
+							Name:  authorName,
+							Email: authorEmail,
+						},
+						Date: date,
+					},
+					Stats: &commitStats,
+				}
+				extendedCommitStats = append(extendedCommitStats, res)
+
+			}
+			_ = stdoutReader.Close()
+			return nil
+		},
+	})
+	if err != nil {
+		return nil, fmt.Errorf("Failed to get ContributorsCommitStats for repository.\nError: %w\nStderr: %s", err, stderr)
+	}
+
+	return extendedCommitStats, nil
+}
+
+func generateContributorStats(genDone chan struct{}, cache cache.Cache, cacheKey string, repo *repo_model.Repository, revision string) {
+	ctx := graceful.GetManager().HammerContext()
+
+	gitRepo, closer, err := gitrepo.RepositoryFromContextOrOpen(ctx, repo)
+	if err != nil {
+		err := fmt.Errorf("OpenRepository: %w", err)
+		_ = cache.Put(cacheKey, err, contributorStatsCacheTimeout)
+		return
+	}
+	defer closer.Close()
+
+	if len(revision) == 0 {
+		revision = repo.DefaultBranch
+	}
+	extendedCommitStats, err := getExtendedCommitStats(gitRepo, revision)
+	if err != nil {
+		err := fmt.Errorf("ExtendedCommitStats: %w", err)
+		_ = cache.Put(cacheKey, err, contributorStatsCacheTimeout)
+		return
+	}
+	if len(extendedCommitStats) == 0 {
+		err := fmt.Errorf("no commit stats returned for revision '%s'", revision)
+		_ = cache.Put(cacheKey, err, contributorStatsCacheTimeout)
+		return
+	}
+
+	layout := time.DateOnly
+
+	unknownUserAvatarLink := user_model.NewGhostUser().AvatarLinkWithSize(ctx, 0)
+	contributorsCommitStats := make(map[string]*ContributorData)
+	contributorsCommitStats["total"] = &ContributorData{
+		Name:  "Total",
+		Weeks: make(map[int64]*WeekData),
+	}
+	total := contributorsCommitStats["total"]
+
+	for _, v := range extendedCommitStats {
+		userEmail := v.Author.Email
+		if len(userEmail) == 0 {
+			continue
+		}
+		u, _ := user_model.GetUserByEmail(ctx, userEmail)
+		if u != nil {
+			// update userEmail with user's primary email address so
+			// that different mail addresses will linked to same account
+			userEmail = u.GetEmail()
+		}
+		// duplicated logic
+		if _, ok := contributorsCommitStats[userEmail]; !ok {
+			if u == nil {
+				avatarLink := avatars.GenerateEmailAvatarFastLink(ctx, userEmail, 0)
+				if avatarLink == "" {
+					avatarLink = unknownUserAvatarLink
+				}
+				contributorsCommitStats[userEmail] = &ContributorData{
+					Name:       v.Author.Name,
+					AvatarLink: avatarLink,
+					Weeks:      make(map[int64]*WeekData),
+				}
+			} else {
+				contributorsCommitStats[userEmail] = &ContributorData{
+					Name:       u.DisplayName(),
+					Login:      u.LowerName,
+					AvatarLink: u.AvatarLinkWithSize(ctx, 0),
+					HomeLink:   u.HomeLink(),
+					Weeks:      make(map[int64]*WeekData),
+				}
+			}
+		}
+		// Update user statistics
+		user := contributorsCommitStats[userEmail]
+		startingOfWeek, _ := findLastSundayBeforeDate(v.Author.Date)
+
+		val, _ := time.Parse(layout, startingOfWeek)
+		week := val.UnixMilli()
+
+		if user.Weeks[week] == nil {
+			user.Weeks[week] = &WeekData{
+				Additions: 0,
+				Deletions: 0,
+				Commits:   0,
+				Week:      week,
+			}
+		}
+		if total.Weeks[week] == nil {
+			total.Weeks[week] = &WeekData{
+				Additions: 0,
+				Deletions: 0,
+				Commits:   0,
+				Week:      week,
+			}
+		}
+		user.Weeks[week].Additions += v.Stats.Additions
+		user.Weeks[week].Deletions += v.Stats.Deletions
+		user.Weeks[week].Commits++
+		user.TotalCommits++
+
+		// Update overall statistics
+		total.Weeks[week].Additions += v.Stats.Additions
+		total.Weeks[week].Deletions += v.Stats.Deletions
+		total.Weeks[week].Commits++
+		total.TotalCommits++
+	}
+
+	_ = cache.Put(cacheKey, contributorsCommitStats, contributorStatsCacheTimeout)
+	generateLock.Delete(cacheKey)
+	if genDone != nil {
+		genDone <- struct{}{}
+	}
+}
diff --git a/services/repository/contributors_graph_test.go b/services/repository/contributors_graph_test.go
new file mode 100644
index 0000000000..3801a5eee4
--- /dev/null
+++ b/services/repository/contributors_graph_test.go
@@ -0,0 +1,87 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package repository
+
+import (
+	"slices"
+	"testing"
+
+	"code.gitea.io/gitea/models/db"
+	repo_model "code.gitea.io/gitea/models/repo"
+	"code.gitea.io/gitea/models/unittest"
+	"code.gitea.io/gitea/modules/git"
+
+	"gitea.com/go-chi/cache"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestRepository_ContributorsGraph(t *testing.T) {
+	assert.NoError(t, unittest.PrepareTestDatabase())
+	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2})
+	assert.NoError(t, repo.LoadOwner(db.DefaultContext))
+	mockCache, err := cache.NewCacher(cache.Options{
+		Adapter:  "memory",
+		Interval: 24 * 60,
+	})
+	assert.NoError(t, err)
+
+	generateContributorStats(nil, mockCache, "key", repo, "404ref")
+	err, isErr := mockCache.Get("key").(error)
+	assert.True(t, isErr)
+	assert.ErrorAs(t, err, &git.ErrNotExist{})
+
+	generateContributorStats(nil, mockCache, "key2", repo, "master")
+	data, isData := mockCache.Get("key2").(map[string]*ContributorData)
+	assert.True(t, isData)
+	var keys []string
+	for k := range data {
+		keys = append(keys, k)
+	}
+	slices.Sort(keys)
+	assert.EqualValues(t, []string{
+		"ethantkoenig@gmail.com",
+		"jimmy.praet@telenet.be",
+		"jon@allspice.io",
+		"total", // generated summary
+	}, keys)
+
+	assert.EqualValues(t, &ContributorData{
+		Name:         "Ethan Koenig",
+		AvatarLink:   "https://secure.gravatar.com/avatar/b42fb195faa8c61b8d88abfefe30e9e3?d=identicon",
+		TotalCommits: 1,
+		Weeks: map[int64]*WeekData{
+			1511654400000: {
+				Week:      1511654400000, // sunday 2017-11-26
+				Additions: 3,
+				Deletions: 0,
+				Commits:   1,
+			},
+		},
+	}, data["ethantkoenig@gmail.com"])
+	assert.EqualValues(t, &ContributorData{
+		Name:         "Total",
+		AvatarLink:   "",
+		TotalCommits: 3,
+		Weeks: map[int64]*WeekData{
+			1511654400000: {
+				Week:      1511654400000, // sunday 2017-11-26 (2017-11-26 20:31:18 -0800)
+				Additions: 3,
+				Deletions: 0,
+				Commits:   1,
+			},
+			1607817600000: {
+				Week:      1607817600000, // sunday 2020-12-13 (2020-12-15 15:23:11 -0500)
+				Additions: 10,
+				Deletions: 0,
+				Commits:   1,
+			},
+			1624752000000: {
+				Week:      1624752000000, // sunday 2021-06-27 (2021-06-29 21:54:09 +0200)
+				Additions: 2,
+				Deletions: 0,
+				Commits:   1,
+			},
+		},
+	}, data["total"])
+}
diff --git a/templates/repo/activity.tmpl b/templates/repo/activity.tmpl
index 3149f20670..960083d2fb 100644
--- a/templates/repo/activity.tmpl
+++ b/templates/repo/activity.tmpl
@@ -1,235 +1,15 @@
 {{template "base/head" .}}
 <div role="main" aria-label="{{.Title}}" class="page-content repository commits">
 	{{template "repo/header" .}}
-	<div class="ui container">
-		<h2 class="ui header activity-header">
-			<span>{{DateTime "long" .DateFrom}} - {{DateTime "long" .DateUntil}}</span>
-			<!-- Period -->
-			<div class="ui floating dropdown jump filter">
-				<div class="ui basic compact button">
-					{{ctx.Locale.Tr "repo.activity.period.filter_label"}} <strong>{{.PeriodText}}</strong>
-					{{svg "octicon-triangle-down" 14 "dropdown icon"}}
-				</div>
-				<div class="menu">
-					<a class="{{if eq .Period "daily"}}active {{end}}item" href="{{$.RepoLink}}/activity/daily">{{ctx.Locale.Tr "repo.activity.period.daily"}}</a>
-					<a class="{{if eq .Period "halfweekly"}}active {{end}}item" href="{{$.RepoLink}}/activity/halfweekly">{{ctx.Locale.Tr "repo.activity.period.halfweekly"}}</a>
-					<a class="{{if eq .Period "weekly"}}active {{end}}item" href="{{$.RepoLink}}/activity/weekly">{{ctx.Locale.Tr "repo.activity.period.weekly"}}</a>
-					<a class="{{if eq .Period "monthly"}}active {{end}}item" href="{{$.RepoLink}}/activity/monthly">{{ctx.Locale.Tr "repo.activity.period.monthly"}}</a>
-					<a class="{{if eq .Period "quarterly"}}active {{end}}item" href="{{$.RepoLink}}/activity/quarterly">{{ctx.Locale.Tr "repo.activity.period.quarterly"}}</a>
-					<a class="{{if eq .Period "semiyearly"}}active {{end}}item" href="{{$.RepoLink}}/activity/semiyearly">{{ctx.Locale.Tr "repo.activity.period.semiyearly"}}</a>
-					<a class="{{if eq .Period "yearly"}}active {{end}}item" href="{{$.RepoLink}}/activity/yearly">{{ctx.Locale.Tr "repo.activity.period.yearly"}}</a>
-				</div>
-			</div>
-		</h2>
-		<div class="divider"></div>
-
-		{{if (or (.Permission.CanRead $.UnitTypeIssues) (.Permission.CanRead $.UnitTypePullRequests))}}
-		<h4 class="ui top attached header">{{ctx.Locale.Tr "repo.activity.overview"}}</h4>
-		<div class="ui attached segment two column grid">
-			{{if .Permission.CanRead $.UnitTypePullRequests}}
-				<div class="column">
-					{{if gt .Activity.ActivePRCount 0}}
-					<div class="stats-table">
-						<a href="#merged-pull-requests" class="table-cell tiny background purple" style="width: {{.Activity.MergedPRPerc}}{{if ne .Activity.MergedPRPerc 0}}%{{end}}"></a>
-						<a href="#proposed-pull-requests" class="table-cell tiny background green"></a>
-					</div>
-					{{else}}
-					<div class="stats-table">
-						<a class="table-cell tiny background light grey"></a>
-					</div>
-					{{end}}
-					{{ctx.Locale.TrN .Activity.ActivePRCount "repo.activity.active_prs_count_1" "repo.activity.active_prs_count_n" .Activity.ActivePRCount | Safe}}
-				</div>
-			{{end}}
-			{{if .Permission.CanRead $.UnitTypeIssues}}
-				<div class="column">
-					{{if gt .Activity.ActiveIssueCount 0}}
-					<div class="stats-table">
-						<a href="#closed-issues" class="table-cell tiny background red" style="width: {{.Activity.ClosedIssuePerc}}{{if ne .Activity.ClosedIssuePerc 0}}%{{end}}"></a>
-						<a href="#new-issues" class="table-cell tiny background green"></a>
-					</div>
-					{{else}}
-					<div class="stats-table">
-						<a class="table-cell tiny background light grey"></a>
-					</div>
-					{{end}}
-					{{ctx.Locale.TrN .Activity.ActiveIssueCount "repo.activity.active_issues_count_1" "repo.activity.active_issues_count_n" .Activity.ActiveIssueCount | Safe}}
-				</div>
-			{{end}}
+	<div class="ui container flex-container">
+		<div class="flex-container-nav">
+			{{template "repo/navbar" .}}
 		</div>
-		<div class="ui attached segment horizontal segments">
-			{{if .Permission.CanRead $.UnitTypePullRequests}}
-				<a href="#merged-pull-requests" class="ui attached segment text center">
-					<span class="text purple">{{svg "octicon-git-pull-request"}}</span> <strong>{{.Activity.MergedPRCount}}</strong><br>
-					{{ctx.Locale.TrN .Activity.MergedPRCount "repo.activity.merged_prs_count_1" "repo.activity.merged_prs_count_n"}}
-				</a>
-				<a href="#proposed-pull-requests" class="ui attached segment text center">
-					<span class="text green">{{svg "octicon-git-branch"}}</span> <strong>{{.Activity.OpenedPRCount}}</strong><br>
-					{{ctx.Locale.TrN .Activity.OpenedPRCount "repo.activity.opened_prs_count_1" "repo.activity.opened_prs_count_n"}}
-				</a>
-			{{end}}
-			{{if .Permission.CanRead $.UnitTypeIssues}}
-				<a href="#closed-issues" class="ui attached segment text center">
-					<span class="text red">{{svg "octicon-issue-closed"}}</span> <strong>{{.Activity.ClosedIssueCount}}</strong><br>
-					{{ctx.Locale.TrN .Activity.ClosedIssueCount "repo.activity.closed_issues_count_1" "repo.activity.closed_issues_count_n"}}
-				</a>
-				<a href="#new-issues" class="ui attached segment text center">
-					<span class="text green">{{svg "octicon-issue-opened"}}</span> <strong>{{.Activity.OpenedIssueCount}}</strong><br>
-					{{ctx.Locale.TrN .Activity.OpenedIssueCount "repo.activity.new_issues_count_1" "repo.activity.new_issues_count_n"}}
-				</a>
-			{{end}}
+		<div class="flex-container-main">
+			{{if .PageIsPulse}}{{template "repo/pulse" .}}{{end}}
+			{{if .PageIsContributors}}{{template "repo/contributors" .}}{{end}}
 		</div>
-		{{end}}
-
-		{{if .Permission.CanRead $.UnitTypeCode}}
-			{{if eq .Activity.Code.CommitCountInAllBranches 0}}
-				<div class="ui center aligned segment">
-				<h4 class="ui header">{{ctx.Locale.Tr "repo.activity.no_git_activity"}}</h4>
-				</div>
-			{{end}}
-			{{if gt .Activity.Code.CommitCountInAllBranches 0}}
-				<div class="ui attached segment horizontal segments">
-					<div class="ui attached segment text">
-						{{ctx.Locale.Tr "repo.activity.git_stats_exclude_merges"}}
-						<strong>{{ctx.Locale.TrN .Activity.Code.AuthorCount "repo.activity.git_stats_author_1" "repo.activity.git_stats_author_n" .Activity.Code.AuthorCount}}</strong>
-						{{ctx.Locale.TrN .Activity.Code.AuthorCount "repo.activity.git_stats_pushed_1" "repo.activity.git_stats_pushed_n"}}
-						<strong>{{ctx.Locale.TrN .Activity.Code.CommitCount "repo.activity.git_stats_commit_1" "repo.activity.git_stats_commit_n" .Activity.Code.CommitCount}}</strong>
-						{{ctx.Locale.Tr "repo.activity.git_stats_push_to_branch" .Repository.DefaultBranch}}
-						<strong>{{ctx.Locale.TrN .Activity.Code.CommitCountInAllBranches "repo.activity.git_stats_commit_1" "repo.activity.git_stats_commit_n" .Activity.Code.CommitCountInAllBranches}}</strong>
-						{{ctx.Locale.Tr "repo.activity.git_stats_push_to_all_branches"}}
-						{{ctx.Locale.Tr "repo.activity.git_stats_on_default_branch" .Repository.DefaultBranch}}
-						<strong>{{ctx.Locale.TrN .Activity.Code.ChangedFiles "repo.activity.git_stats_file_1" "repo.activity.git_stats_file_n" .Activity.Code.ChangedFiles}}</strong>
-						{{ctx.Locale.TrN .Activity.Code.ChangedFiles "repo.activity.git_stats_files_changed_1" "repo.activity.git_stats_files_changed_n"}}
-						{{ctx.Locale.Tr "repo.activity.git_stats_additions"}}
-						<strong class="text green">{{ctx.Locale.TrN .Activity.Code.Additions "repo.activity.git_stats_addition_1" "repo.activity.git_stats_addition_n" .Activity.Code.Additions}}</strong>
-						{{ctx.Locale.Tr "repo.activity.git_stats_and_deletions"}}
-						<strong class="text red">{{ctx.Locale.TrN .Activity.Code.Deletions "repo.activity.git_stats_deletion_1" "repo.activity.git_stats_deletion_n" .Activity.Code.Deletions}}</strong>.
-					</div>
-					<div class="ui attached segment">
-						<div id="repo-activity-top-authors-chart"></div>
-					</div>
-				</div>
-			{{end}}
-		{{end}}
-
-		{{if gt .Activity.PublishedReleaseCount 0}}
-			<h4 class="divider divider-text gt-normal-case" id="published-releases">
-				{{svg "octicon-tag" 16 "gt-mr-3"}}
-				{{ctx.Locale.Tr "repo.activity.title.releases_published_by"
-					(ctx.Locale.TrN .Activity.PublishedReleaseCount "repo.activity.title.releases_1" "repo.activity.title.releases_n" .Activity.PublishedReleaseCount)
-					(ctx.Locale.TrN .Activity.PublishedReleaseAuthorCount "repo.activity.title.user_1" "repo.activity.title.user_n" .Activity.PublishedReleaseAuthorCount)
-				}}
-			</h4>
-			<div class="list">
-				{{range .Activity.PublishedReleases}}
-					<p class="desc">
-						<span class="ui green label">{{ctx.Locale.Tr "repo.activity.published_release_label"}}</span>
-						{{.TagName}}
-						{{if not .IsTag}}
-							<a class="title" href="{{$.RepoLink}}/src/{{.TagName | PathEscapeSegments}}">{{.Title | RenderEmoji $.Context | RenderCodeBlock}}</a>
-						{{end}}
-						{{TimeSinceUnix .CreatedUnix ctx.Locale}}
-					</p>
-				{{end}}
-			</div>
-		{{end}}
-
-		{{if gt .Activity.MergedPRCount 0}}
-			<h4 class="divider divider-text gt-normal-case" id="merged-pull-requests">
-				{{svg "octicon-git-pull-request" 16 "gt-mr-3"}}
-				{{ctx.Locale.Tr "repo.activity.title.prs_merged_by"
-					(ctx.Locale.TrN .Activity.MergedPRCount "repo.activity.title.prs_1" "repo.activity.title.prs_n" .Activity.MergedPRCount)
-					(ctx.Locale.TrN .Activity.MergedPRAuthorCount "repo.activity.title.user_1" "repo.activity.title.user_n" .Activity.MergedPRAuthorCount)
-				}}
-			</h4>
-			<div class="list">
-				{{range .Activity.MergedPRs}}
-					<p class="desc">
-						<span class="ui purple label">{{ctx.Locale.Tr "repo.activity.merged_prs_label"}}</span>
-						#{{.Index}} <a class="title" href="{{$.RepoLink}}/pulls/{{.Index}}">{{.Issue.Title | RenderEmoji $.Context | RenderCodeBlock}}</a>
-						{{TimeSinceUnix .MergedUnix ctx.Locale}}
-					</p>
-				{{end}}
-			</div>
-		{{end}}
-
-		{{if gt .Activity.OpenedPRCount 0}}
-			<h4 class="divider divider-text gt-normal-case" id="proposed-pull-requests">
-				{{svg "octicon-git-branch" 16 "gt-mr-3"}}
-				{{ctx.Locale.Tr "repo.activity.title.prs_opened_by"
-					(ctx.Locale.TrN .Activity.OpenedPRCount "repo.activity.title.prs_1" "repo.activity.title.prs_n" .Activity.OpenedPRCount)
-					(ctx.Locale.TrN .Activity.OpenedPRAuthorCount "repo.activity.title.user_1" "repo.activity.title.user_n" .Activity.OpenedPRAuthorCount)
-				}}
-			</h4>
-			<div class="list">
-				{{range .Activity.OpenedPRs}}
-					<p class="desc">
-						<span class="ui green label">{{ctx.Locale.Tr "repo.activity.opened_prs_label"}}</span>
-						#{{.Index}} <a class="title" href="{{$.RepoLink}}/pulls/{{.Index}}">{{.Issue.Title | RenderEmoji $.Context | RenderCodeBlock}}</a>
-						{{TimeSinceUnix .Issue.CreatedUnix ctx.Locale}}
-					</p>
-				{{end}}
-			</div>
-		{{end}}
-
-		{{if gt .Activity.ClosedIssueCount 0}}
-			<h4 class="divider divider-text gt-normal-case" id="closed-issues">
-				{{svg "octicon-issue-closed" 16 "gt-mr-3"}}
-				{{ctx.Locale.Tr "repo.activity.title.issues_closed_from"
-					(ctx.Locale.TrN .Activity.ClosedIssueCount "repo.activity.title.issues_1" "repo.activity.title.issues_n" .Activity.ClosedIssueCount)
-					(ctx.Locale.TrN .Activity.ClosedIssueAuthorCount "repo.activity.title.user_1" "repo.activity.title.user_n" .Activity.ClosedIssueAuthorCount)
-				}}
-			</h4>
-			<div class="list">
-				{{range .Activity.ClosedIssues}}
-					<p class="desc">
-						<span class="ui red label">{{ctx.Locale.Tr "repo.activity.closed_issue_label"}}</span>
-						#{{.Index}} <a class="title" href="{{$.RepoLink}}/issues/{{.Index}}">{{.Title | RenderEmoji $.Context | RenderCodeBlock}}</a>
-						{{TimeSinceUnix .ClosedUnix ctx.Locale}}
-					</p>
-				{{end}}
-			</div>
-		{{end}}
-
-		{{if gt .Activity.OpenedIssueCount 0}}
-			<h4 class="divider divider-text gt-normal-case" id="new-issues">
-				{{svg "octicon-issue-opened" 16 "gt-mr-3"}}
-				{{ctx.Locale.Tr "repo.activity.title.issues_created_by"
-					(ctx.Locale.TrN .Activity.OpenedIssueCount "repo.activity.title.issues_1" "repo.activity.title.issues_n" .Activity.OpenedIssueCount)
-					(ctx.Locale.TrN .Activity.OpenedIssueAuthorCount "repo.activity.title.user_1" "repo.activity.title.user_n" .Activity.OpenedIssueAuthorCount)
-				}}
-			</h4>
-			<div class="list">
-				{{range .Activity.OpenedIssues}}
-					<p class="desc">
-						<span class="ui green label">{{ctx.Locale.Tr "repo.activity.new_issue_label"}}</span>
-						#{{.Index}} <a class="title" href="{{$.RepoLink}}/issues/{{.Index}}">{{.Title | RenderEmoji $.Context | RenderCodeBlock}}</a>
-						{{TimeSinceUnix .CreatedUnix ctx.Locale}}
-					</p>
-				{{end}}
-			</div>
-		{{end}}
-
-		{{if gt .Activity.UnresolvedIssueCount 0}}
-			<h4 class="divider divider-text gt-normal-case" id="unresolved-conversations" data-tooltip-content="{{ctx.Locale.Tr "repo.activity.unresolved_conv_desc"}}">
-				{{svg "octicon-comment-discussion" 16 "gt-mr-3"}}
-				{{ctx.Locale.TrN .Activity.UnresolvedIssueCount "repo.activity.title.unresolved_conv_1" "repo.activity.title.unresolved_conv_n" .Activity.UnresolvedIssueCount}}
-			</h4>
-			<div class="list">
-				{{range .Activity.UnresolvedIssues}}
-					<p class="desc">
-						<span class="ui green label">{{ctx.Locale.Tr "repo.activity.unresolved_conv_label"}}</span>
-						#{{.Index}}
-						{{if .IsPull}}
-						<a class="title" href="{{$.RepoLink}}/pulls/{{.Index}}">{{.Title | RenderEmoji $.Context | RenderCodeBlock}}</a>
-						{{else}}
-						<a class="title" href="{{$.RepoLink}}/issues/{{.Index}}">{{.Title | RenderEmoji $.Context | RenderCodeBlock}}</a>
-						{{end}}
-						{{TimeSinceUnix .UpdatedUnix ctx.Locale}}
-					</p>
-				{{end}}
-			</div>
-		{{end}}
 	</div>
 </div>
 {{template "base/footer" .}}
+
diff --git a/templates/repo/contributors.tmpl b/templates/repo/contributors.tmpl
new file mode 100644
index 0000000000..49a251c1f9
--- /dev/null
+++ b/templates/repo/contributors.tmpl
@@ -0,0 +1,13 @@
+{{if .Permission.CanRead $.UnitTypeCode}}
+	<div id="repo-contributors-chart"
+		data-locale-filter-label="{{ctx.Locale.Tr "repo.contributors.contribution_type.filter_label"}}"
+		data-locale-contribution-type-commits="{{ctx.Locale.Tr "repo.contributors.contribution_type.commits"}}"
+		data-locale-contribution-type-additions="{{ctx.Locale.Tr "repo.contributors.contribution_type.additions"}}"
+		data-locale-contribution-type-deletions="{{ctx.Locale.Tr "repo.contributors.contribution_type.deletions"}}"
+		data-locale-loading-title="{{ctx.Locale.Tr "repo.contributors.loading_title"}}"
+		data-locale-loading-title-failed="{{ctx.Locale.Tr "repo.contributors.loading_title_failed"}}"
+		data-locale-loading-info="{{ctx.Locale.Tr "repo.contributors.loading_info"}}"
+		data-locale-component-failed-to-load="{{ctx.Locale.Tr "repo.contributors.component_failed_to_load"}}"
+	>
+	</div>
+{{end}}
diff --git a/templates/repo/navbar.tmpl b/templates/repo/navbar.tmpl
new file mode 100644
index 0000000000..a9042ee30d
--- /dev/null
+++ b/templates/repo/navbar.tmpl
@@ -0,0 +1,8 @@
+<div class="ui fluid vertical menu">
+	<a class="{{if .PageIsPulse}}active {{end}}item" href="{{.RepoLink}}/activity">
+		{{ctx.Locale.Tr "repo.activity.navbar.pulse"}}
+	</a>
+	<a class="{{if .PageIsContributors}}active {{end}}item" href="{{.RepoLink}}/activity/contributors">
+		{{ctx.Locale.Tr "repo.activity.navbar.contributors"}}
+	</a>
+</div>
diff --git a/templates/repo/pulse.tmpl b/templates/repo/pulse.tmpl
new file mode 100644
index 0000000000..ccd7ebf6b5
--- /dev/null
+++ b/templates/repo/pulse.tmpl
@@ -0,0 +1,227 @@
+<h2 class="ui header activity-header">
+	<span>{{DateTime "long" .DateFrom}} - {{DateTime "long" .DateUntil}}</span>
+	<!-- Period -->
+	<div class="ui floating dropdown jump filter">
+		<div class="ui basic compact button">
+			{{ctx.Locale.Tr "repo.activity.period.filter_label"}} <strong>{{.PeriodText}}</strong>
+			{{svg "octicon-triangle-down" 14 "dropdown icon"}}
+		</div>
+		<div class="menu">
+			<a class="{{if eq .Period "daily"}}active {{end}}item" href="{{$.RepoLink}}/activity/daily">{{ctx.Locale.Tr "repo.activity.period.daily"}}</a>
+			<a class="{{if eq .Period "halfweekly"}}active {{end}}item" href="{{$.RepoLink}}/activity/halfweekly">{{ctx.Locale.Tr "repo.activity.period.halfweekly"}}</a>
+			<a class="{{if eq .Period "weekly"}}active {{end}}item" href="{{$.RepoLink}}/activity/weekly">{{ctx.Locale.Tr "repo.activity.period.weekly"}}</a>
+			<a class="{{if eq .Period "monthly"}}active {{end}}item" href="{{$.RepoLink}}/activity/monthly">{{ctx.Locale.Tr "repo.activity.period.monthly"}}</a>
+			<a class="{{if eq .Period "quarterly"}}active {{end}}item" href="{{$.RepoLink}}/activity/quarterly">{{ctx.Locale.Tr "repo.activity.period.quarterly"}}</a>
+			<a class="{{if eq .Period "semiyearly"}}active {{end}}item" href="{{$.RepoLink}}/activity/semiyearly">{{ctx.Locale.Tr "repo.activity.period.semiyearly"}}</a>
+			<a class="{{if eq .Period "yearly"}}active {{end}}item" href="{{$.RepoLink}}/activity/yearly">{{ctx.Locale.Tr "repo.activity.period.yearly"}}</a>
+		</div>
+	</div>
+</h2>
+
+{{if (or (.Permission.CanRead $.UnitTypeIssues) (.Permission.CanRead $.UnitTypePullRequests))}}
+<h4 class="ui top attached header">{{ctx.Locale.Tr "repo.activity.overview"}}</h4>
+<div class="ui attached segment two column grid">
+	{{if .Permission.CanRead $.UnitTypePullRequests}}
+		<div class="column">
+			{{if gt .Activity.ActivePRCount 0}}
+			<div class="stats-table">
+				<a href="#merged-pull-requests" class="table-cell tiny background purple" style="width: {{.Activity.MergedPRPerc}}{{if ne .Activity.MergedPRPerc 0}}%{{end}}"></a>
+				<a href="#proposed-pull-requests" class="table-cell tiny background green"></a>
+			</div>
+			{{else}}
+			<div class="stats-table">
+				<a class="table-cell tiny background light grey"></a>
+			</div>
+			{{end}}
+			{{ctx.Locale.TrN .Activity.ActivePRCount "repo.activity.active_prs_count_1" "repo.activity.active_prs_count_n" .Activity.ActivePRCount | Safe}}
+		</div>
+	{{end}}
+	{{if .Permission.CanRead $.UnitTypeIssues}}
+		<div class="column">
+			{{if gt .Activity.ActiveIssueCount 0}}
+			<div class="stats-table">
+				<a href="#closed-issues" class="table-cell tiny background red" style="width: {{.Activity.ClosedIssuePerc}}{{if ne .Activity.ClosedIssuePerc 0}}%{{end}}"></a>
+				<a href="#new-issues" class="table-cell tiny background green"></a>
+			</div>
+			{{else}}
+			<div class="stats-table">
+				<a class="table-cell tiny background light grey"></a>
+			</div>
+			{{end}}
+			{{ctx.Locale.TrN .Activity.ActiveIssueCount "repo.activity.active_issues_count_1" "repo.activity.active_issues_count_n" .Activity.ActiveIssueCount | Safe}}
+		</div>
+	{{end}}
+</div>
+<div class="ui attached segment horizontal segments">
+	{{if .Permission.CanRead $.UnitTypePullRequests}}
+		<a href="#merged-pull-requests" class="ui attached segment text center">
+			<span class="text purple">{{svg "octicon-git-pull-request"}}</span> <strong>{{.Activity.MergedPRCount}}</strong><br>
+			{{ctx.Locale.TrN .Activity.MergedPRCount "repo.activity.merged_prs_count_1" "repo.activity.merged_prs_count_n"}}
+		</a>
+		<a href="#proposed-pull-requests" class="ui attached segment text center">
+			<span class="text green">{{svg "octicon-git-branch"}}</span> <strong>{{.Activity.OpenedPRCount}}</strong><br>
+			{{ctx.Locale.TrN .Activity.OpenedPRCount "repo.activity.opened_prs_count_1" "repo.activity.opened_prs_count_n"}}
+		</a>
+	{{end}}
+	{{if .Permission.CanRead $.UnitTypeIssues}}
+		<a href="#closed-issues" class="ui attached segment text center">
+			<span class="text red">{{svg "octicon-issue-closed"}}</span> <strong>{{.Activity.ClosedIssueCount}}</strong><br>
+			{{ctx.Locale.TrN .Activity.ClosedIssueCount "repo.activity.closed_issues_count_1" "repo.activity.closed_issues_count_n"}}
+		</a>
+		<a href="#new-issues" class="ui attached segment text center">
+			<span class="text green">{{svg "octicon-issue-opened"}}</span> <strong>{{.Activity.OpenedIssueCount}}</strong><br>
+			{{ctx.Locale.TrN .Activity.OpenedIssueCount "repo.activity.new_issues_count_1" "repo.activity.new_issues_count_n"}}
+		</a>
+	{{end}}
+</div>
+{{end}}
+
+{{if .Permission.CanRead $.UnitTypeCode}}
+	{{if eq .Activity.Code.CommitCountInAllBranches 0}}
+		<div class="ui center aligned segment">
+		<h4 class="ui header">{{ctx.Locale.Tr "repo.activity.no_git_activity"}}</h4>
+		</div>
+	{{end}}
+	{{if gt .Activity.Code.CommitCountInAllBranches 0}}
+		<div class="ui attached segment horizontal segments">
+			<div class="ui attached segment text">
+				{{ctx.Locale.Tr "repo.activity.git_stats_exclude_merges"}}
+				<strong>{{ctx.Locale.TrN .Activity.Code.AuthorCount "repo.activity.git_stats_author_1" "repo.activity.git_stats_author_n" .Activity.Code.AuthorCount}}</strong>
+				{{ctx.Locale.TrN .Activity.Code.AuthorCount "repo.activity.git_stats_pushed_1" "repo.activity.git_stats_pushed_n"}}
+				<strong>{{ctx.Locale.TrN .Activity.Code.CommitCount "repo.activity.git_stats_commit_1" "repo.activity.git_stats_commit_n" .Activity.Code.CommitCount}}</strong>
+				{{ctx.Locale.Tr "repo.activity.git_stats_push_to_branch" .Repository.DefaultBranch}}
+				<strong>{{ctx.Locale.TrN .Activity.Code.CommitCountInAllBranches "repo.activity.git_stats_commit_1" "repo.activity.git_stats_commit_n" .Activity.Code.CommitCountInAllBranches}}</strong>
+				{{ctx.Locale.Tr "repo.activity.git_stats_push_to_all_branches"}}
+				{{ctx.Locale.Tr "repo.activity.git_stats_on_default_branch" .Repository.DefaultBranch}}
+				<strong>{{ctx.Locale.TrN .Activity.Code.ChangedFiles "repo.activity.git_stats_file_1" "repo.activity.git_stats_file_n" .Activity.Code.ChangedFiles}}</strong>
+				{{ctx.Locale.TrN .Activity.Code.ChangedFiles "repo.activity.git_stats_files_changed_1" "repo.activity.git_stats_files_changed_n"}}
+				{{ctx.Locale.Tr "repo.activity.git_stats_additions"}}
+				<strong class="text green">{{ctx.Locale.TrN .Activity.Code.Additions "repo.activity.git_stats_addition_1" "repo.activity.git_stats_addition_n" .Activity.Code.Additions}}</strong>
+				{{ctx.Locale.Tr "repo.activity.git_stats_and_deletions"}}
+				<strong class="text red">{{ctx.Locale.TrN .Activity.Code.Deletions "repo.activity.git_stats_deletion_1" "repo.activity.git_stats_deletion_n" .Activity.Code.Deletions}}</strong>.
+			</div>
+			<div class="ui attached segment">
+				<div id="repo-activity-top-authors-chart"></div>
+			</div>
+		</div>
+	{{end}}
+{{end}}
+
+{{if gt .Activity.PublishedReleaseCount 0}}
+	<h4 class="divider divider-text gt-normal-case" id="published-releases">
+		{{svg "octicon-tag" 16 "gt-mr-3"}}
+		{{ctx.Locale.Tr "repo.activity.title.releases_published_by"
+			(ctx.Locale.TrN .Activity.PublishedReleaseCount "repo.activity.title.releases_1" "repo.activity.title.releases_n" .Activity.PublishedReleaseCount)
+			(ctx.Locale.TrN .Activity.PublishedReleaseAuthorCount "repo.activity.title.user_1" "repo.activity.title.user_n" .Activity.PublishedReleaseAuthorCount)
+		}}
+	</h4>
+	<div class="list">
+		{{range .Activity.PublishedReleases}}
+			<p class="desc">
+				<span class="ui green label">{{ctx.Locale.Tr "repo.activity.published_release_label"}}</span>
+				{{.TagName}}
+				{{if not .IsTag}}
+					<a class="title" href="{{$.RepoLink}}/src/{{.TagName | PathEscapeSegments}}">{{.Title | RenderEmoji $.Context | RenderCodeBlock}}</a>
+				{{end}}
+				{{TimeSinceUnix .CreatedUnix ctx.Locale}}
+			</p>
+		{{end}}
+	</div>
+{{end}}
+
+{{if gt .Activity.MergedPRCount 0}}
+	<h4 class="divider divider-text gt-normal-case" id="merged-pull-requests">
+		{{svg "octicon-git-pull-request" 16 "gt-mr-3"}}
+		{{ctx.Locale.Tr "repo.activity.title.prs_merged_by"
+			(ctx.Locale.TrN .Activity.MergedPRCount "repo.activity.title.prs_1" "repo.activity.title.prs_n" .Activity.MergedPRCount)
+			(ctx.Locale.TrN .Activity.MergedPRAuthorCount "repo.activity.title.user_1" "repo.activity.title.user_n" .Activity.MergedPRAuthorCount)
+		}}
+	</h4>
+	<div class="list">
+		{{range .Activity.MergedPRs}}
+			<p class="desc">
+				<span class="ui purple label">{{ctx.Locale.Tr "repo.activity.merged_prs_label"}}</span>
+				#{{.Index}} <a class="title" href="{{$.RepoLink}}/pulls/{{.Index}}">{{.Issue.Title | RenderEmoji $.Context | RenderCodeBlock}}</a>
+				{{TimeSinceUnix .MergedUnix ctx.Locale}}
+			</p>
+		{{end}}
+	</div>
+{{end}}
+
+{{if gt .Activity.OpenedPRCount 0}}
+	<h4 class="divider divider-text gt-normal-case" id="proposed-pull-requests">
+		{{svg "octicon-git-branch" 16 "gt-mr-3"}}
+		{{ctx.Locale.Tr "repo.activity.title.prs_opened_by"
+			(ctx.Locale.TrN .Activity.OpenedPRCount "repo.activity.title.prs_1" "repo.activity.title.prs_n" .Activity.OpenedPRCount)
+			(ctx.Locale.TrN .Activity.OpenedPRAuthorCount "repo.activity.title.user_1" "repo.activity.title.user_n" .Activity.OpenedPRAuthorCount)
+		}}
+	</h4>
+	<div class="list">
+		{{range .Activity.OpenedPRs}}
+			<p class="desc">
+				<span class="ui green label">{{ctx.Locale.Tr "repo.activity.opened_prs_label"}}</span>
+				#{{.Index}} <a class="title" href="{{$.RepoLink}}/pulls/{{.Index}}">{{.Issue.Title | RenderEmoji $.Context | RenderCodeBlock}}</a>
+				{{TimeSinceUnix .Issue.CreatedUnix ctx.Locale}}
+			</p>
+		{{end}}
+	</div>
+{{end}}
+
+{{if gt .Activity.ClosedIssueCount 0}}
+	<h4 class="divider divider-text gt-normal-case" id="closed-issues">
+		{{svg "octicon-issue-closed" 16 "gt-mr-3"}}
+		{{ctx.Locale.Tr "repo.activity.title.issues_closed_from"
+			(ctx.Locale.TrN .Activity.ClosedIssueCount "repo.activity.title.issues_1" "repo.activity.title.issues_n" .Activity.ClosedIssueCount)
+			(ctx.Locale.TrN .Activity.ClosedIssueAuthorCount "repo.activity.title.user_1" "repo.activity.title.user_n" .Activity.ClosedIssueAuthorCount)
+		}}
+	</h4>
+	<div class="list">
+		{{range .Activity.ClosedIssues}}
+			<p class="desc">
+				<span class="ui red label">{{ctx.Locale.Tr "repo.activity.closed_issue_label"}}</span>
+				#{{.Index}} <a class="title" href="{{$.RepoLink}}/issues/{{.Index}}">{{.Title | RenderEmoji $.Context | RenderCodeBlock}}</a>
+				{{TimeSinceUnix .ClosedUnix ctx.Locale}}
+			</p>
+		{{end}}
+	</div>
+{{end}}
+
+{{if gt .Activity.OpenedIssueCount 0}}
+	<h4 class="divider divider-text gt-normal-case" id="new-issues">
+		{{svg "octicon-issue-opened" 16 "gt-mr-3"}}
+		{{ctx.Locale.Tr "repo.activity.title.issues_created_by"
+			(ctx.Locale.TrN .Activity.OpenedIssueCount "repo.activity.title.issues_1" "repo.activity.title.issues_n" .Activity.OpenedIssueCount)
+			(ctx.Locale.TrN .Activity.OpenedIssueAuthorCount "repo.activity.title.user_1" "repo.activity.title.user_n" .Activity.OpenedIssueAuthorCount)
+		}}
+	</h4>
+	<div class="list">
+		{{range .Activity.OpenedIssues}}
+			<p class="desc">
+				<span class="ui green label">{{ctx.Locale.Tr "repo.activity.new_issue_label"}}</span>
+				#{{.Index}} <a class="title" href="{{$.RepoLink}}/issues/{{.Index}}">{{.Title | RenderEmoji $.Context | RenderCodeBlock}}</a>
+				{{TimeSinceUnix .CreatedUnix ctx.Locale}}
+			</p>
+		{{end}}
+	</div>
+{{end}}
+
+{{if gt .Activity.UnresolvedIssueCount 0}}
+	<h4 class="divider divider-text gt-normal-case" id="unresolved-conversations" data-tooltip-content="{{ctx.Locale.Tr "repo.activity.unresolved_conv_desc"}}">
+		{{svg "octicon-comment-discussion" 16 "gt-mr-3"}}
+		{{ctx.Locale.TrN .Activity.UnresolvedIssueCount "repo.activity.title.unresolved_conv_1" "repo.activity.title.unresolved_conv_n" .Activity.UnresolvedIssueCount}}
+	</h4>
+	<div class="list">
+		{{range .Activity.UnresolvedIssues}}
+			<p class="desc">
+				<span class="ui green label">{{ctx.Locale.Tr "repo.activity.unresolved_conv_label"}}</span>
+				#{{.Index}}
+				{{if .IsPull}}
+				<a class="title" href="{{$.RepoLink}}/pulls/{{.Index}}">{{.Title | RenderEmoji $.Context | RenderCodeBlock}}</a>
+				{{else}}
+				<a class="title" href="{{$.RepoLink}}/issues/{{.Index}}">{{.Title | RenderEmoji $.Context | RenderCodeBlock}}</a>
+				{{end}}
+				{{TimeSinceUnix .UpdatedUnix ctx.Locale}}
+			</p>
+		{{end}}
+	</div>
+{{end}}
diff --git a/web_src/js/components/.eslintrc.yaml b/web_src/js/components/.eslintrc.yaml
index 0cab470f6b..0d233442bc 100644
--- a/web_src/js/components/.eslintrc.yaml
+++ b/web_src/js/components/.eslintrc.yaml
@@ -7,6 +7,10 @@ extends:
   - plugin:vue/vue3-recommended
   - plugin:vue-scoped-css/vue3-recommended
 
+parserOptions:
+  sourceType: module
+  ecmaVersion: latest
+
 env:
   browser: true
 
diff --git a/web_src/js/components/RepoContributors.vue b/web_src/js/components/RepoContributors.vue
new file mode 100644
index 0000000000..fa1545b3df
--- /dev/null
+++ b/web_src/js/components/RepoContributors.vue
@@ -0,0 +1,443 @@
+<script>
+import {SvgIcon} from '../svg.js';
+import {
+  Chart,
+  Title,
+  Tooltip,
+  Legend,
+  BarElement,
+  CategoryScale,
+  LinearScale,
+  TimeScale,
+  PointElement,
+  LineElement,
+  Filler,
+} from 'chart.js';
+import {GET} from '../modules/fetch.js';
+import zoomPlugin from 'chartjs-plugin-zoom';
+import {Line as ChartLine} from 'vue-chartjs';
+import {
+  startDaysBetween,
+  firstStartDateAfterDate,
+  fillEmptyStartDaysWithZeroes,
+} from '../utils/time.js';
+import 'chartjs-adapter-dayjs-4/dist/chartjs-adapter-dayjs-4.esm';
+import $ from 'jquery';
+
+const {pageData} = window.config;
+
+const colors = {
+  text: '--color-text',
+  border: '--color-secondary-alpha-60',
+  commits: '--color-primary-alpha-60',
+  additions: '--color-green',
+  deletions: '--color-red',
+  title: '--color-secondary-dark-4',
+};
+
+const styles = window.getComputedStyle(document.documentElement);
+const getColor = (name) => styles.getPropertyValue(name).trim();
+
+for (const [key, value] of Object.entries(colors)) {
+  colors[key] = getColor(value);
+}
+
+const customEventListener = {
+  id: 'customEventListener',
+  afterEvent: (chart, args, opts) => {
+    // event will be replayed from chart.update when reset zoom,
+    // so we need to check whether args.replay is true to avoid call loops
+    if (args.event.type === 'dblclick' && opts.chartType === 'main' && !args.replay) {
+      chart.resetZoom();
+      opts.instance.updateOtherCharts(args.event, true);
+    }
+  }
+};
+
+Chart.defaults.color = colors.text;
+Chart.defaults.borderColor = colors.border;
+
+Chart.register(
+  TimeScale,
+  CategoryScale,
+  LinearScale,
+  BarElement,
+  Title,
+  Tooltip,
+  Legend,
+  PointElement,
+  LineElement,
+  Filler,
+  zoomPlugin,
+  customEventListener,
+);
+
+export default {
+  components: {ChartLine, SvgIcon},
+  props: {
+    locale: {
+      type: Object,
+      required: true,
+    },
+  },
+  data: () => ({
+    isLoading: false,
+    errorText: '',
+    totalStats: {},
+    sortedContributors: {},
+    repoLink: pageData.repoLink || [],
+    type: pageData.contributionType,
+    contributorsStats: [],
+    xAxisStart: null,
+    xAxisEnd: null,
+    xAxisMin: null,
+    xAxisMax: null,
+  }),
+  mounted() {
+    this.fetchGraphData();
+
+    $('#repo-contributors').dropdown({
+      onChange: (val) => {
+        this.xAxisMin = this.xAxisStart;
+        this.xAxisMax = this.xAxisEnd;
+        this.type = val;
+        this.sortContributors();
+      }
+    });
+  },
+  methods: {
+    sortContributors() {
+      const contributors = this.filterContributorWeeksByDateRange();
+      const criteria = `total_${this.type}`;
+      this.sortedContributors = Object.values(contributors)
+        .filter((contributor) => contributor[criteria] !== 0)
+        .sort((a, b) => a[criteria] > b[criteria] ? -1 : a[criteria] === b[criteria] ? 0 : 1)
+        .slice(0, 100);
+    },
+
+    async fetchGraphData() {
+      this.isLoading = true;
+      try {
+        let response;
+        do {
+          response = await GET(`${this.repoLink}/activity/contributors/data`);
+          if (response.status === 202) {
+            await new Promise((resolve) => setTimeout(resolve, 1000)); // wait for 1 second before retrying
+          }
+        } while (response.status === 202);
+        if (response.ok) {
+          const data = await response.json();
+          const {total, ...rest} = data;
+          // below line might be deleted if we are sure go produces map always sorted by keys
+          total.weeks = Object.fromEntries(Object.entries(total.weeks).sort());
+
+          const weekValues = Object.values(total.weeks);
+          this.xAxisStart = weekValues[0].week;
+          this.xAxisEnd = firstStartDateAfterDate(new Date());
+          const startDays = startDaysBetween(new Date(this.xAxisStart), new Date(this.xAxisEnd));
+          total.weeks = fillEmptyStartDaysWithZeroes(startDays, total.weeks);
+          this.xAxisMin = this.xAxisStart;
+          this.xAxisMax = this.xAxisEnd;
+          this.contributorsStats = {};
+          for (const [email, user] of Object.entries(rest)) {
+            user.weeks = fillEmptyStartDaysWithZeroes(startDays, user.weeks);
+            this.contributorsStats[email] = user;
+          }
+          this.sortContributors();
+          this.totalStats = total;
+          this.errorText = '';
+        } else {
+          this.errorText = response.statusText;
+        }
+      } catch (err) {
+        this.errorText = err.message;
+      } finally {
+        this.isLoading = false;
+      }
+    },
+
+    filterContributorWeeksByDateRange() {
+      const filteredData = {};
+      const data = this.contributorsStats;
+      for (const key of Object.keys(data)) {
+        const user = data[key];
+        user.total_commits = 0;
+        user.total_additions = 0;
+        user.total_deletions = 0;
+        user.max_contribution_type = 0;
+        const filteredWeeks = user.weeks.filter((week) => {
+          const oneWeek = 7 * 24 * 60 * 60 * 1000;
+          if (week.week >= this.xAxisMin - oneWeek && week.week <= this.xAxisMax + oneWeek) {
+            user.total_commits += week.commits;
+            user.total_additions += week.additions;
+            user.total_deletions += week.deletions;
+            if (week[this.type] > user.max_contribution_type) {
+              user.max_contribution_type = week[this.type];
+            }
+            return true;
+          }
+          return false;
+        });
+        // this line is required. See https://github.com/sahinakkaya/gitea/pull/3#discussion_r1396495722
+        // for details.
+        user.max_contribution_type += 1;
+
+        filteredData[key] = {...user, weeks: filteredWeeks};
+      }
+
+      return filteredData;
+    },
+
+    maxMainGraph() {
+      // This method calculates maximum value for Y value of the main graph. If the number
+      // of maximum contributions for selected contribution type is 15.955 it is probably
+      // better to round it up to 20.000.This method is responsible for doing that.
+      // Normally, chartjs handles this automatically, but it will resize the graph when you
+      // zoom, pan etc. I think resizing the graph makes it harder to compare things visually.
+      const maxValue = Math.max(
+        ...this.totalStats.weeks.map((o) => o[this.type])
+      );
+      const [coefficient, exp] = maxValue.toExponential().split('e').map(Number);
+      if (coefficient % 1 === 0) return maxValue;
+      return (1 - (coefficient % 1)) * 10 ** exp + maxValue;
+    },
+
+    maxContributorGraph() {
+      // Similar to maxMainGraph method this method calculates maximum value for Y value
+      // for contributors' graph. If I let chartjs do this for me, it will choose different
+      // maxY value for each contributors' graph which again makes it harder to compare.
+      const maxValue = Math.max(
+        ...this.sortedContributors.map((c) => c.max_contribution_type)
+      );
+      const [coefficient, exp] = maxValue.toExponential().split('e').map(Number);
+      if (coefficient % 1 === 0) return maxValue;
+      return (1 - (coefficient % 1)) * 10 ** exp + maxValue;
+    },
+
+    toGraphData(data) {
+      return {
+        datasets: [
+          {
+            data: data.map((i) => ({x: i.week, y: i[this.type]})),
+            pointRadius: 0,
+            pointHitRadius: 0,
+            fill: 'start',
+            backgroundColor: colors[this.type],
+            borderWidth: 0,
+            tension: 0.3,
+          },
+        ],
+      };
+    },
+
+    updateOtherCharts(event, reset) {
+      const minVal = event.chart.options.scales.x.min;
+      const maxVal = event.chart.options.scales.x.max;
+      if (reset) {
+        this.xAxisMin = this.xAxisStart;
+        this.xAxisMax = this.xAxisEnd;
+        this.sortContributors();
+      } else if (minVal) {
+        this.xAxisMin = minVal;
+        this.xAxisMax = maxVal;
+        this.sortContributors();
+      }
+    },
+
+    getOptions(type) {
+      return {
+        responsive: true,
+        maintainAspectRatio: false,
+        animation: false,
+        events: ['mousemove', 'mouseout', 'click', 'touchstart', 'touchmove', 'dblclick'],
+        plugins: {
+          title: {
+            display: type === 'main',
+            text: 'drag: zoom, shift+drag: pan, double click: reset zoom',
+            color: colors.title,
+            position: 'top',
+            align: 'center',
+          },
+          customEventListener: {
+            chartType: type,
+            instance: this,
+          },
+          legend: {
+            display: false,
+          },
+          zoom: {
+            pan: {
+              enabled: true,
+              modifierKey: 'shift',
+              mode: 'x',
+              threshold: 20,
+              onPanComplete: this.updateOtherCharts,
+            },
+            limits: {
+              x: {
+                // Check https://www.chartjs.org/chartjs-plugin-zoom/latest/guide/options.html#scale-limits
+                // to know what each option means
+                min: 'original',
+                max: 'original',
+
+                // number of milliseconds in 2 weeks. Minimum x range will be 2 weeks when you zoom on the graph
+                minRange: 2 * 7 * 24 * 60 * 60 * 1000,
+              },
+            },
+            zoom: {
+              drag: {
+                enabled: type === 'main',
+              },
+              pinch: {
+                enabled: type === 'main',
+              },
+              mode: 'x',
+              onZoomComplete: this.updateOtherCharts,
+            },
+          },
+        },
+        scales: {
+          x: {
+            min: this.xAxisMin,
+            max: this.xAxisMax,
+            type: 'time',
+            grid: {
+              display: false,
+            },
+            time: {
+              minUnit: 'month',
+            },
+            ticks: {
+              maxRotation: 0,
+              maxTicksLimit: type === 'main' ? 12 : 6,
+            },
+          },
+          y: {
+            min: 0,
+            max: type === 'main' ? this.maxMainGraph() : this.maxContributorGraph(),
+            ticks: {
+              maxTicksLimit: type === 'main' ? 6 : 4,
+            },
+          },
+        },
+      };
+    },
+  },
+};
+</script>
+<template>
+  <div>
+    <h2 class="ui header gt-df gt-ac gt-sb">
+      <div>
+        <relative-time
+          v-if="xAxisMin > 0"
+          format="datetime"
+          year="numeric"
+          month="short"
+          day="numeric"
+          weekday=""
+          :datetime="new Date(xAxisMin)"
+        >
+          {{ new Date(xAxisMin) }}
+        </relative-time>
+        {{ isLoading ? locale.loadingTitle : errorText ? locale.loadingTitleFailed: "-" }}
+        <relative-time
+          v-if="xAxisMax > 0"
+          format="datetime"
+          year="numeric"
+          month="short"
+          day="numeric"
+          weekday=""
+          :datetime="new Date(xAxisMax)"
+        >
+          {{ new Date(xAxisMax) }}
+        </relative-time>
+      </div>
+      <div>
+        <!-- Contribution type -->
+        <div class="ui dropdown jump" id="repo-contributors">
+          <div class="ui basic compact button">
+            <span class="text">
+              {{ locale.filterLabel }} <strong>{{ locale.contributionType[type] }}</strong>
+              <svg-icon name="octicon-triangle-down" :size="14"/>
+            </span>
+          </div>
+          <div class="menu">
+            <div :class="['item', {'active': type === 'commits'}]">
+              {{ locale.contributionType.commits }}
+            </div>
+            <div :class="['item', {'active': type === 'additions'}]">
+              {{ locale.contributionType.additions }}
+            </div>
+            <div :class="['item', {'active': type === 'deletions'}]">
+              {{ locale.contributionType.deletions }}
+            </div>
+          </div>
+        </div>
+      </div>
+    </h2>
+    <div class="gt-df ui segment main-graph">
+      <div v-if="isLoading || errorText !== ''" class="gt-tc gt-m-auto">
+        <div v-if="isLoading">
+          <SvgIcon name="octicon-sync" class="gt-mr-3 job-status-rotate"/>
+          {{ locale.loadingInfo }}
+        </div>
+        <div v-else class="text red">
+          <SvgIcon name="octicon-x-circle-fill"/>
+          {{ errorText }}
+        </div>
+      </div>
+      <ChartLine
+        v-memo="[totalStats.weeks, type]" v-if="Object.keys(totalStats).length !== 0"
+        :data="toGraphData(totalStats.weeks)" :options="getOptions('main')"
+      />
+    </div>
+    <div class="contributor-grid">
+      <div
+        v-for="(contributor, index) in sortedContributors" :key="index" class="stats-table"
+        v-memo="[sortedContributors, type]"
+      >
+        <div class="ui top attached header gt-df gt-f1">
+          <b class="ui right">#{{ index + 1 }}</b>
+          <a :href="contributor.home_link">
+            <img class="ui avatar gt-vm" height="40" width="40" :src="contributor.avatar_link">
+          </a>
+          <div class="gt-ml-3">
+            <a v-if="contributor.home_link !== ''" :href="contributor.home_link"><h4>{{ contributor.name }}</h4></a>
+            <h4 v-else class="contributor-name">
+              {{ contributor.name }}
+            </h4>
+            <p class="gt-font-12 gt-df gt-gap-2">
+              <strong v-if="contributor.total_commits">{{ contributor.total_commits.toLocaleString() }} {{ locale.contributionType.commits }}</strong>
+              <strong v-if="contributor.total_additions" class="text green">{{ contributor.total_additions.toLocaleString() }}++ </strong>
+              <strong v-if="contributor.total_deletions" class="text red">
+                {{ contributor.total_deletions.toLocaleString() }}--</strong>
+            </p>
+          </div>
+        </div>
+        <div class="ui attached segment">
+          <div>
+            <ChartLine
+              :data="toGraphData(contributor.weeks)"
+              :options="getOptions('contributor')"
+            />
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+<style scoped>
+.main-graph {
+  height: 260px;
+}
+.contributor-grid {
+  display: grid;
+  grid-template-columns: repeat(2, 1fr);
+  gap: 1rem;
+}
+
+.contributor-name {
+  margin-bottom: 0;
+}
+</style>
diff --git a/web_src/js/features/contributors.js b/web_src/js/features/contributors.js
new file mode 100644
index 0000000000..66185ac315
--- /dev/null
+++ b/web_src/js/features/contributors.js
@@ -0,0 +1,28 @@
+import {createApp} from 'vue';
+
+export async function initRepoContributors() {
+  const el = document.getElementById('repo-contributors-chart');
+  if (!el) return;
+
+  const {default: RepoContributors} = await import(/* webpackChunkName: "contributors-graph" */'../components/RepoContributors.vue');
+  try {
+    const View = createApp(RepoContributors, {
+      locale: {
+        filterLabel: el.getAttribute('data-locale-filter-label'),
+        contributionType: {
+          commits: el.getAttribute('data-locale-contribution-type-commits'),
+          additions: el.getAttribute('data-locale-contribution-type-additions'),
+          deletions: el.getAttribute('data-locale-contribution-type-deletions'),
+        },
+
+        loadingTitle: el.getAttribute('data-locale-loading-title'),
+        loadingTitleFailed: el.getAttribute('data-locale-loading-title-failed'),
+        loadingInfo: el.getAttribute('data-locale-loading-info'),
+      }
+    });
+    View.mount(el);
+  } catch (err) {
+    console.error('RepoContributors failed to load', err);
+    el.textContent = el.getAttribute('data-locale-component-failed-to-load');
+  }
+}
diff --git a/web_src/js/index.js b/web_src/js/index.js
index 4713618506..078f9fc9df 100644
--- a/web_src/js/index.js
+++ b/web_src/js/index.js
@@ -83,6 +83,7 @@ import {initGiteaFomantic} from './modules/fomantic.js';
 import {onDomReady} from './utils/dom.js';
 import {initRepoIssueList} from './features/repo-issue-list.js';
 import {initCommonIssueListQuickGoto} from './features/common-issue-list.js';
+import {initRepoContributors} from './features/contributors.js';
 import {initRepoDiffCommitBranchesAndTags} from './features/repo-diff-commit.js';
 import {initDirAuto} from './modules/dirauto.js';
 
@@ -172,6 +173,7 @@ onDomReady(() => {
   initRepoWikiForm();
   initRepository();
   initRepositoryActionView();
+  initRepoContributors();
 
   initCommitStatuses();
   initCaptcha();
diff --git a/web_src/js/utils/time.js b/web_src/js/utils/time.js
new file mode 100644
index 0000000000..3284e893e1
--- /dev/null
+++ b/web_src/js/utils/time.js
@@ -0,0 +1,46 @@
+import dayjs from 'dayjs';
+
+// Returns an array of millisecond-timestamps of start-of-week days (Sundays)
+export function startDaysBetween(startDate, endDate) {
+  // Ensure the start date is a Sunday
+  while (startDate.getDay() !== 0) {
+    startDate.setDate(startDate.getDate() + 1);
+  }
+
+  const start = dayjs(startDate);
+  const end = dayjs(endDate);
+  const startDays = [];
+
+  let current = start;
+  while (current.isBefore(end)) {
+    startDays.push(current.valueOf());
+    // we are adding 7 * 24 hours instead of 1 week because we don't want
+    // date library to use local time zone to calculate 1 week from now.
+    // local time zone is problematic because of daylight saving time (dst)
+    // used on some countries
+    current = current.add(7 * 24, 'hour');
+  }
+
+  return startDays;
+}
+
+export function firstStartDateAfterDate(inputDate) {
+  if (!(inputDate instanceof Date)) {
+    throw new Error('Invalid date');
+  }
+  const dayOfWeek = inputDate.getDay();
+  const daysUntilSunday = 7 - dayOfWeek;
+  const resultDate = new Date(inputDate.getTime());
+  resultDate.setDate(resultDate.getDate() + daysUntilSunday);
+  return resultDate.valueOf();
+}
+
+export function fillEmptyStartDaysWithZeroes(startDays, data) {
+  const result = {};
+
+  for (const startDay of startDays) {
+    result[startDay] = data[startDay] || {'week': startDay, 'additions': 0, 'deletions': 0, 'commits': 0};
+  }
+
+  return Object.values(result);
+}
diff --git a/web_src/js/utils/time.test.js b/web_src/js/utils/time.test.js
new file mode 100644
index 0000000000..dd1114ce7f
--- /dev/null
+++ b/web_src/js/utils/time.test.js
@@ -0,0 +1,15 @@
+import {startDaysBetween} from './time.js';
+
+test('startDaysBetween', () => {
+  expect(startDaysBetween(new Date('2024-02-15'), new Date('2024-04-18'))).toEqual([
+    1708214400000,
+    1708819200000,
+    1709424000000,
+    1710028800000,
+    1710633600000,
+    1711238400000,
+    1711843200000,
+    1712448000000,
+    1713052800000,
+  ]);
+});

From b16e26dbeb982f5e2985b9b9d5a1286d798c74b1 Mon Sep 17 00:00:00 2001
From: GiteaBot <teabot@gitea.io>
Date: Fri, 16 Feb 2024 00:23:19 +0000
Subject: [PATCH 07/28] [skip ci] Updated translations via Crowdin

(cherry picked from commit 6d4dc16c726dd0be8d0f56405ba396d44dfd04ac)
---
 options/locale/locale_cs-CZ.ini | 384 ++++++++++++++++++++++++++++++--
 1 file changed, 362 insertions(+), 22 deletions(-)

diff --git a/options/locale/locale_cs-CZ.ini b/options/locale/locale_cs-CZ.ini
index 5b0caf67c9..78268104ff 100644
--- a/options/locale/locale_cs-CZ.ini
+++ b/options/locale/locale_cs-CZ.ini
@@ -5,6 +5,7 @@ explore=Procházet
 help=Nápověda
 logo=Logo
 sign_in=Přihlásit se
+sign_in_with_provider=Přihlásit se pomocí %s
 sign_in_or=nebo
 sign_out=Odhlásit se
 sign_up=Registrovat se
@@ -17,6 +18,7 @@ template=Šablona
 language=Jazyk
 notifications=Oznámení
 active_stopwatch=Aktivní sledování času
+tracked_time_summary=Shrnutí sledovaného času na základě filtrů v seznamu úkolů
 create_new=Vytvořit…
 user_profile_and_more=Profily a nastavení…
 signed_in_as=Přihlášen jako
@@ -80,6 +82,7 @@ milestones=Milníky
 
 ok=OK
 cancel=Zrušit
+retry=Znovu
 rerun=Znovu spustit
 rerun_all=Znovu spustit všechny úlohy
 save=Uložit
@@ -87,14 +90,17 @@ add=Přidat
 add_all=Přidat vše
 remove=Odstranit
 remove_all=Odstranit vše
-remove_label_str=`Odstranit položku "%s"`
+remove_label_str=Odstranit položku „%s“
 edit=Upravit
+view=Zobrazit
 
 enabled=Povolený
 disabled=Zakázané
+locked=Uzamčeno
 
 copy=Kopírovat
 copy_url=Kopírovat URL
+copy_hash=Kopírovat hash
 copy_content=Kopírovat obsah
 copy_branch=Kopírovat jméno větve
 copy_success=Zkopírováno!
@@ -107,6 +113,7 @@ loading=Načítá se…
 
 error=Chyba
 error404=Stránka, kterou se snažíte zobrazit, buď <strong>neexistuje</strong>, nebo <strong>nemáte oprávnění</strong> ji zobrazit.
+go_back=Zpět
 
 never=Nikdy
 unknown=Neznámý
@@ -128,7 +135,9 @@ concept_user_organization=Organizace
 show_timestamps=Zobrazit časové značky
 show_log_seconds=Zobrazit sekundy
 show_full_screen=Zobrazit celou obrazovku
+download_logs=Stáhnout logy
 
+confirm_delete_selected=Potvrdit odstranění všech vybraných položek?
 
 name=Název
 value=Hodnota
@@ -155,7 +164,7 @@ buttons.code.tooltip=Přidat kód
 buttons.link.tooltip=Přidat odkaz
 buttons.list.unordered.tooltip=Přidat seznam odrážek
 buttons.list.ordered.tooltip=Přidat číslovaný seznam
-buttons.list.task.tooltip=Přidat seznam úkolů
+buttons.list.task.tooltip=Přidat seznam úloh
 buttons.mention.tooltip=Uveďte uživatele nebo tým
 buttons.ref.tooltip=Odkaz na issue nebo pull request
 buttons.switch_to_legacy.tooltip=Místo toho použít starší editor
@@ -168,6 +177,7 @@ string.desc=Z – A
 
 [error]
 occurred=Došlo k chybě
+report_message=Pokud jste si jisti, že se jedná o chybu Gitea, prosím vyhledejte problém na <a href="https://github.com/go-gitea/gitea/issues" target="_blank">GitHub</a> a v případě potřeby založte nový problém.
 missing_csrf=Špatný požadavek: Neexistuje CSRF token
 invalid_csrf=Špatný požadavek: Neplatný CSRF token
 not_found=Cíl nebyl nalezen.
@@ -176,6 +186,7 @@ network_error=Chyba sítě
 [startpage]
 app_desc=Snadno přístupný vlastní Git
 install=Jednoduchá na instalaci
+install_desc=Jednoduše <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.com/installation/install-from-binary">spusťte jako binární program</a> pro vaši platformu, nasaďte jej pomocí <a target="_blank" rel="noopener noreferrer" href="https://github.com/go-gitea/gitea/tree/master/docker">Docker</a>, nebo jej stáhněte jako <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.com/installation/install-from-package">balíček</a>.
 platform=Multiplatformní
 platform_desc=Forgejo běží všude, kde <a target="_blank" rel="noopener noreferrer" href="http://golang.org/">Go</a> může kompilovat: Windows, macOS, Linux, ARM, atd. Vyberte si ten, který milujete!
 lightweight=Lehká
@@ -220,6 +231,7 @@ repo_path_helper=Všechny vzdálené repozitáře Gitu budou uloženy do tohoto
 lfs_path=Kořenový adresář Git LFS
 lfs_path_helper=V tomto adresáři budou uloženy soubory, které jsou sledovány Git LFS. Pokud ponecháte prázdné, LFS zakážete.
 run_user=Spustit jako uživatel
+run_user_helper=Zadejte uživatelské jméno, pod kterým Gitea běží v operačním systému. Pozor: tento uživatel musí mít přístup ke kořenovému adresáři repozitářů.
 domain=Doména serveru
 domain_helper=Adresa domény, nebo hostitele serveru.
 ssh_port=Port SSH serveru
@@ -269,7 +281,7 @@ install_btn_confirm=Nainstalovat Forgejo
 test_git_failed=Chyba při testu příkazu 'git': %v
 sqlite3_not_available=Tato verze Forgejo nepodporuje SQLite3. Stáhněte si oficiální binární verzi od %s (nikoli verzi „gobuild“).
 invalid_db_setting=Nastavení databáze je neplatné: %v
-invalid_db_table=Databázová tabulka "%s" je neplatná: %v
+invalid_db_table=Databázová tabulka „%s“ je neplatná: %v
 invalid_repo_path=Kořenový adresář repozitářů není správný: %v
 invalid_app_data_path=Cesta k datům aplikace je neplatná: %v
 run_user_not_match=`"Run as" uživatelské jméno není aktuální uživatelské jméno: %s -> %s`
@@ -291,6 +303,8 @@ invalid_password_algorithm=Neplatný algoritmus hash hesla
 password_algorithm_helper=Nastavte algoritmus hashování hesla. Algoritmy mají odlišné požadavky a sílu. Algoritmus argon2 je poměrně bezpečný, ale používá spoustu paměti a může být nevhodný pro malé systémy.
 enable_update_checker=Povolit kontrolu aktualizací
 enable_update_checker_helper=Kontroluje vydání nových verzí pravidelně připojením ke gitea.io.
+env_config_keys=Konfigurace prostředí
+env_config_keys_prompt=Následující proměnné prostředí budou také použity pro váš konfigurační soubor:
 
 [home]
 uname_holder=Uživatelské jméno nebo e-mailová adresa
@@ -336,7 +350,7 @@ repo_no_results=Nebyly nalezeny žádné odpovídající repozitáře.
 user_no_results=Nebyly nalezeni žádní odpovídající uživatelé.
 org_no_results=Nebyly nalezeny žádné odpovídající organizace.
 code_no_results=Nebyl nalezen žádný zdrojový kód odpovídající hledanému výrazu.
-code_search_results=`Výsledky hledání pro "%s"`
+code_search_results=Výsledky hledání pro „%s“
 code_last_indexed_at=Naposledy indexováno %s
 relevant_repositories_tooltip=Repozitáře, které jsou rozštěpení nebo nemají žádné téma, ikonu a žádný popis jsou skryty.
 relevant_repositories=Zobrazují se pouze relevantní repositáře, <a href="%s">zobrazit nefiltrované výsledky</a>.
@@ -349,9 +363,11 @@ disable_register_prompt=Registrace jsou vypnuty. Prosíme, kontaktujte správce
 disable_register_mail=E-mailové potvrzení o registraci je zakázané.
 manual_activation_only=Pro dokončení aktivace kontaktujte správce webu.
 remember_me=Pamatovat si toto zařízení
+remember_me.compromised=Přihlašovací token již není platný, což může znamenat napadení účtu. Zkontrolujte prosím svůj účet pro neobvyklé aktivity.
 forgot_password_title=Zapomenuté heslo
 forgot_password=Zapomenuté heslo?
 sign_up_now=Potřebujete účet? Zaregistrujte se.
+sign_up_successful=Účet byl úspěšně vytvořen. Vítejte!
 confirmation_mail_sent_prompt=Na adresu <b>%s</b> byl zaslán nový potvrzovací e-mail. Zkontrolujte prosím vaši doručenou poštu během následujících %s, abyste dokončili proces registrace.
 must_change_password=Aktualizujte své heslo
 allow_password_change=Vyžádat od uživatele změnu hesla (doporučeno)
@@ -359,6 +375,7 @@ reset_password_mail_sent_prompt=Na adresu <b>%s</b> byl zaslán potvrzovací e-m
 active_your_account=Aktivujte si váš účet
 account_activated=Účet byl aktivován
 prohibit_login=Přihlášení zakázáno
+prohibit_login_desc=Vašemu účtu je zakázáno se přihlásit, kontaktujte prosím správce webu.
 resent_limit_prompt=Omlouváme se, ale před chvílí jste požádal o zaslání aktivačního e-mailu. Počkejte prosím 3 minuty a pak to zkuste znovu.
 has_unconfirmed_mail=Zdravím, %s, máte nepotvrzenou e-mailovou adresu (<b>%s</b>). Pokud jste nedostali e-mail pro potvrzení nebo potřebujete zaslat nový, klikněte prosím na tlačítku níže.
 resend_mail=Klikněte zde pro odeslání aktivačního e-mailu
@@ -366,8 +383,10 @@ email_not_associate=Tato e-mailová adresa není spojena s žádným účtem.
 send_reset_mail=Zaslat e-mail pro obnovení účtu
 reset_password=Obnovení účtu
 invalid_code=Tento potvrzující kód je neplatný nebo mu vypršela platnost.
+invalid_code_forgot_password=Váš potvrzovací kód je neplatný nebo mu vypršela platnost. <a href="%s">Klikněte zde</a> pro vytvoření nového kódu.
 invalid_password=Vaše heslo se neshoduje s heslem, které bylo použito k vytvoření účtu.
 reset_password_helper=Obnovit účet
+reset_password_wrong_user=Jste přihlášen/a jako %s, ale odkaz pro obnovení účtu je pro %s
 password_too_short=Délka hesla musí být minimálně %d znaků.
 non_local_account=Externě ověřovaní uživatelé nemohou aktualizovat své heslo prostřednictvím webového rozhraní Forgejo.
 verify=Ověřit
@@ -392,6 +411,7 @@ openid_connect_title=Připojení k existujícímu účtu
 openid_connect_desc=Zvolené OpenID URI není známé. Přidružte nový účet zde.
 openid_register_title=Vytvořit nový účet
 openid_register_desc=Zvolené OpenID URI není známé. Přidružte nový účet zde.
+openid_signin_desc=Zadejte vaši OpenID URI. Například: alice.openid.example.org nebo https://openid.example.org/alice.
 disable_forgot_password_mail=Obnovení účtu je zakázáno, protože není nastaven žádný e-mail. Obraťte se na správce webu.
 disable_forgot_password_mail_admin=Obnovení účtu je dostupné pouze po nastavení e-mailu. Pro povolení obnovy účtu nastavte prosím e-mail.
 email_domain_blacklisted=Nemůžete se registrovat s vaší e-mailovou adresou.
@@ -401,7 +421,9 @@ authorize_application_created_by=Tuto aplikaci vytvořil %s.
 authorize_application_description=Pokud povolíte přístup, bude moci přistupovat a zapisovat do všech vašich informací o účtu včetně soukromých repozitářů a organizací.
 authorize_title=Autorizovat „%s“ pro přístup k vašemu účtu?
 authorization_failed=Autorizace selhala
+authorization_failed_desc=Autorizace selhala, protože jsme detekovali neplatný požadavek. Kontaktujte prosím správce aplikace, kterou jste se pokoušeli autorizovat.
 sspi_auth_failed=SSPI autentizace selhala
+password_pwned=Heslo, které jste zvolili, je na <a target="_blank" rel="noopener noreferrer" href="https://haveibeenpwned.com/Passwords">seznamu odcizených hesel</a>, která byla dříve odhalena při narušení veřejných dat. Zkuste to prosím znovu s jiným heslem.
 password_pwned_err=Nelze dokončit požadavek na HaveIBeenPwned
 
 [mail]
@@ -416,6 +438,7 @@ activate_account.text_1=Ahoj <b>%[1]s</b>, děkujeme za registraci na %[2]s!
 activate_account.text_2=Pro aktivaci vašeho účtu do <b>%s</b> klikněte na následující odkaz:
 
 activate_email=Ověřte vaši e-mailovou adresu
+activate_email.title=%s, prosím ověřte vaši e-mailovou adresu
 activate_email.text=Pro aktivaci vašeho účtu do <b>%s</b> klikněte na následující odkaz:
 
 register_notify=Vítejte v Forgejo
@@ -511,6 +534,7 @@ url_error=`„%s“ není platná adresa URL.`
 include_error=` musí obsahovat substring „%s“.`
 glob_pattern_error=`zástupný vzor je neplatný: %s.`
 regex_pattern_error=` regex vzor je neplatný: %s.`
+username_error=` může obsahovat pouze alfanumerické znaky („0-9“, „a-z“, „A-Z“), pomlčku („-“), podtržítka („_“) a tečka („.“). Nemůže začínat nebo končit nealfanumerickými znaky a po sobě jdoucí nealfanumerické znaky jsou také zakázány.`
 invalid_group_team_map_error=` mapování je neplatné: %s`
 unknown_error=Neznámá chyba:
 captcha_incorrect=CAPTCHA kód není správný.
@@ -555,13 +579,20 @@ invalid_ssh_key=Nelze ověřit váš SSH klíč: %s
 invalid_gpg_key=Nelze ověřit váš GPG klíč: %s
 invalid_ssh_principal=Neplatný SSH Principal certifikát: %s
 must_use_public_key=Zadaný klíč je soukromý klíč. Nenahrávejte svůj soukromý klíč nikde. Místo toho použijte váš veřejný klíč.
+unable_verify_ssh_key=Nelze ověřit váš SSH klíč.
 auth_failed=Ověření selhalo: %v
 
+still_own_repo=Váš účet vlastní jeden nebo více repozitářů. Nejprve je smažte nebo převeďte.
+still_has_org=Váš účet je členem jedné nebo více organizací. Nejdříve je musíte opustit.
+still_own_packages=Váš účet vlastní jeden nebo více balíčků. Nejprve je musíte odstranit.
+org_still_own_repo=Organizace stále vlastní jeden nebo více repozitářů. Nejdříve je smažte nebo převeďte.
+org_still_own_packages=Organizace stále vlastní jeden nebo více balíčků. Nejdříve je smažte.
 
 target_branch_not_exist=Cílová větev neexistuje.
 
 [user]
 change_avatar=Změnit váš avatar…
+joined_on=Přidal/a se %s
 repositories=Repozitáře
 activity=Veřejná aktivita
 followers=Sledující
@@ -577,10 +608,12 @@ user_bio=Životopis
 disabled_public_activity=Tento uživatel zakázal veřejnou viditelnost aktivity.
 email_visibility.limited=Vaše e-mailová adresa je viditelná pro všechny ověřené uživatele
 email_visibility.private=Vaše e-mailová adresa je viditelná pouze pro vás a administrátory
+show_on_map=Zobrazit toto místo na mapě
+settings=Uživatelská nastavení
 
-form.name_reserved=Uživatelské jméno "%s" je rezervováno.
-form.name_pattern_not_allowed=Vzor "%s" není povolen v uživatelském jméně.
-form.name_chars_not_allowed=Uživatelské jméno "%s" obsahuje neplatné znaky.
+form.name_reserved=Uživatelské jméno „%s“ je rezervováno.
+form.name_pattern_not_allowed=Vzor „%s“ není povolen v uživatelském jméně.
+form.name_chars_not_allowed=Uživatelské jméno „%s“ obsahuje neplatné znaky.
 
 [settings]
 profile=Profil
@@ -598,9 +631,13 @@ delete=Smazat účet
 twofa=Dvoufaktorové ověřování
 account_link=Propojené účty
 organization=Organizace
+uid=UID
 webauthn=Bezpečnostní klíče
 
 public_profile=Veřejný profil
+biography_placeholder=Řekněte nám něco o sobě! (Můžete použít Markdown)
+location_placeholder=Sdílejte svou přibližnou polohu s ostatními
+profile_desc=Nastavte, jak bude váš profil zobrazen ostatním uživatelům. Vaše hlavní e-mailová adresa bude použita pro oznámení, obnovení hesla a operace Git.
 password_username_disabled=Externí uživatelé nemohou měnit svoje uživatelské jméno. Kontaktujte prosím svého administrátora pro více detailů.
 full_name=Celé jméno
 website=Web
@@ -608,15 +645,20 @@ location=Místo
 update_theme=Aktualizovat motiv vzhledu
 update_profile=Aktualizovat profil
 update_language=Aktualizovat jazyk
-update_language_not_found=Jazyk "%s" není k dispozici.
+update_language_not_found=Jazyk „%s“ není k dispozici.
 update_language_success=Jazyk byl aktualizován.
 update_profile_success=Váš profil byl aktualizován.
 change_username=Vaše uživatelské jméno bylo změněno.
+change_username_prompt=Poznámka: Změna uživatelského jména také změní URL vašeho účtu.
+change_username_redirect_prompt=Staré uživatelské jméno bude přesměrováváno, dokud nebude znovu obsazeno.
 continue=Pokračovat
 cancel=Zrušit
 language=Jazyk
 ui=Motiv vzhledu
 hidden_comment_types=Skryté typy komentářů
+hidden_comment_types_description=Zde zkontrolované typy komentářů nebudou zobrazeny na stránkách problémů. Zaškrtnutí „Štítek“ například odstraní všechny komentáře „<user> přidal/odstranil <label>“.
+hidden_comment_types.ref_tooltip=Komentáře, na které se odkazovalo z jiného úkolu/commitu/…
+hidden_comment_types.issue_ref_tooltip=Komentáře, kde uživatel změní větev/značku spojenou s problémem
 comment_type_group_reference=Reference
 comment_type_group_label=Štítek
 comment_type_group_milestone=Milník
@@ -633,6 +675,7 @@ comment_type_group_project=Projekt
 comment_type_group_issue_ref=Referenční číslo úkolu
 saved_successfully=Vaše nastavení bylo úspěšně uloženo.
 privacy=Soukromí
+keep_activity_private=Skrýt aktivitu z profilové stránky
 keep_activity_private_popup=Učinit aktivitu viditelnou pouze pro vás a administrátory
 
 lookup_avatar_by_mail=Vyhledat avatar pomocí e-mailové adresy
@@ -642,12 +685,14 @@ choose_new_avatar=Vybrat nový avatar
 update_avatar=Aktualizovat avatar
 delete_current_avatar=Smazat aktuální avatar
 uploaded_avatar_not_a_image=Nahraný soubor není obrázek.
+uploaded_avatar_is_too_big=Nahraný soubor (%d KiB) přesahuje maximální velikost (%d KiB).
 update_avatar_success=Vaše avatar byl aktualizován.
 update_user_avatar_success=Uživatelův avatar byl aktualizován.
 
 change_password=Aktualizovat heslo
 old_password=Stávající heslo
 new_password=Nové heslo
+retype_new_password=Potvrdit nové heslo
 password_incorrect=Zadané heslo není správné.
 change_password_success=Vaše heslo bylo aktualizováno. Od teď se přihlašujte novým heslem.
 password_change_disabled=Externě ověřovaní uživatelé nemohou aktualizovat své heslo prostřednictvím webového rozhraní Forgejo.
@@ -656,6 +701,7 @@ emails=E-mailová adresa
 manage_emails=Správa e-mailových adres
 manage_themes=Vyberte výchozí motiv vzhledu
 manage_openid=Správa OpenID adres
+email_desc=Vaše hlavní e-mailová adresa bude použita pro oznámení, obnovení hesla, a pokud není skrytá, pro operace Gitu.
 theme_desc=Toto bude váš výchozí motiv vzhledu napříč stránkou.
 primary=Hlavní
 activated=Aktivován
@@ -663,6 +709,7 @@ requires_activation=Vyžaduje aktivaci
 primary_email=Nastavit jako hlavní
 activate_email=Odeslat aktivaci
 activations_pending=Čekající aktivace
+can_not_add_email_activations_pending=Existuje čekající aktivace, zkuste to znovu za pár minut, pokud chcete přidat nový e-mail.
 delete_email=Smazat
 email_deletion=Odstranit e-mailovou adresu
 email_deletion_desc=E-mailová adresa a přidružené informace budou z vašeho účtu odstraněny. Commity Gitu s touto e-mailovou adresou zůstanou nezměněny. Pokračovat?
@@ -676,10 +723,12 @@ add_new_email=Přidat novou e-mailovou adresu
 add_new_openid=Přidat novou OpenID URI
 add_email=Přidat e-mailovou adresu
 add_openid=Přidat OpenID URI
+add_email_confirmation_sent=Potvrzovací e-mail byl odeslán na „%s“. Prosím zkontrolujte příchozí poštu během následujících %s pro potvrzení vaší e-mailové adresy.
 add_email_success=Nová e-mailová adresa byla přidána.
 email_preference_set_success=Nastavení e-mailu bylo úspěšně nastaveno.
 add_openid_success=Nová OpenID adresa byla přidána.
 keep_email_private=Schovat e-mailovou adresu
+keep_email_private_popup=Toto skryje vaši e-mailovou adresu z vašeho profilu, stejně jako při vytvoření pull requestu nebo úpravě souboru pomocí webového rozhraní. Odeslané commity nebudou změněny. Použijte %s v commitech pro jejich přiřazení k vašemu účtu.
 openid_desc=OpenID vám umožní delegovat ověřování na externího poskytovatele.
 
 manage_ssh_keys=Správa klíčů SSH
@@ -713,7 +762,7 @@ gpg_token_help=Podpis můžete vygenerovat pomocí:
 gpg_token_code=echo "%s" | gpg -a --default-key %s --detach-sig
 gpg_token_signature=Zakódovaný podpis GPG
 key_signature_gpg_placeholder=Začíná s „-----BEGIN PGP SIGNATURE-----“
-verify_gpg_key_success=GPG klíč "%s" byl ověřen.
+verify_gpg_key_success=GPG klíč „%s“ byl ověřen.
 ssh_key_verified=Ověřený klíč
 ssh_key_verified_long=Klíč byl ověřen pomocí tokenu a může být použit k ověření commitů shodujících se s libovolnou vaší aktivovanou e-mailovou adresou pro tohoto uživatele.
 ssh_key_verify=Ověřit
@@ -723,14 +772,15 @@ ssh_token=Token
 ssh_token_help=Podpis můžete vygenerovat pomocí:
 ssh_token_signature=Zakódovaný podpis SSH
 key_signature_ssh_placeholder=Začíná s „-----BEGIN SSH SIGNATURE-----“
-verify_ssh_key_success=SSH klíč "%s" byl ověřen.
+verify_ssh_key_success=SSH klíč „%s“ byl ověřen.
 subkeys=Podklíče
 key_id=ID klíče
 key_name=Název klíče
 key_content=Obsah
 principal_content=Obsah
-add_key_success=SSH klíč "%s" byl přidán.
-add_gpg_key_success=GPG klíč "%s" byl přidán.
+add_key_success=SSH klíč „%s“ byl přidán.
+add_gpg_key_success=GPG klíč „%s“ byl přidán.
+add_principal_success=Byl přidán SSH Principal certifikát „%s“.
 delete_key=Odstranit
 ssh_key_deletion=Odstraňte SSH klíč
 gpg_key_deletion=Odstraňte GPG klíč
@@ -757,7 +807,9 @@ ssh_disabled=SSH zakázáno
 ssh_signonly=SSH je v současné době zakázáno, proto jsou tyto klíče použity pouze pro ověření podpisu.
 ssh_externally_managed=Tento SSH klíč je spravován externě pro tohoto uživatele
 manage_social=Správa propojených účtů sociálních sítí
+social_desc=Tyto účty sociálních sítí lze použít k přihlášení k vašemu účtu. Ujistěte se, že jsou všechny vaše.
 unbind=Odpojit
+unbind_success=Účet sociální sítě byl úspěšně odstraněn.
 
 manage_access_token=Spravovat přístupové tokeny
 generate_new_token=Vygenerovat nový token
@@ -778,6 +830,7 @@ permissions_access_all=Vše (veřejné, soukromé a omezené)
 select_permissions=Vyberte oprávnění
 permission_no_access=Bez přístupu
 permission_read=Přečtené
+permission_write=čtení i zápis
 at_least_one_permission=Musíte vybrat alespoň jedno oprávnění pro vytvoření tokenu
 permissions_list=Oprávnění:
 
@@ -789,6 +842,8 @@ remove_oauth2_application_desc=Odstraněním OAuth2 aplikace odeberete přístup
 remove_oauth2_application_success=Aplikace byla odstraněna.
 create_oauth2_application=Vytvořit novou OAuth2 aplikaci
 create_oauth2_application_button=Vytvořit aplikaci
+create_oauth2_application_success=Úspěšně jste vytvořili novou OAuth2 aplikaci.
+update_oauth2_application_success=Úspěšně jste aktualizovali OAuth2 aplikaci.
 oauth2_application_name=Název aplikace
 oauth2_confidential_client=Důvěrný klient. Vyberte aplikace, které zachovávají důvěrnosti v utajení, jako jsou webové aplikace. Nevybírejte pro nativní aplikace včetně stolních a mobilních aplikací.
 oauth2_redirect_uris=Přesměrování URI. Použijte nový řádek pro každou URI.
@@ -797,19 +852,26 @@ oauth2_client_id=ID klienta
 oauth2_client_secret=Tajný klíč klienta
 oauth2_regenerate_secret=Obnovit tajný klíč
 oauth2_regenerate_secret_hint=Ztratili jste svůj tajný klíč?
+oauth2_client_secret_hint=Tajný klíč se znovu nezobrazí po opuštění nebo obnovení této stránky. Ujistěte se, že jste si jej uložili.
 oauth2_application_edit=Upravit
 oauth2_application_create_description=OAuth2 aplikace poskytuje přístup aplikacím třetích stran k uživatelským účtům na této instanci.
+oauth2_application_remove_description=Odebráním OAuth2 aplikace zabrání přístupu ověřeným uživatelům na této instanci. Pokračovat?
+oauth2_application_locked=Gitea předregistruje některé OAuth2 aplikace při spuštění, pokud je to povoleno v konfiguraci. Aby se zabránilo neočekávanému chování, nelze je upravovat ani odstranit. Více informací naleznete v dokumentaci OAuth2.
 
 authorized_oauth2_applications=Autorizovat OAuth2 aplikaci
+authorized_oauth2_applications_description=Úspěšně jste povolili přístup k vašemu osobnímu účtu této aplikaci třetí strany. Zrušte prosím přístup aplikacím, které již nadále nepotřebujete.
 revoke_key=Zrušit
 revoke_oauth2_grant=Zrušit přístup
 revoke_oauth2_grant_description=Zrušením přístupu této aplikaci třetí strany ji zabráníte v přístupu k vašim datům. Jste si jisti?
+revoke_oauth2_grant_success=Přístup byl úspěšně zrušen.
 
 twofa_desc=Dvoufaktorový způsob ověřování zvýší zabezpečení vašeho účtu.
+twofa_recovery_tip=Pokud ztratíte své zařízení, budete moci použít jednorázový obnovovací klíč k získání přístupu k vašemu účtu.
 twofa_is_enrolled=Váš účet aktuálně <strong>používá</strong> dvoufaktorové ověřování.
 twofa_not_enrolled=Váš účet aktuálně nepoužívá dvoufaktorové ověřování.
 twofa_disable=Zakázat dvoufaktorové ověřování
 twofa_scratch_token_regenerate=Obnovit pomocný token
+twofa_scratch_token_regenerated=Váš jednorázový obnovovací klíč je nyní %s. Uložte jej na bezpečném místě, protože se znovu nezobrazí.
 twofa_enroll=Povolit dvoufaktorové ověřování
 twofa_disable_note=Dvoufaktorové ověřování můžete zakázat, když bude potřeba.
 twofa_disable_desc=Zakážete-li dvoufaktorové ověřování, bude váš účet méně zabezpečený. Pokračovat?
@@ -827,6 +889,8 @@ webauthn_register_key=Přidat bezpečnostní klíč
 webauthn_nickname=Přezdívka
 webauthn_delete_key=Odstranit bezpečnostní klíč
 webauthn_delete_key_desc=Pokud odstraníte bezpečnostní klíč, již se s ním nebudete moci přihlásit. Pokračovat?
+webauthn_key_loss_warning=Pokud ztratíte své bezpečnostní klíče, ztratíte přístup k vašemu účtu.
+webauthn_alternative_tip=Možná budete chtít nakonfigurovat další metodu ověřování.
 
 manage_account_links=Správa propojených účtů
 manage_account_links_desc=Tyto externí účty jsou propojeny s vaším Forgejo účtem.
@@ -836,8 +900,10 @@ remove_account_link=Odstranit propojený účet
 remove_account_link_desc=Odstraněním propojeného účtu zrušíte jeho přístup k vašemu Forgejo účtu. Pokračovat?
 remove_account_link_success=Propojený účet byl odstraněn.
 
+hooks.desc=Přidat webhooky, které budou spouštěny pro <strong>všechny repozitáře</strong> vve vašem vlastnictví.
 
 orgs_none=Nejste členem žádné organizace.
+repos_none=Nevlastníte žádné repozitáře.
 
 delete_account=Smazat váš účet
 delete_prompt=Tato operace natrvalo odstraní váš uživatelský účet. <strong>NELZE</strong> ji vrátit zpět.
@@ -856,9 +922,12 @@ visibility=Viditelnost uživatele
 visibility.public=Veřejný
 visibility.public_tooltip=Viditelné pro všechny
 visibility.limited=Omezený
+visibility.limited_tooltip=Viditelné pouze pro ověřené uživatele
 visibility.private=Soukromý
+visibility.private_tooltip=Viditelné pouze pro členy organizací, ke kterým jste se připojili
 
 [repo]
+new_repo_helper=Repozitář obsahuje všechny projektové soubory, včetně historie revizí. Už jej hostujete jinde? <a href="%s">Migrovat repozitář.</a>
 owner=Vlastník
 owner_helper=Některé organizace se nemusejí v seznamu zobrazit kvůli maximálnímu dosaženému počtu repozitářů.
 repo_name=Název repozitáře
@@ -870,6 +939,7 @@ template_helper=Z repozitáře vytvořit šablonu
 template_description=Šablony repozitářů umožňují uživatelům generovat nové repositáře se stejnou strukturou, soubory a volitelnými nastaveními.
 visibility=Viditelnost
 visibility_description=Pouze majitelé nebo členové organizace to budou moci vidět, pokud mají práva.
+visibility_helper=Nastavit repozitář jako soukromý
 visibility_helper_forced=Váš administrátor vynutil, že nové repozitáře budou soukromé.
 visibility_fork_helper=(Změna tohoto ovlivní všechny rozštěpení repozitáře.)
 clone_helper=Potřebujete pomoci s klonováním? Navštivte <a target="_blank" rel="noopener noreferrer" href="%s">nápovědu</a>.
@@ -878,6 +948,9 @@ fork_from=Rozštěpit z
 already_forked=Již jsi rozštěpil %s
 fork_to_different_account=Rozštěpit na jiný účet
 fork_visibility_helper=Viditelnost rozštěpeného repozitáře nemůže být změněna.
+fork_branch=Větev, která má být klonována pro fork
+all_branches=Všechny větve
+fork_no_valid_owners=Tento repozitář nemůže být rozštěpen, protože neexistují žádní platní vlastníci.
 use_template=Použít tuto šablonu
 clone_in_vsc=Klonovat ve VS Code
 download_zip=Stáhnout ZIP
@@ -906,6 +979,7 @@ trust_model_helper_collaborator_committer=Spolupracovník+Přispěvatel: Důvě
 trust_model_helper_default=Výchozí: Použít výchozí model důvěry pro tuto instalaci
 create_repo=Vytvořit repozitář
 default_branch=Výchozí větev
+default_branch_label=výchozí
 default_branch_helper=Výchozí větev je základní větev pro požadavky na natažení a commity kódu.
 mirror_prune=Vyčistit
 mirror_prune_desc=Odstranit zastaralé reference na vzdálené sledování
@@ -914,6 +988,8 @@ mirror_interval_invalid=Interval zrcadlení není platný.
 mirror_sync_on_commit=Synchronizovat při nahrávání revizí
 mirror_address=Klonovat z URL
 mirror_address_desc=Zadejte požadované přístupové údaje do sekce Ověření.
+mirror_address_url_invalid=Poskytnutá URL je neplatná. Všechny části musíte správně nahradit escape sekvencí.
+mirror_address_protocol_invalid=Zadaná URL je neplatná. Mohou být zrcadleny pouze umístění http(s):// nebo git://.
 mirror_lfs=Úložiště velkých souborů (LFS)
 mirror_lfs_desc=Aktivovat zrcadlení dat LFS.
 mirror_lfs_endpoint=Koncový bod LFS
@@ -939,13 +1015,19 @@ delete_preexisting=Odstranit již existující soubory
 delete_preexisting_content=Odstranit soubory v %s
 delete_preexisting_success=Smazány nepřijaté soubory v %s
 blame_prior=Zobrazit blame před touto změnou
+blame.ignore_revs.failed=Nepodařilo se ignorovat revize v <a href="%s">.git-blame-ignore-revs</a>.
 author_search_tooltip=Zobrazí maximálně 30 uživatelů
 
+tree_path_not_found_commit=Cesta %[1]s v commitu %[2]s neexistuje
+tree_path_not_found_branch=Cesta %[1]s ve větvi %[2]s neexistuje
+tree_path_not_found_tag=Cesta %[1]s ve značce %[2]s neexistuje
 
 transfer.accept=Přijmout převod
 transfer.accept_desc=Převést do „%s“
 transfer.reject=Odmítnout převod
 transfer.reject_desc=Zrušit převod do „%s“
+transfer.no_permission_to_accept=Nemáte oprávnění k přijetí tohoto převodu.
+transfer.no_permission_to_reject=Nemáte oprávnění k odmítnutí tohoto převodu.
 
 desc.private=Soukromý
 desc.public=Veřejný
@@ -964,12 +1046,15 @@ template.issue_labels=Štítky úkolů
 template.one_item=Musíte vybrat alespoň jednu položku šablony
 template.invalid=Musíte vybrat repositář šablony
 
+archive.title=Tento repozitář je archivovaný. Můžete prohlížet soubory, klonovat, ale nemůžete nahrávat a vytvářet nové úkoly nebo požadavky na natažení.
+archive.title_date=Tento repositář byl archivován %s. Můžete zobrazit soubory a klonovat je, ale nemůžete nahrávat ani otevírat problémy nebo požadavky na natažení.
 archive.issue.nocomment=Tento repozitář je archivovaný. Nemůžete komentovat úkoly.
 archive.pull.nocomment=Tento repozitář je archivovaný. Nemůžete komentovat požadavky na natažení.
 
 form.reach_limit_of_creation_1=Již jste dosáhli svůj limit %d repozitář.
 form.reach_limit_of_creation_n=Již jste dosáhli svůj limit %d repozitářů.
 form.name_reserved=Název repozitáře „%s“ je rezervován.
+form.name_pattern_not_allowed=Vzor „%s“ není povolený v názvu repozitáře.
 
 need_auth=Ověření
 migrate_options=Možnosti migrace
@@ -979,6 +1064,7 @@ migrate_options_lfs=Migrovat LFS soubory
 migrate_options_lfs_endpoint.label=Koncový bod LFS
 migrate_options_lfs_endpoint.description=Migrace se pokusí použít váš vzdálený Git pro <a target="_blank" rel="noopener noreferrer" href="%s">určení LFS serveru</a>. Můžete také zadat vlastní koncový bod, pokud jsou data LFS repozitáře uložena někde jinde.
 migrate_options_lfs_endpoint.description.local=Podporována je také cesta k lokálnímu serveru.
+migrate_options_lfs_endpoint.placeholder=Ponecháte-li prázdné, koncový bod bude odvozen z adresy URL klonu
 migrate_items=Položky pro migrování
 migrate_items_wiki=Wiki
 migrate_items_milestones=Milníky
@@ -994,6 +1080,7 @@ migrate.github_token_desc=Můžete sem vložit jeden nebo více tokenů oddělen
 migrate.clone_local_path=nebo místní cesta serveru
 migrate.permission_denied=Není dovoleno importovat místní repozitáře.
 migrate.permission_denied_blocked=Nelze importovat z nepovolených hostitelů, prosím požádejte správce, aby zkontroloval nastavení ALLOWED_DOMAINS/ALLOW_LOCALETWORKS/BLOCKED_DOMAINS.
+migrate.invalid_local_path=Místní cesta je neplatná, buď neexistuje nebo není adresářem.
 migrate.invalid_lfs_endpoint=Koncový bod LFS není platný.
 migrate.failed=Přenesení selhalo: %v
 migrate.migrate_items_options=Pro migraci dalších položek je vyžadován přístupový token
@@ -1071,6 +1158,7 @@ release=Vydání
 releases=Vydání
 tag=Značka
 released_this=vydal/a toto
+tagged_this=označil/a
 file.title=%s v %s
 file_raw=Surový
 file_history=Historie
@@ -1079,6 +1167,10 @@ file_view_rendered=Zobrazit vykreslené
 file_view_raw=Zobrazit v surovém stavu
 file_permalink=Trvalý odkaz
 file_too_large=Soubor je příliš velký pro zobrazení.
+invisible_runes_header=`Tento soubor obsahuje neviditelné znaky Unicode`
+invisible_runes_description=`Tento soubor obsahuje neviditelné Unicode znaky, které jsou pro člověka nerozeznatelné, ale mohou být zpracovány jiným způsobem. Pokud si myslíte, že je to záměrné, můžete toto varování bezpečně ignorovat. Použijte tlačítko Escape sekvence k jejich zobrazení.`
+ambiguous_runes_header=`Tento soubor obsahuje nejednoznačné znaky Unicode`
+ambiguous_runes_description=`Tento soubor obsahuje znaky Unicode, které mohou být zaměněny s jinými znaky. Pokud si myslíte, že je to záměrné, můžete toto varování bezpečně ignorovat. Použijte tlačítko Escape sekvence k jejich zobrazení.`
 invisible_runes_line=`Tento řádek má neviditelné znaky Unicode`
 ambiguous_runes_line=`Tento řádek má nejednoznačné znaky Unicode`
 ambiguous_character=`%[1]c [U+%04[1]X] je zaměnitelný s %[2]c [U+%04[2]X]`
@@ -1091,11 +1183,15 @@ video_not_supported_in_browser=Váš prohlížeč nepodporuje značku pro HTML5
 audio_not_supported_in_browser=Váš prohlížeč nepodporuje značku pro HTML5 audio.
 stored_lfs=Uloženo pomocí Git LFS
 symbolic_link=Symbolický odkaz
+executable_file=Spustitelný soubor
 commit_graph=Graf commitů
 commit_graph.select=Vybrat větve
 commit_graph.hide_pr_refs=Skrýt požadavky na natažení
 commit_graph.monochrome=Černobílé
 commit_graph.color=Barva
+commit.contained_in=Tento commit je obsažen v:
+commit.contained_in_default_branch=Tento commit je součástí výchozí větve
+commit.load_referencing_branches_and_tags=Načíst větve a značky odkazující na tento commit
 blame=Blame
 download_file=Stáhnout soubor
 normal_view=Normální zobrazení
@@ -1129,6 +1225,7 @@ editor.update=Aktualizovat %s
 editor.delete=Odstranit %s
 editor.patch=Použít záplatu
 editor.patching=Záplatování:
+editor.fail_to_apply_patch=Nelze použít záplatu „%s“
 editor.new_patch=Nová záplata
 editor.commit_message_desc=Přidat volitelný rozšířený popis…
 editor.signoff_desc=Přidat Signed-off-by podpis přispěvatele na konec zprávy o commitu.
@@ -1143,7 +1240,13 @@ editor.filename_cannot_be_empty=Jméno nemůže být prázdné.
 editor.filename_is_invalid=Název souboru je neplatný: „%s“.
 editor.branch_does_not_exist=Větev „%s“ v tomto repozitáři neexistuje.
 editor.branch_already_exists=Větev „%s“ již existuje v tomto repozitáři.
+editor.directory_is_a_file=Jméno adresáře „%s“ je již použito jako jméno souboru v tomto repozitáři.
+editor.file_is_a_symlink=`„%s“ je symbolický odkaz. Symbolické odkazy nemohou být upravovány ve webovém editoru`
+editor.filename_is_a_directory=Jméno souboru „%s“ je již použito jako jméno adresáře v tomto repozitáři.
+editor.file_editing_no_longer_exists=Upravovaný soubor „%s“ již není součástí tohoto repozitáře.
+editor.file_deleting_no_longer_exists=Odstraňovaný soubor „%s“ již není součástí tohoto repozitáře.
 editor.file_changed_while_editing=Obsah souboru byl změněn od doby, kdy jste začaly s úpravou. <a target="_blank" rel="noopener noreferrer" href="%s">Klikněte zde</a>, abyste je zobrazili, nebo <strong>potvrďte změny ještě jednou</strong> pro jejich přepsání.
+editor.file_already_exists=Soubor „%s“ již existuje v tomto repozitáři.
 editor.commit_empty_file_header=Odevzdat prázdný soubor
 editor.commit_empty_file_text=Soubor, který se chystáte odevzdat, je prázdný. Pokračovat?
 editor.no_changes_to_show=Žádné změny k zobrazení.
@@ -1165,8 +1268,10 @@ editor.revert=Vrátit %s na:
 
 commits.desc=Procházet historii změn zdrojového kódu.
 commits.commits=Commity
+commits.no_commits=Žádné společné commity. „%s“ a „%s“ mají zcela odlišnou historii.
 commits.nothing_to_compare=Tyto větve jsou stejné.
 commits.search=Hledání commitů…
+commits.search.tooltip=Můžete předřadit klíčová slova s „author:“, „committer:“, „after:“ nebo „before:“, např. „revert author:Alice before:2019-01-03“.
 commits.find=Vyhledat
 commits.search_all=Všechny větve
 commits.author=Autor
@@ -1179,6 +1284,7 @@ commits.signed_by_untrusted_user=Podepsáno nedůvěryhodným uživatelem
 commits.signed_by_untrusted_user_unmatched=Podepsáno nedůvěryhodným uživatelem, který nesouhlasí s přispěvatelem
 commits.gpg_key_id=ID GPG klíče
 commits.ssh_key_fingerprint=Otisk klíče SSH
+commits.view_path=Zobrazit v tomto bodě v historii
 
 commit.operations=Operace
 commit.revert=Vrátit
@@ -1204,12 +1310,14 @@ projects.create=Vytvořit projekt
 projects.title=Název
 projects.new=Nový projekt
 projects.new_subheader=Koordinujte, sledujte a aktualizujte svou práci na jednom místě, aby projekty zůstaly transparentní a v plánu.
+projects.create_success=Projekt „%s“ byl vytvořen.
 projects.deletion=Odstranit projekt
 projects.deletion_desc=Odstranění projektu jej odstraní ze všech souvisejících úkolů. Pokračovat?
 projects.deletion_success=Projekt byl odstraněn.
 projects.edit=Upravit projekty
 projects.edit_subheader=Projekty organizují úkoly a sledují pokrok.
 projects.modify=Aktualizovat projekt
+projects.edit_success=Projekt „%s“ byl aktualizován.
 projects.type.none=Žádný
 projects.type.basic_kanban=Základní Kanban
 projects.type.bug_triage=Třídění chyb
@@ -1235,7 +1343,7 @@ projects.card_type.desc=Náhledy karet
 projects.card_type.images_and_text=Obrázky a text
 projects.card_type.text_only=Pouze text
 
-issues.desc=Organizování hlášení chyb, úkolů a milníků.
+issues.desc=Organizování hlášení chyb, úloh a milníků.
 issues.filter_assignees=Filtrovat zpracovatele
 issues.filter_milestones=Filtrovat milník
 issues.filter_projects=Filtrovat projekt
@@ -1267,6 +1375,7 @@ issues.choose.blank=Výchozí
 issues.choose.blank_about=Vytvořit úkol z výchozí šablony.
 issues.choose.ignore_invalid_templates=Neplatné šablony byly ignorovány
 issues.choose.invalid_templates=%v nalezených neplatných šablon
+issues.choose.invalid_config=Nastavení problému obsahuje chyby:
 issues.no_ref=Není určena žádná větev/značka
 issues.create=Vytvořit úkol
 issues.new_label=Nový štítek
@@ -1303,6 +1412,7 @@ issues.delete_branch_at=`odstranil/a větev <b>%s</b> %s`
 issues.filter_label=Štítek
 issues.filter_label_exclude=`Chcete-li vyloučit štítky, použijte <code>alt</code> + <code>click/enter</code>`
 issues.filter_label_no_select=Všechny štítky
+issues.filter_label_select_no_label=Bez štítku
 issues.filter_milestone=Milník
 issues.filter_milestone_all=Všechny milníky
 issues.filter_milestone_none=Žádné milníky
@@ -1336,6 +1446,7 @@ issues.filter_sort.moststars=Nejvíce hvězdiček
 issues.filter_sort.feweststars=Nejméně hvězdiček
 issues.filter_sort.mostforks=Nejvíce rozštěpení
 issues.filter_sort.fewestforks=Nejméně rozštěpení
+issues.keyword_search_unavailable=Hledání podle klíčového slova není momentálně dostupné. Obraťte se na správce webu.
 issues.action_open=Otevřít
 issues.action_close=Zavřít
 issues.action_label=Štítek
@@ -1356,6 +1467,7 @@ issues.next=Další
 issues.open_title=otevřený
 issues.closed_title=zavřený
 issues.draft_title=Koncept
+issues.num_comments_1=%d komentář
 issues.num_comments=%d komentářů
 issues.commented_at=`okomentoval <a href="#%s">%s</a>`
 issues.delete_comment_confirm=Jste si jist, že chcete smazat tento komentář?
@@ -1364,7 +1476,9 @@ issues.context.quote_reply=Citovat odpověď
 issues.context.reference_issue=Odkázat v novém úkolu
 issues.context.edit=Upravit
 issues.context.delete=Smazat
+issues.no_content=K dispozici není žádný popis.
 issues.close=Zavřít problém
+issues.comment_pull_merged_at=sloučený commit %[1]s do %[2]s %[3]s
 issues.comment_manually_pull_merged_at=ručně sloučený commit %[1]s do %[2]s %[3]s
 issues.close_comment_issue=Okomentovat a zavřít
 issues.reopen_issue=Znovuotevřít
@@ -1381,8 +1495,16 @@ issues.ref_closed_from=`<a href="%[3]s">uzavřel/a tento úkol %[4]s</a> <a id="
 issues.ref_reopened_from=`<a href="%[3]s">znovu otevřel/a tento úkol %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
 issues.ref_from=`z %[1]s`
 issues.author=Autor
+issues.author_helper=Tento uživatel je autor.
 issues.role.owner=Vlastník
+issues.role.owner_helper=Tento uživatel je vlastníkem tohoto repozitáře.
 issues.role.member=Člen
+issues.role.member_helper=Tento uživatel je členem organizace vlastnící tento repositář.
+issues.role.collaborator=Spolupracovník
+issues.role.collaborator_helper=Tento uživatel byl pozván ke spolupráci v repozitáři.
+issues.role.first_time_contributor_helper=Toto je první příspěvek tohoto uživatele do repozitáře.
+issues.role.contributor=Přispěvatel
+issues.role.contributor_helper=Tento uživatel již dříve přispíval do repozitáře.
 issues.re_request_review=Znovu požádat o posouzení
 issues.is_stale=Od tohoto posouzení došlo ke změnám v tomto požadavku na natažení
 issues.remove_request_review=Odstranit žádost o posouzení
@@ -1397,6 +1519,7 @@ issues.label_title=Název štítku
 issues.label_description=Popis štítku
 issues.label_color=Barva štítku
 issues.label_exclusive=Exkluzivní
+issues.label_archived_filter=Zobrazit archivované popisky
 issues.label_count=%d štítků
 issues.label_open_issues=%d otevřených úkolů
 issues.label_edit=Upravit
@@ -1449,6 +1572,7 @@ issues.tracking_already_started=`Již jste spustili sledování času na <a href
 issues.stop_tracking=Zastavit časovač
 issues.stop_tracking_history=`ukončil/a práci %s`
 issues.cancel_tracking=Zahodit
+issues.cancel_tracking_history=`zrušil/a sledování času %s`
 issues.add_time=Přidat čas ručně
 issues.del_time=Odstranit tento časový záznam
 issues.add_time_short=Přidat čas
@@ -1472,6 +1596,7 @@ issues.due_date_form=rrrr-mm-dd
 issues.due_date_form_add=Přidat termín dokončení
 issues.due_date_form_edit=Upravit
 issues.due_date_form_remove=Odstranit
+issues.due_date_not_writer=Potřebujete přístup k zápisu do tohoto repozitáře, abyste mohli aktualizovat datum dokončení problému.
 issues.due_date_not_set=Žádný termín dokončení.
 issues.due_date_added=přidal/a termín dokončení %s %s
 issues.due_date_modified=upravil/a termín termínu z %[2]s na %[1]s %[3]s
@@ -1526,6 +1651,9 @@ issues.review.pending.tooltip=Tento komentář není momentálně viditelný pro
 issues.review.review=Posouzení
 issues.review.reviewers=Posuzovatelé
 issues.review.outdated=Zastaralé
+issues.review.outdated_description=Obsah se změnil od chvíle, kdy byl tento komentář vytvořen
+issues.review.option.show_outdated_comments=Zobrazit zastaralé komentáře
+issues.review.option.hide_outdated_comments=Skrýt zastaralé komentáře
 issues.review.show_outdated=Zobrazit zastaralé
 issues.review.hide_outdated=Skrýt zastaralé
 issues.review.show_resolved=Zobrazit vyřešené
@@ -1565,6 +1693,13 @@ pulls.switch_comparison_type=Přepnout typ porovnání
 pulls.switch_head_and_base=Prohodit hlavní a základní větev
 pulls.filter_branch=Filtrovat větev
 pulls.no_results=Nebyly nalezeny žádné výsledky.
+pulls.show_all_commits=Zobrazit všechny commity
+pulls.show_changes_since_your_last_review=Zobrazit změny od vašeho posledního posouzení
+pulls.showing_only_single_commit=Zobrazuji pouze změny commitu %[1]s
+pulls.showing_specified_commit_range=Zobrazují se pouze změny mezi %[1]s..%[2]s
+pulls.select_commit_hold_shift_for_range=Vyberte commit. Podržte klávesu shift + klepněte pro výběr rozsahu
+pulls.review_only_possible_for_full_diff=Posouzení je možné pouze při zobrazení plného rozlišení
+pulls.filter_changes_by_commit=Filtrovat podle commitu
 pulls.nothing_to_compare=Tyto větve jsou stejné. Není potřeba vytvářet požadavek na natažení.
 pulls.nothing_to_compare_and_allow_empty_pr=Tyto větve jsou stejné. Tento požadavek na natažení bude prázdný.
 pulls.has_pull_request=`Požadavek na natažení mezi těmito větvemi již existuje: <a href="%[1]s">%[2]s#%[3]d</a>`
@@ -1578,6 +1713,8 @@ pulls.tab_files=Změněné soubory
 pulls.reopen_to_merge=Prosíme, otevřete znovu tento požadavek na natažení, aby se provedlo sloučení.
 pulls.cant_reopen_deleted_branch=Tento požadavek na natažení nemůže být znovu otevřen protože větev byla smazána.
 pulls.merged=Sloučený
+pulls.merged_success=Požadavek na natažení byl úspěšně sloučen a uzavřen
+pulls.closed=Požadavek na natažení uzavřen
 pulls.manually_merged=Sloučeno ručně
 pulls.merged_info_text=Větev %s může být nyní odstraněna.
 pulls.is_closed=Požadavek na natažení byl uzavřen.
@@ -1594,6 +1731,12 @@ pulls.is_empty=Změny na této větvi jsou již na cílové větvi. Toto bude pr
 pulls.required_status_check_failed=Některé požadované kontroly nebyly úspěšné.
 pulls.required_status_check_missing=Některé požadované kontroly chybí.
 pulls.required_status_check_administrator=Jako administrátor stále můžete sloučit tento požadavek na natažení.
+pulls.blocked_by_approvals=Tento požadavek na natažení ještě nemá dostatek schválení. Uděleno %d z %d schválení.
+pulls.blocked_by_rejection=Tento požadavek na natažení obsahuje změny požadované oficiálním posuzovatelem.
+pulls.blocked_by_official_review_requests=Tento požadavek na natažení obsahuje oficiální žádosti o posouzení.
+pulls.blocked_by_outdated_branch=Tento požadavek na natažení je zablokován, protože je zastaralý.
+pulls.blocked_by_changed_protected_files_1=Tento požadavek na natažení je zablokován, protože mění chráněný soubor:
+pulls.blocked_by_changed_protected_files_n=Tento požadavek na natažení je zablokován, protože mění chráněné soubory:
 pulls.can_auto_merge_desc=Tento požadavek na natažení může být automaticky sloučen.
 pulls.cannot_auto_merge_desc=Tento požadavek na natažení nemůže být automaticky sloučen, neboť se v něm nachází konflikty.
 pulls.cannot_auto_merge_helper=Pro vyřešení konfliktů proveďte ruční sloučení.
@@ -1628,6 +1771,7 @@ pulls.rebase_conflict_summary=Chybové hlášení
 pulls.unrelated_histories=Sloučení selhalo: Hlavní a základní revize nesdílí společnou historii. Tip: Zkuste jinou strategii
 pulls.merge_out_of_date=Sloučení selhalo: Základ byl aktualizován při generování sloučení. Tip: Zkuste to znovu.
 pulls.head_out_of_date=Sloučení selhalo: Hlavní revize byla aktualizován při generování sloučení. Tip: Zkuste to znovu.
+pulls.has_merged=Chyba: Požadavek na natažení byl sloučen, nelze znovu sloučit nebo změnit cílovou větev.
 pulls.push_rejected=Sloučení selhalo: Nahrání bylo zamítnuto. Zkontrolujte háčky Gitu pro tento repozitář.
 pulls.push_rejected_summary=Úplná zpráva o odmítnutí
 pulls.push_rejected_no_message=Sloučení se nezdařilo: Nahrání bylo odmítnuto, ale nebyla nalezena žádná vzdálená zpráva.<br>Zkontrolujte háčky gitu pro tento repozitář
@@ -1639,13 +1783,21 @@ pulls.status_checks_failure=Některé kontroly se nezdařily
 pulls.status_checks_error=Některé kontroly nahlásily chyby
 pulls.status_checks_requested=Požadováno
 pulls.status_checks_details=Podrobnosti
+pulls.status_checks_hide_all=Skrýt všechny kontroly
+pulls.status_checks_show_all=Zobrazit všechny kontroly
 pulls.update_branch=Aktualizovat větev sloučením
 pulls.update_branch_rebase=Aktualizovat větev pomocí rebase
 pulls.update_branch_success=Aktualizace větve byla úspěšná
 pulls.update_not_allowed=Nemáte oprávnění aktualizovat větev
 pulls.outdated_with_base_branch=Tato větev je zastaralá oproti základní větvi
+pulls.close=Zavřít požadavek na natažení
 pulls.closed_at=`uzavřel/a tento požadavek na natažení <a id="%[1]s" href="#%[1]s">%[2]s</a>`
 pulls.reopened_at=`znovuotevřel/a tento požadavek na natažení <a id="%[1]s" href="#%[1]s">%[2]s</a>`
+pulls.cmd_instruction_hint=`Zobrazit <a class="show-instruction">instrukce příkazové řádky</a>.`
+pulls.cmd_instruction_checkout_desc=Z vašeho repositáře projektu se podívejte na novou větev a vyzkoušejte změny.
+pulls.cmd_instruction_merge_title=Sloučit
+pulls.cmd_instruction_merge_desc=Slučte změny a aktualizujte je na Gitea.
+pulls.clear_merge_message=Vymazat zprávu o sloučení
 
 pulls.auto_merge_button_when_succeed=(Když kontroly uspějí)
 pulls.auto_merge_when_succeed=Automaticky sloučit, když všechny kontroly uspějí
@@ -1663,9 +1815,11 @@ pulls.delete.title=Odstranit tento požadavek na natažení?
 pulls.delete.text=Opravdu chcete tento požadavek na natažení smazat? (Tím se trvale odstraní veškerý obsah. Pokud jej hodláte archivovat, zvažte raději jeho uzavření.)
 
 
+pull.deleted_branch=(odstraněno):%s
 
 milestones.new=Nový milník
 milestones.closed=Zavřen dne %s
+milestones.update_ago=Aktualizováno %s
 milestones.no_due_date=Bez lhůty dokončení
 milestones.open=Otevřít
 milestones.close=Zavřít
@@ -1676,18 +1830,35 @@ milestones.desc=Popis
 milestones.due_date=Termín (volitelný)
 milestones.clear=Zrušit
 milestones.invalid_due_date_format=Termín dokončení musí být ve formátu 'rrrr-mm-dd'.
+milestones.create_success=Milník „%s“ byl vytvořen.
 milestones.edit=Upravit milník
 milestones.edit_subheader=Milník organizuje úkoly a sledují pokrok.
 milestones.cancel=Zrušit
 milestones.modify=Aktualizovat milník
+milestones.edit_success=Milník „%s“ byl aktualizován.
 milestones.deletion=Smazat milník
 milestones.deletion_desc=Odstranění milníku jej smaže ze všech souvisejících úkolů. Pokračovat?
 milestones.deletion_success=Milník byl odstraněn.
+milestones.filter_sort.earliest_due_data=Nejstarší datum dokončení
+milestones.filter_sort.latest_due_date=Nejnovější datum dokončení
 milestones.filter_sort.least_complete=Nejméně dokončené
 milestones.filter_sort.most_complete=Nejvíce dokončené
 milestones.filter_sort.most_issues=Nejvíce úkolů
 milestones.filter_sort.least_issues=Nejméně úkolů
 
+signing.will_sign=Tento commit bude podepsána klíčem „%s“.
+signing.wont_sign.error=Došlo k chybě při kontrole, zda může být commit podepsán.
+signing.wont_sign.nokey=K podpisu tohoto commitu není k dispozici žádný klíč.
+signing.wont_sign.never=Commity nejsou nikdy podepsány.
+signing.wont_sign.always=Commity jsou vždy podepsány.
+signing.wont_sign.pubkey=Commit nebude podepsán, protože nemáte veřejný klíč spojený s vaším účtem.
+signing.wont_sign.twofa=Pro podepsání commitů musíte mít povoleno dvoufaktorové ověření.
+signing.wont_sign.parentsigned=Commit nebude podepsán, protože nadřazený commit není podepsán.
+signing.wont_sign.basesigned=Sloučení nebude podepsáno, protože základní commit není podepsaný.
+signing.wont_sign.headsigned=Sloučení nebude podepsáno, protože hlavní revize není podepsána.
+signing.wont_sign.commitssigned=Sloučení nebude podepsáno, protože všechny přidružené revize nejsou podepsány.
+signing.wont_sign.approved=Sloučení nebude podepsáno, protože požadavek na natažení není schválen.
+signing.wont_sign.not_signed_in=Nejste přihlášeni.
 
 ext_wiki=Přístup k externí Wiki
 ext_wiki.desc=Odkaz do externí Wiki.
@@ -1711,10 +1882,13 @@ wiki.file_revision=Revize stránky
 wiki.wiki_page_revisions=Revize Wiki stránky
 wiki.back_to_wiki=Zpět na wiki stránku
 wiki.delete_page_button=Smazat stránku
+wiki.delete_page_notice_1=Odstranění Wiki stránky „%s“ nemůže být vráceno zpět. Pokračovat?
 wiki.page_already_exists=Stránka Wiki se stejným názvem již existuje.
+wiki.reserved_page=Jméno Wiki stránky „%s“ je rezervováno.
 wiki.pages=Stránky
 wiki.last_updated=Naposledy aktualizováno: %s
 wiki.page_name_desc=Zadejte název této Wiki stránky. Některé speciální názvy jsou: „Home“, „_Sidebar“ a „_Footer“.
+wiki.original_git_entry_tooltip=Zobrazit originální Git soubor namísto použití přátelského odkazu.
 
 activity=Aktivita
 activity.period.filter_label=Období:
@@ -1806,6 +1980,12 @@ settings.hooks=Webové háčky
 settings.githooks=Háčky Gitu
 settings.basic_settings=Základní nastavení
 settings.mirror_settings=Nastavení zrcadla
+settings.mirror_settings.docs=Nastavte repozitář pro automatickou synchronizaci commitů, značek a větví s jiným repozitářem.
+settings.mirror_settings.docs.disabled_pull_mirror.instructions=Nastavte váš projekt pro automatické nahrávání commitů, značek a větví do jiného repozitáře. Správce webu zakázal zrcadla pro natažení.
+settings.mirror_settings.docs.disabled_push_mirror.instructions=Nastavte svůj projekt pro automatické natažení commitů, značek a větví z jiného repozitáře.
+settings.mirror_settings.docs.no_new_mirrors=Váš repozitář zrcadlí změny do nebo z jiného repozitáře. Mějte prosím na paměti, že v tuto chvíli nemůžete vytvořit žádná nová zrcadla.
+settings.mirror_settings.docs.can_still_use=I když nemůžete upravit stávající zrcadla nebo vytvořit nová, stále můžete použít své stávající zrcadlo.
+settings.mirror_settings.docs.more_information_if_disabled=Více informací o zrcadlech pro nahrání a natažení naleznete zde:
 settings.mirror_settings.docs.doc_link_title=Jak mohu zrcadlit repozitáře?
 settings.mirror_settings.docs.pulling_remote_title=Stažení ze vzdáleného úložiště
 settings.mirror_settings.mirrored_repository=Zrcadlený repozitář
@@ -1816,6 +1996,7 @@ settings.mirror_settings.last_update=Poslední aktualizace
 settings.mirror_settings.push_mirror.none=Nenastavena žádná zrcadla pro nahrání
 settings.mirror_settings.push_mirror.remote_url=URL vzdáleného Git repozitáře
 settings.mirror_settings.push_mirror.add=Přidat zrcadlo pro nahrání
+settings.mirror_settings.push_mirror.edit_sync_time=Upravit interval synchronizace zrcadla
 
 settings.sync_mirror=Synchronizovat nyní
 settings.site=Webová stránka
@@ -1853,8 +2034,11 @@ settings.pulls.ignore_whitespace=Ignorovat bílé znaky při konfliktech
 settings.pulls.enable_autodetect_manual_merge=Povolit autodetekci ručních sloučení (Poznámka: V některých zvláštních případech může dojít k nesprávnému rozhodnutí)
 settings.pulls.allow_rebase_update=Povolit aktualizaci větve požadavku na natažení pomocí rebase
 settings.pulls.default_delete_branch_after_merge=Ve výchozím nastavení mazat větev požadavku na natažení po jeho sloučení
+settings.pulls.default_allow_edits_from_maintainers=Ve výchozím nastavení povolit úpravy od správců
+settings.releases_desc=Povolit vydání v repozitáři
 settings.packages_desc=Povolit registr balíčků repozitáře
 settings.projects_desc=Povolit projekty v repozitáři
+settings.actions_desc=Povolit akce repozitáře
 settings.admin_settings=Nastavení správce
 settings.admin_enable_health_check=Povolit kontrolu stavu repozitáře (git fsck)
 settings.admin_code_indexer=Indexování kódu
@@ -1881,6 +2065,7 @@ settings.transfer.rejected=Převod repozitáře byl zamítnut.
 settings.transfer.success=Převod repozitáře byl úspěšný.
 settings.transfer_abort=Zrušit převod
 settings.transfer_abort_invalid=Nemůžete zrušit neexistující převod repozitáře.
+settings.transfer_abort_success=Převod repozitáře do %s byl úspěšně zrušen.
 settings.transfer_desc=Předat tento repozitář uživateli nebo organizaci, ve které máte administrátorská práva.
 settings.transfer_form_title=Zadejte jméno repozitáře pro potvrzení:
 settings.transfer_in_progress=V současné době probíhá převod. Zrušte jej, pokud chcete převést tento repozitář jinému uživateli.
@@ -1900,6 +2085,7 @@ settings.trust_model.collaborator.long=Spolupracovník: Důvěřovat podpisům s
 settings.trust_model.collaborator.desc=Platné podpisy spolupracovníků tohoto repozitáře budou označeny jako „důvěryhodné“ - (ať se shodují s autorem, či nikoli). V opačném případě budou platné podpisy označeny jako „nedůvěryhodné“, pokud se podpis shoduje s přispěvatelem a „neodpovídající“, pokud ne.
 settings.trust_model.committer=Přispěvatel
 settings.trust_model.committer.long=Přispěvatel: Důvěřovat podpisům, které odpovídají autorům (což odpovídá GitHub a přinutí Giteu nastavit jako tvůrce pro Giteou podepsané revize)
+settings.trust_model.committer.desc=Platné podpisy budou označeny pouze jako „důvěryhodné“, pokud se shodují s přispěvatelem, jinak budou označeny jako „neodpovídající“. To přinutí Giteu, aby byla přispěvatelem podepsaných commitů se skutečným přispěvatelem označeným jako Co-authored-by: a Co-committed-by: na konci commitu. Výchozí klíč Gitea musí odpovídat uživateli v databázi.
 settings.trust_model.collaboratorcommitter=Spolupracovník+Přispěvatel
 settings.trust_model.collaboratorcommitter.long=Spolupracovník+Přispěvatel: Důvěřovat podpisům od spolupracovníků, které odpovídají tvůrci revize
 settings.trust_model.collaboratorcommitter.desc=Platné podpisy spolupracovníků tohoto repozitáře budou označeny jako „důvěryhodné“, pokud se shodují s přispěvatelem. V opačném případě budou platné podpisy označeny jako "nedůvěryhodné", pokud se podpis shoduje s přispěvatelem a „neodpovídajícím“ v opačném případě. To přinutí Giteu, aby byla označena jako přispěvatel podepsaných commitů se skutečným přispěvatelem označeným jako Co-Authored-By: a Co-Committed-By: na konci commitu. Výchozí klíč Forgejo musí odpovídat uživateli v databázi.
@@ -1915,6 +2101,7 @@ settings.delete_notices_2=- Tato operace trvale smaže repozitář <strong>%s</s
 settings.delete_notices_fork_1=- Rozštěpení repozitáře bude nezávislé po smazání.
 settings.deletion_success=Repozitář byl odstraněn.
 settings.update_settings_success=Nastavení repozitáře bylo aktualizováno.
+settings.update_settings_no_unit=Repozitář by měl povolit alespoň určitý druh interakce.
 settings.confirm_delete=Smazat repozitář
 settings.add_collaborator=Přidat spolupracovníka
 settings.add_collaborator_success=Spolupracovník byl přidán.
@@ -1945,12 +2132,14 @@ settings.webhook_deletion_desc=Odstranění webového háčku smaže jeho nastav
 settings.webhook_deletion_success=Webový háček byl smazán.
 settings.webhook.test_delivery=Test doručitelnosti
 settings.webhook.test_delivery_desc=Vyzkoušet tento webový háček pomocí falešné události.
+settings.webhook.test_delivery_desc_disabled=Chcete-li tento webový háček otestovat s falešnou událostí, aktivujte ho.
 settings.webhook.request=Požadavek
 settings.webhook.response=Odpověď
 settings.webhook.headers=Hlavičky
 settings.webhook.payload=Obsah
 settings.webhook.body=Tělo zprávy
 settings.webhook.replay.description=Zopakovat tento webový háček.
+settings.webhook.replay.description_disabled=Chcete-li znovu spustit tento webový háček, aktivujte jej.
 settings.webhook.delivery.success=Událost byla přidána do fronty doručení. Může to trvat několik sekund, než se zobrazí v historii doručení.
 settings.githooks_desc=Jelikož háčky Gitu jsou spravovány Gitem samotným, můžete upravit soubory háčků k provádění uživatelských operací.
 settings.githook_edit_desc=Je-li háček neaktivní, bude zobrazen vzorový obsah. Nebude-li zadán žádný obsah, háček bude vypnut.
@@ -2012,6 +2201,7 @@ settings.event_pull_request_review=Požadavek na natažení přezkoumán
 settings.event_pull_request_review_desc=Požadavek na natažení schválen, odmítnut nebo zkontrolován.
 settings.event_pull_request_sync=Požadavek na natažení synchronizován
 settings.event_pull_request_sync_desc=Požadavek na natažení synchronizován.
+settings.event_pull_request_review_request=Vyžádán požadavek na natažení
 settings.event_package=Balíček
 settings.event_package_desc=Balíček vytvořen nebo odstraněn v repozitáři.
 settings.branch_filter=Filtr větví
@@ -2057,6 +2247,7 @@ settings.title=Název
 settings.deploy_key_content=Obsah
 settings.key_been_used=Klíč pro nasazení se stejným obsahem je již používán.
 settings.key_name_used=Klíč pro nasazení se stejným jménem již existuje.
+settings.add_key_success=Klíč pro nasazení „%s“ byl přidán.
 settings.deploy_key_deletion=Odstranit klíč pro nasazení
 settings.deploy_key_deletion_desc=Odstranění klíče pro nasazení zruší jeho přístup k repozitáři. Pokračovat?
 settings.deploy_key_deletion_success=Klíč pro nasazení byl odstraněn.
@@ -2074,6 +2265,7 @@ settings.protect_disable_push=Zakázat nahrávání
 settings.protect_disable_push_desc=Žádné nahrávání do této větve nebude povoleno.
 settings.protect_enable_push=Povolit nahrávání
 settings.protect_enable_push_desc=Každý, kdo má přístup k zápisu, bude moci nahrávat do této větve (ale ne vynucená nahrávání).
+settings.protect_enable_merge=Povolit sloučení
 settings.protect_whitelist_committers=Povolit nahrání jen vyjmenovaným
 settings.protect_whitelist_committers_desc=Pouze povolení uživatelé budou moci nahrávat do této větve (ale ne vynucení nahrávání).
 settings.protect_whitelist_deploy_keys=Povolit nahrání klíčům pro nasazení s přístupem pro zápis.
@@ -2086,8 +2278,12 @@ settings.protect_merge_whitelist_committers_desc=Povolit pouze vyjmenovaným už
 settings.protect_merge_whitelist_users=Povolení uživatelé pro slučování:
 settings.protect_merge_whitelist_teams=Povolené týmy pro slučování:
 settings.protect_check_status_contexts=Povolit kontrolu stavu
+settings.protect_status_check_patterns=Vzorce kontroly stavu:
 settings.protect_check_status_contexts_desc=Požadovat kontrolu stavu před sloučením. Vyberte, jaké kontroly stavu musí projít před tím, než je možné větev sloučit do větve, která vyhovuje tomuto pravidlu. Pokud je povoleno, revize musí být nejprve nahrány do jiné větve, projít kontrolou stavu, a následné sloučeny nebo přímo nahrány do větve, která vyhovuje tomuto pravidlu. Pokud nejsou vybrány žádné kontexty, musí být poslední potvrzení úspěšné bez ohledu na kontext.
 settings.protect_check_status_contexts_list=Kontroly stavu pro tento repozitář zjištěné během posledního týdne
+settings.protect_status_check_matched=Odpovídá
+settings.protect_invalid_status_check_pattern=Neplatný vzor kontroly stavu: „%s“.
+settings.protect_no_valid_status_check_patterns=Žádné platné vzory kontroly stavu.
 settings.protect_required_approvals=Požadovaná schválení:
 settings.protect_required_approvals_desc=Umožnit sloučení pouze požadavkům na natažení s dostatečným pozitivním hodnocením.
 settings.protect_approvals_whitelist_enabled=Omezit schválení na povolené uživatele nebo týmy
@@ -2098,8 +2294,18 @@ settings.dismiss_stale_approvals=Odmítnout nekvalitní schválení
 settings.dismiss_stale_approvals_desc=Pokud budou do větve nahrány nové revize, které mění obsah tohoto požadavku na natažení, všechna stará schválení budou zamítnuta.
 settings.require_signed_commits=Vyžadovat podepsané revize
 settings.require_signed_commits_desc=Odmítnout nahrání do této větve pokud nejsou podepsaná nebo jsou neověřitelná.
+settings.protect_branch_name_pattern=Vzor jména chráněných větví
+settings.protect_branch_name_pattern_desc=Vzory jmen chráněných větví. Pro vzorovou syntaxi viz <a href="https://github.com/gobwas/glob">dokumentace</a>. Příklady: main, release/**
+settings.protect_patterns=Vzory
+settings.protect_protected_file_patterns=Vzory chráněných souborů (oddělené středníkem „;“):
+settings.protect_protected_file_patterns_desc=Chráněné soubory, které nemají povoleno být měněny přímo, i když uživatel má právo přidávat, upravovat nebo mazat soubory v této větvi. Více vzorů lze oddělit pomocí středníku („;“). Podívejte se na <a href='https://pkg.go.dev/github.com/gobwas/glob#Compile'>github.com/gobwas/glob</a> dokumentaci pro syntaxi vzoru. Příklady: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>.
+settings.protect_unprotected_file_patterns=Vzory nechráněných souborů (oddělené středníkem „;“):
+settings.protect_unprotected_file_patterns_desc=Nechráněné soubory, které je možné měnit přímo, pokud má uživatel právo zápisu, čímž se obejde omezení push. Více vzorů lze oddělit pomocí středníku („;“). Podívejte se na <a href='https://pkg.go.dev/github.com/gobwas/glob#Compile'>github.com/gobwas/glob</a> dokumentaci pro syntaxi vzoru. Příklady: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>.
 settings.add_protected_branch=Zapnout ochranu
 settings.delete_protected_branch=Vypnout ochranu
+settings.update_protect_branch_success=Ochrana větví pro větev „%s“ byla aktualizována.
+settings.remove_protected_branch_success=Ochrana větví pro větev „%s“ byla zakázána.
+settings.remove_protected_branch_failed=Odstranění ochranného pravidla větve „%s“ se nezdařilo.
 settings.protected_branch_deletion=Zakázat ochranu větví
 settings.protected_branch_deletion_desc=Zakázání ochrany větví umožní uživatelům s právem zápisu nahrávat do této větve. Pokračovat?
 settings.block_rejected_reviews=Blokovat sloučení při zamítavých posouzeních
@@ -2109,10 +2315,13 @@ settings.block_on_official_review_requests_desc=Slučování nebude možné, pok
 settings.block_outdated_branch=Blokovat sloučení, pokud je požadavek na natažení zastaralý
 settings.block_outdated_branch_desc=Slučování nebude možné, pokud je hlavní větev za základní větví.
 settings.default_branch_desc=Vybrat výchozí větev repozitáře pro požadavky na natažení a revize kódu:
+settings.merge_style_desc=Sloučit styly
 settings.default_merge_style_desc=Výchozí styl sloučení pro požadavky na natažení:
 settings.choose_branch=Vyberte větev…
 settings.no_protected_branch=Nejsou tu žádné chráněné větve.
 settings.edit_protected_branch=Upravit
+settings.protected_branch_required_rule_name=Požadovaný název pravidla
+settings.protected_branch_duplicate_rule_name=Duplikovat název pravidla
 settings.protected_branch_required_approvals_min=Požadovaná schválení nesmí být záporné číslo.
 settings.tags=Značky
 settings.tags.protection=Ochrana značek
@@ -2123,8 +2332,10 @@ settings.tags.protection.allowed.teams=Povolené týmy
 settings.tags.protection.allowed.noone=Nikdo
 settings.tags.protection.create=Chránit značku
 settings.tags.protection.none=Neexistují žádné chráněné značky.
+settings.tags.protection.pattern.description=Můžete použít jediné jméno nebo vzor glob nebo regulární výraz, který bude odpovídat více značek. Přečtěte si více v <a target="_blank" rel="noopener" href="https://docs.gitea.com/usage/protected-tags">průvodci chráněnými značkami</a>.
 settings.bot_token=Token pro robota
 settings.chat_id=ID chatu
+settings.thread_id=ID vlákna
 settings.matrix.homeserver_url=URL adresa Homeserveru
 settings.matrix.room_id=ID místnosti
 settings.matrix.message_type=Typ zprávy
@@ -2135,6 +2346,11 @@ settings.archive.error=Nastala chyba při archivování repozitáře. Prohlédn
 settings.archive.error_ismirror=Nemůžete archivovat zrcadlený repozitář.
 settings.archive.branchsettings_unavailable=Nastavení větví není dostupné, pokud je repozitář archivovaný.
 settings.archive.tagsettings_unavailable=Nastavení značek není k dispozici, pokud je repozitář archivován.
+settings.unarchive.button=Obnovit repozitář
+settings.unarchive.header=Obnovit tento repozitář
+settings.unarchive.text=Obnovení repozitáře vrátí možnost přijímání commitů a nahrávání. Stejně tak se obnoví i možnost zadávání nových úkolů a požadavků na natažení.
+settings.unarchive.success=Repozitář byl úspěšně obnoven.
+settings.unarchive.error=Nastala chyba při obnovování repozitáře. Prohlédněte si záznam pro více detailů.
 settings.update_avatar_success=Avatar repozitáře byl aktualizován.
 settings.lfs=LFS
 settings.lfs_filelist=LFS soubory uložené v tomto repozitáři
@@ -2201,6 +2417,7 @@ diff.show_more=Zobrazit více
 diff.load=Načíst rozdílové porovnání
 diff.generated=vygenerováno
 diff.vendored=vendorováno
+diff.comment.add_line_comment=Přidat jednořádkový komentář
 diff.comment.placeholder=Zanechat komentář
 diff.comment.markdown_info=Je podporována úprava vzhledu pomocí markdown.
 diff.comment.add_single_comment=Přidat jeden komentář
@@ -2212,7 +2429,9 @@ diff.review.header=Odeslat posouzení
 diff.review.placeholder=Posoudit komentář
 diff.review.comment=Okomentovat
 diff.review.approve=Schválit
+diff.review.self_reject=Autoři požadavků na natažení nemohou požadovat změny na svém vlastním požadavku na natažení
 diff.review.reject=Požadovat změny
+diff.review.self_approve=Autoři požadavku na natažení nemohou schválit svůj vlastní požadavek na natažení
 diff.committed_by=odevzdal
 diff.protected=Chráněno
 diff.image.side_by_side=Vedle sebe
@@ -2234,13 +2453,18 @@ release.compare=Porovnat
 release.edit=upravit
 release.ahead.commits=<strong>%d</strong> revizí
 release.ahead.target=do %s od tohoto vydání
+tag.ahead.target=do %s od této značky
 release.source_code=Zdrojový kód
 release.new_subheader=Vydání organizuje verze projektu.
 release.edit_subheader=Vydání organizuje verze projektu.
 release.tag_name=Název značky
 release.target=Cíl
 release.tag_helper=Vyberte existující značku nebo vytvořte novou značku.
+release.tag_helper_new=Nová značka. Tato značka bude vytvořen z cíle.
+release.tag_helper_existing=Stávající značka.
 release.title=Název vydání
+release.title_empty=Název nesmí být prázdný.
+release.message=Popište toto vydání
 release.prerelease_desc=Označit jako předběžná verze
 release.prerelease_helper=Označit vydání jako nevhodné pro produkční nasazení.
 release.cancel=Zrušit
@@ -2250,6 +2474,7 @@ release.edit_release=Aktualizovat vydání
 release.delete_release=Smazat vydání
 release.delete_tag=Smazat značku
 release.deletion=Smazat vydání
+release.deletion_desc=Smazání vydání jej pouze odebere z Gitea. Nebude to mít vliv na značku Git, obsah vašeho repozitáře nebo jeho historii. Pokračovat?
 release.deletion_success=Vydání bylo odstraněno.
 release.deletion_tag_desc=Odstraní tuto značku z repozitáře. Obsah repozitáře a historie zůstanou nezměněny. Pokračovat?
 release.deletion_tag_success=Značka byla odstraněna.
@@ -2261,32 +2486,56 @@ release.downloads=Soubory ke stažení
 release.download_count=Stažení: %s
 release.add_tag_msg=Použít název a obsah vydání jako zprávu značky.
 release.add_tag=Vytvořit pouze značku
+release.releases_for=Vydání pro %s
 release.tags_for=Značky pro %s
 
 branch.name=Jméno větve
+branch.already_exists=Větev pojmenovaná „%s“ již existuje.
 branch.delete_head=Smazat
+branch.delete=Smazat větev „%s“
 branch.delete_html=Smazat větev
+branch.delete_desc=Smazání větve je trvalé. Přestože zrušená větev může existovat i po krátkou dobu, než bude skutečně odstraněna, NELZE ji většinou vrátit. Pokračovat?
+branch.deletion_success=Větev „%s“ byla smazána.
+branch.deletion_failed=Nepodařilo se odstranit větev „%s“.
+branch.delete_branch_has_new_commits=Větev „%s“ nemůže být smazána, protože byly přidány nové commity po sloučení.
 branch.create_branch=Vytvořit větev <strong>%s</strong>
+branch.create_from=z „%s“
+branch.create_success=Větev „%s“ byla vytvořena.
 branch.branch_already_exists=Větev „%s“ již existuje v tomto repozitáři.
+branch.branch_name_conflict=Jméno větve „%s“ koliduje s již existující větví „%s“.
+branch.tag_collision=Větev „%s“ nemůže být vytvořena, protože v repozitáři existuje značka se stejným jménem.
 branch.deleted_by=Odstranil %s
+branch.restore_success=Větev „%s“ byla obnovena.
+branch.restore_failed=Nepodařilo se obnovit větev „%s“.
+branch.protected_deletion_failed=Větev „%s“ je chráněna. Nemůže být smazána.
+branch.default_deletion_failed=Větev „%s“ je výchozí větev. Nelze ji odstranit.
+branch.restore=Obnovit větev „%s“
+branch.download=Stáhnout větev „%s“
+branch.rename=Přejmenovat větev „%s“
+branch.search=Hledat větev
 branch.included_desc=Tato větev je součástí výchozí větve
 branch.included=Zahrnuje
 branch.create_new_branch=Vytvořit větev z větve:
 branch.confirm_create_branch=Vytvořit větev
+branch.warning_rename_default_branch=Přejmenováváte výchozí větev.
+branch.rename_branch_to=Přejmenovat „%s“ na:
 branch.confirm_rename_branch=Přejmenovat větev
 branch.create_branch_operation=Vytvořit větev
 branch.new_branch=Vytvořit novou větev
+branch.new_branch_from=Vytvořit novou větev z „%s“
 branch.renamed=Větev %s byla přejmenována na %s.
 
 tag.create_tag=Vytvořit značku <strong>%s</strong>
 tag.create_tag_operation=Vytvořit značku
 tag.confirm_create_tag=Vytvořit značku
+tag.create_tag_from=Vytvořit novou značku z „%s“
 
 tag.create_success=Značka „%s“ byla vytvořena.
 
 topic.manage_topics=Spravovat témata
 topic.done=Hotovo
 topic.count_prompt=Nelze vybrat více než 25 témat
+topic.format_prompt=Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a tečky („.“) a může být dlouhé až 35 znaků. Písmena musí být malá.
 
 find_file.go_to_file=Přejít na soubor
 find_file.no_matching=Nebyl nalezen žádný odpovídající soubor
@@ -2318,23 +2567,28 @@ team_permission_desc=Oprávnění
 team_unit_desc=Povolit přístup do částí repozitáře
 team_unit_disabled=(zakázaná)
 
+form.name_reserved=Název organizace „%s“ je rezervován.
+form.name_pattern_not_allowed=Vzor „%s“ není povolený v názvu organizace.
 form.create_org_not_allowed=Nemáte oprávnění vytvářet nové organizace.
 
 settings=Nastavení
 settings.options=Organizace
 settings.full_name=Celé jméno
+settings.email=Kontaktní e-mail
 settings.website=Webové stránky
 settings.location=Umístění
 settings.permission=Oprávnění
 settings.repoadminchangeteam=Správce úložišť může týmům přidávat a odebírat přístup
 settings.visibility=Viditelnost
 settings.visibility.public=Veřejná
+settings.visibility.limited=Omezeno (Viditelné pouze pro ověřené uživatele)
 settings.visibility.limited_shortname=Omezený
 settings.visibility.private=Soukromá (viditelné jen členům organizace)
 settings.visibility.private_shortname=Soukromý
 
 settings.update_settings=Upravit nastavení
 settings.update_setting_success=Nastavení organizace bylo upraveno.
+settings.change_orgname_prompt=Poznámka: Změna názvu organizace také změní adresu URL vaší organizace a uvolní staré jméno této organizace.
 settings.change_orgname_redirect_prompt=Staré jméno bude přesměrovávat, dokud nebude znovu obsazeno.
 settings.update_avatar_success=Avatar organizace byl aktualizován.
 settings.delete=Smazat organizaci
@@ -2399,6 +2653,7 @@ teams.remove_all_repos_title=Odstranit všechny repozitáře týmu
 teams.remove_all_repos_desc=Tímto odeberete všechny repozitáře z týmu.
 teams.add_all_repos_title=Přidat všechny repozitáře
 teams.add_all_repos_desc=Tímto přidáte do týmu všechny repozitáře organizace.
+teams.add_nonexistent_repo=Repositář, který se snažíte přidat, neexistuje. Nejdříve jej vytvořte, prosím.
 teams.add_duplicate_users=Uživatel je již členem týmu.
 teams.repos.none=Tento tým nemůže přistoupit k žádným repozitářům.
 teams.members.none=Žádní členové v tomto týmu.
@@ -2409,15 +2664,18 @@ teams.all_repositories_helper=Tým má přístup ke všem repositářům. Výbě
 teams.all_repositories_read_permission_desc=Tomuto týmu je udělen přístup pro <strong>Čtení</strong> <strong>všech repozitářů</strong>: členové mohou prohlížet a klonovat repozitáře.
 teams.all_repositories_write_permission_desc=Tomuto týmu je udělen přístup pro <strong>Zápis</strong> do <strong>všech repozitářů</strong>: členové mohou prohlížet a nahrávat do repozitářů.
 teams.all_repositories_admin_permission_desc=Tomuto týmu je udělen <strong>Administrátorský</strong> přístup do <strong>všech repozitářů</strong>: členové mohou prohlížet, nahrávat a přidávat spolupracovníky do repozitářů.
+teams.invite.title=Byli jste pozváni do týmu <strong>%s</strong> v organizaci <strong>%s</strong>.
 teams.invite.by=Pozvání od %s
 teams.invite.description=Pro připojení k týmu klikněte na tlačítko níže.
 
 [admin]
 dashboard=Přehled
+identity_access=Identita a přístup
 users=Uživatelské účty
 organizations=Organizace
 repositories=Repozitáře
 hooks=Webové háčky
+integrations=Integrace
 authentication=Zdroje ověření
 emails=Uživatelské e-maily
 config=Nastavení
@@ -2426,7 +2684,9 @@ monitor=Sledování
 first_page=První
 last_page=Poslední
 total=Celkem: %d
+settings=Nastavení správce
 
+dashboard.new_version_hint=Gitea %s je nyní k dispozici, právě u vás běži %s. Podívej se na <a target="_blank" rel="noreferrer" href="https://blog.gitea.io">blogu</a> pro více informací.
 dashboard.statistic=Souhrn
 dashboard.operations=Operace údržby
 dashboard.system_status=Status systému
@@ -2435,13 +2695,15 @@ dashboard.operation_switch=Přepnout
 dashboard.operation_run=Spustit
 dashboard.clean_unbind_oauth=Vyčistit neprovázané OAuth spojení
 dashboard.clean_unbind_oauth_success=Všechna neprovázaná OAuth spojení byla smazána.
-dashboard.task.started=Zahájen úkol: %[1]s
-dashboard.task.process=Úkol: %[1]s
-dashboard.task.error=Chyba v úkolu: %[1]s: %[3]s
-dashboard.task.finished=Úkol: %[1]s začalo %[2]s skončilo
-dashboard.task.unknown=Neznámý úkol: %[1]s
+dashboard.task.started=Zahájena úloha: %[1]s
+dashboard.task.process=Úloha: %[1]s
+dashboard.task.cancelled=Úloha: %[1]s zrušean: %[3]s
+dashboard.task.error=Chyba v úloze: %[1]s: %[3]s
+dashboard.task.finished=Úloha: %[1]s začala %[2]s skončila
+dashboard.task.unknown=Neznámá úloha: %[1]s
 dashboard.cron.started=Začala naplánovaná úloha: %[1]s
 dashboard.cron.process=Naplánovaná úloha: %[1]s
+dashboard.cron.cancelled=Naplánovaná úloha: %[1]s zrušena: %[3]s
 dashboard.cron.error=Chyba v naplánované úloze: %s: %[3]s
 dashboard.cron.finished=Naplánovaná úloha: %[1]s skončila
 dashboard.delete_inactive_accounts=Smazat všechny neaktivované účty
@@ -2498,9 +2760,11 @@ dashboard.delete_old_actions=Odstranit všechny staré akce z databáze
 dashboard.delete_old_actions.started=Začalo odstraňování všech starých akcí z databáze.
 dashboard.update_checker=Kontrola aktualizací
 dashboard.delete_old_system_notices=Odstranit všechna stará systémová upozornění z databáze
-dashboard.stop_zombie_tasks=Zastavit zombie úkoly
-dashboard.stop_endless_tasks=Zastavit nekonečné úkoly
+dashboard.stop_zombie_tasks=Zastavit zombie úlohy
+dashboard.stop_endless_tasks=Zastavit nekonečné úlohy
 dashboard.cancel_abandoned_jobs=Zrušit opuštěné úlohy
+dashboard.start_schedule_tasks=Spustit naplánované úlohy
+dashboard.sync_branch.started=Synchronizace větví byla spuštěna
 
 users.user_manage_panel=Správa uživatelských účtů
 users.new_account=Vytvořit uživatelský účet
@@ -2509,12 +2773,16 @@ users.full_name=Celé jméno
 users.activated=Aktivován
 users.admin=Správce
 users.restricted=Omezený
+users.reserved=Rezervováno
+users.bot=Bot
+users.remote=Vzdálený
 users.2fa=2FA
 users.repos=Repozitáře
 users.created=Vytvořen
 users.last_login=Poslední přihlášení
 users.never_login=Nikdy nepřihlášen
 users.send_register_notify=Odeslat upozornění při registraci uživatele
+users.new_success=Uživatelský účet „%s“ byl vytvořen.
 users.edit=Upravit
 users.auth_source=Zdroj ověřování
 users.local=Místní
@@ -2539,6 +2807,7 @@ users.still_own_repo=Tento uživatel stále vlastní jeden nebo více repozitá
 users.still_has_org=Uživatel je člen organizace. Nejprve odstraňte uživatele ze všech organizací.
 users.purge=Vymazat uživatele
 users.purge_help=Vynuceně smazat uživatele a všechny repositáře, organizace a balíčky vlastněné uživatelem. Všechny komentáře budou také smazány.
+users.still_own_packages=Tento uživatel stále vlastní jeden nebo více balíčků, nejprve odstraňte tyto balíčky.
 users.deletion_success=Uživatelský účet byl smazán.
 users.reset_2fa=Resetovat 2FA
 users.list_status_filter.menu_text=Filtr
@@ -2553,6 +2822,7 @@ users.list_status_filter.is_prohibit_login=Zakázat přihlášení
 users.list_status_filter.not_prohibit_login=Povolit přihlášení
 users.list_status_filter.is_2fa_enabled=2FA povoleno
 users.list_status_filter.not_2fa_enabled=2FA zakázáno
+users.details=Detaily uživatele
 
 emails.email_manage_panel=Správa e-mailů uživatele
 emails.primary=Hlavní
@@ -2565,6 +2835,7 @@ emails.updated=E-mail aktualizován
 emails.not_updated=Aktualizace požadované e-mailové adresy se nezdařila: %v
 emails.duplicate_active=Tato e-mailová adresa je již aktivní pro jiného uživatele.
 emails.change_email_header=Aktualizovat vlastnosti e-mailu
+emails.change_email_text=Opravdu chcete aktualizovat tuto e-mailovou adresu?
 
 orgs.org_manage_panel=Správa organizací
 orgs.name=Název
@@ -2583,9 +2854,12 @@ repos.stars=Oblíbení
 repos.forks=Rozštěpení
 repos.issues=Úkoly
 repos.size=Velikost
+repos.lfs_size=Velikost LFS
 
 packages.package_manage_panel=Správa balíčků
 packages.total_size=Celková velikost: %s
+packages.unreferenced_size=Neodkazovaná velikost: %s
+packages.cleanup=Vyčistit prošlá data
 packages.owner=Vlastník
 packages.creator=Tvůrce
 packages.name=Název
@@ -2691,6 +2965,7 @@ auths.sspi_default_language=Výchozí jazyk uživatele
 auths.sspi_default_language_helper=Výchozí jazyk pro uživatele automaticky vytvořené pomocí SSPI auth metody. Pokud dáváte přednost automatickému zjištění jazyka, ponechte prázdné.
 auths.tips=Tipy
 auths.tips.oauth2.general=Ověřování OAuth2
+auths.tips.oauth2.general.tip=Při registraci nové OAuth2 autentizace by URL callbacku/přesměrování měla být:
 auths.tip.oauth2_provider=Poskytovatel OAuth2
 auths.tip.bitbucket=Vytvořte nového OAuth konzumenta na https://bitbucket.org/account/user/<vase-uzivatelske-jmeno>/oauth-consumers/new a přidejte oprávnění „Account“ - „Read“
 auths.tip.nextcloud=Zaregistrujte nového OAuth konzumenta na vaší instanci pomocí následujícího menu „Nastavení -> Zabezpečení -> OAuth 2.0 klient“
@@ -2702,10 +2977,12 @@ auths.tip.google_plus=Získejte klientské pověření OAuth2 z Google API konzo
 auths.tip.openid_connect=Použijte OpenID URL pro objevování spojení (<server>/.well-known/openid-configuration) k nastavení koncových bodů
 auths.tip.twitter=Jděte na https://dev.twitter.com/apps, vytvořte aplikaci a ujistěte se, že volba „Allow this application to be used to Sign in with Twitter“ je povolená
 auths.tip.discord=Registrujte novou aplikaci na https://discordapp.com/developers/applications/me
+auths.tip.gitea=Registrovat novou Oauth2 aplikaci. Návod naleznete na https://docs.gitea.com/development/oauth2-provider
 auths.tip.yandex=Vytvořte novou aplikaci na https://oauth.yandex.com/client/new. Vyberte následující oprávnění z „Yandex.Passport API“ sekce: „Přístup k e-mailové adrese“, „Přístup k uživatelskému avataru“ a „Přístup k uživatelskému jménu, jménu a příjmení, pohlaví“
 auths.tip.mastodon=Vložte vlastní URL instance pro mastodon, kterou se chcete autentizovat (nebo použijte výchozí)
 auths.edit=Upravit zdroj ověřování
 auths.activated=Tento zdroj ověřování je aktivován
+auths.new_success=Zdroj ověřování „%s“ byl přidán.
 auths.update_success=Zdroj ověřování byl aktualizován.
 auths.update=Aktualizovat zdroj ověřování
 auths.delete=Smazat zdroj ověřování
@@ -2713,7 +2990,9 @@ auths.delete_auth_title=Smazat zdroj ověřování
 auths.delete_auth_desc=Zamezíte přihlášení uživatelům pomocí tohoto zdroje ověřování, pokud ho smažete. Pokračovat?
 auths.still_in_used=Zdroj ověřování je stále používán. Nejprve převeďte nebo smažte všechny uživatele, kteří používají tento způsob ověřování.
 auths.deletion_success=Zdroj ověřování byl smazán.
+auths.login_source_exist=Zdroj ověřování „%s“ již existuje.
 auths.login_source_of_type_exist=Zdroj ověřování tohoto typu již existuje.
+auths.unable_to_initialize_openid=Nelze inicializovat poskytovatele OpenID Connect: %s
 auths.invalid_openIdConnectAutoDiscoveryURL=Neplatná URL adresa pro automatické vyhledání (musí být platná adresa URL začínající http:// nebo https://)
 
 config.server_config=Nastavení serveru
@@ -2728,6 +3007,7 @@ config.disable_router_log=Vypnout log směrovače
 config.run_user=Spustit jako uživatel
 config.run_mode=Režim spouštění
 config.git_version=Verze Gitu
+config.app_data_path=Cesta k datům aplikace
 config.repo_root_path=Kořenový adresář repozitářů
 config.lfs_root_path=Kořenový adresář LFS
 config.log_file_root_path=Adresář logů
@@ -2802,6 +3082,9 @@ config.mailer_sendmail_timeout=Časový limit Sandmail
 config.mailer_use_dummy=Fiktivní
 config.test_email_placeholder=E-mail (např.: test@example.com)
 config.send_test_mail=Odeslat zkušební e-mail
+config.send_test_mail_submit=Odeslat
+config.test_mail_failed=Odeslání testovacího e-mailu na „%s“ selhalo: %v
+config.test_mail_sent=Zkušební e-mail byl odeslán na „%s“.
 
 config.oauth_config=Nastavení ověření OAuth
 config.oauth_enabled=Zapnutý
@@ -2841,10 +3124,12 @@ config.git_gc_timeout=Časový limit operace GC
 config.log_config=Nastavení logů
 config.disabled_logger=Zakázané
 config.access_log_mode=Režim logování přístupu
+config.access_log_template=Šablona záznamu přístupu
 config.xorm_log_sql=Logovat SQL
 
 config.set_setting_failed=Nastavení %s se nezdařilo
 
+monitor.stats=Statistiky
 
 monitor.cron=Naplánované úlohy
 monitor.name=Název
@@ -2854,6 +3139,7 @@ monitor.previous=Předešlý čas spuštění
 monitor.execute_times=Vykonání
 monitor.process=Spuštěné procesy
 monitor.stacktrace=Výpisy zásobníku
+monitor.processes_count=%d procesů
 monitor.desc=Popis
 monitor.start=Čas zahájení
 monitor.execute_time=Doba provádění
@@ -2871,6 +3157,7 @@ monitor.queue.exemplar=Typ vzoru
 monitor.queue.numberworkers=Počet workerů
 monitor.queue.maxnumberworkers=Maximální počet workerů
 monitor.queue.numberinqueue=Číslo ve frontě
+monitor.queue.review_add=Posoudit / přidat workery
 monitor.queue.settings.title=Nastavení fondu
 monitor.queue.settings.maxnumberworkers=Maximální počet workerů
 monitor.queue.settings.maxnumberworkers.placeholder=V současné době %[1]d
@@ -2990,6 +3277,7 @@ desc=Správa balíčků repozitáře.
 empty=Zatím nejsou žádné balíčky.
 empty.documentation=Další informace o registru balíčků naleznete v <a target="_blank" rel="noopener noreferrer" href="%s">dokumentaci</a>.
 empty.repo=Nahráli jste balíček, ale nezobrazil se zde? Přejděte na <a href="%[1]s">nastavení balíčku</a> a propojte jej s tímto repozitářem.
+registry.documentation=Další informace o registru %s naleznete v <a target="_blank" rel="noopener noreferrer" href="%s">dokumentaci</a>.
 filter.type=Typ
 filter.type.all=Vše
 filter.no_result=Váš filtr nepřinesl žádné výsledky.
@@ -3053,6 +3341,7 @@ debian.repository.distributions=Distribuce
 debian.repository.components=Komponenty
 debian.repository.architectures=Architektury
 generic.download=Stáhnout balíček z příkazové řádky:
+go.install=Nainstalujte balíček z příkazové řádky:
 helm.registry=Nastavte tento registr z příkazového řádku:
 helm.install=Pro instalaci balíčku spusťte následující příkaz:
 maven.registry=Nastavte tento registr ve vašem projektu <code>pom.xml</code> souboru:
@@ -3074,6 +3363,8 @@ pub.install=Chcete-li nainstalovat balíček pomocí Dart, spusťte následujíc
 pypi.requires=Vyžaduje Python
 pypi.install=Pro instalaci balíčku pomocí pip spusťte následující příkaz:
 rpm.registry=Nastavte tento registr z příkazového řádku:
+rpm.distros.redhat=na distribuce založené na RedHat
+rpm.distros.suse=na distribuce založené na SUSE
 rpm.install=Pro instalaci balíčku spusťte následující příkaz:
 rubygems.install=Pro instalaci balíčku pomocí gem spusťte následující příkaz:
 rubygems.install2=nebo ho přidejte do Gemfie:
@@ -3082,6 +3373,8 @@ rubygems.dependencies.development=Vývojové závislosti
 rubygems.required.ruby=Vyžaduje verzi Ruby
 rubygems.required.rubygems=Vyžaduje verzi RubyGem
 swift.registry=Nastavte tento registr z příkazového řádku:
+swift.install=Přidejte balíček do svého <code>Package.swift</code> souboru:
+swift.install2=a spustit následující příkaz:
 vagrant.install=Pro přidání Vagrant box spusťte následující příkaz:
 settings.link=Propojit tento balíček s repozitářem
 settings.link.description=Pokud propojíte balíček s repozitářem, je tento balíček uveden v seznamu balíčků repozitáře.
@@ -3096,6 +3389,7 @@ settings.delete.success=Balíček byl odstraněn.
 settings.delete.error=Nepodařilo se odstranit balíček.
 owner.settings.cargo.title=Index registru Cargo
 owner.settings.cargo.initialize=Inicializovat index
+owner.settings.cargo.initialize.description=Pro použití Cargo registru je zapotřebí speciální index Git. Použití této možnosti (znovu)vytvoří repozitář a automaticky jej nastaví.
 owner.settings.cargo.initialize.error=Nepodařilo se inicializovat Cargo index: %v
 owner.settings.cargo.initialize.success=Index Cargo byl úspěšně vytvořen.
 owner.settings.cargo.rebuild=Znovu vytvořit Index
@@ -3104,6 +3398,7 @@ owner.settings.cargo.rebuild.success=Cargo Index byl úspěšně obnoven.
 owner.settings.cleanuprules.title=Spravovat pravidla pro čištění
 owner.settings.cleanuprules.add=Přidat pravidlo pro čištění
 owner.settings.cleanuprules.edit=Upravit pravidlo pro čištění
+owner.settings.cleanuprules.none=Nejsou k dispozici žádná pravidla čištění. Prohlédněte si prosím dokumentaci.
 owner.settings.cleanuprules.preview=Náhled pravidla pro čištění
 owner.settings.cleanuprules.preview.overview=%d balíčků má být odstraněno.
 owner.settings.cleanuprules.preview.none=Pravidlo čištění neodpovídá žádným balíčkům.
@@ -3122,6 +3417,7 @@ owner.settings.cleanuprules.success.update=Pravidlo pro čištění bylo aktuali
 owner.settings.cleanuprules.success.delete=Pravidlo pro čištění bylo odstraněno.
 owner.settings.chef.title=Registr Chef
 owner.settings.chef.keypair=Generovat pár klíčů
+owner.settings.chef.keypair.description=Pro autentizaci do registru Chef je zapotřebí pár klíčů. Pokud jste předtím vytvořili pár klíčů, nově vygenerovaný pár klíčů vyřadí starý pár klíčů.
 
 [secrets]
 secrets=Tajné klíče
@@ -3148,6 +3444,7 @@ status.waiting=Čekání
 status.running=Probíhá
 status.success=Úspěch
 status.failure=Chyba
+status.cancelled=Zrušeno
 status.skipped=Přeskočeno
 status.blocked=Blokováno
 
@@ -3161,7 +3458,8 @@ runners.description=Popis
 runners.labels=Štítky
 runners.last_online=Poslední čas online
 runners.runner_title=Runner
-runners.task_list=Nedávné úkoly na tomto runneru
+runners.task_list=Nedávné úlohy na tomto runneru
+runners.task_list.no_tasks=Zatím zde nejsou žádné úlohy.
 runners.task_list.run=Spustit
 runners.task_list.status=Status
 runners.task_list.repository=Repozitář
@@ -3175,23 +3473,65 @@ runners.delete_runner=Odstranit tento runner
 runners.delete_runner_success=Runner byl úspěšně odstraněn
 runners.delete_runner_failed=Odstranění runneru selhalo
 runners.delete_runner_header=Potvrdit odstranění tohoto runneru
+runners.delete_runner_notice=Pokud na tomto runneru běží úloha, bude ukončena a označena jako neúspěšná. Může dojít k přerušení vytváření pracovního postupu.
 runners.status.unspecified=Neznámý
 runners.status.idle=Nečinný
 runners.status.active=Aktivní
 runners.status.offline=Offline
 runners.version=Verze
+runners.reset_registration_token=Resetovat registrační token
+runners.reset_registration_token_success=Registrační token runneru byl úspěšně obnoven
 
 runs.all_workflows=Všechny pracovní postupy
 runs.commit=Commit
+runs.scheduled=Naplánováno
+runs.invalid_workflow_helper=Konfigurační soubor pracovního postupu je neplatný. Zkontrolujte prosím konfigurační soubor: %s
+runs.no_matching_online_runner_helper=Žádný odpovídající online runner s popiskem: %s
+runs.actor=Aktér
 runs.status=Status
+runs.actors_no_select=Všichni aktéři
+runs.status_no_select=Všechny stavy
+runs.no_results=Nebyly nalezeny žádné výsledky.
+runs.no_workflows=Zatím neexistují žádné pracovní postupy.
+runs.no_workflows.quick_start=Nevíte jak začít s Gitea Action? Podívejte se na <a target="_blank" rel="noopener noreferrer" href="%s">průvodce rychlým startem</a>.
+runs.no_workflows.documentation=Další informace o Gitea Action, viz <a target="_blank" rel="noopener noreferrer" href="%s">dokumentace</a>.
+runs.no_runs=Pracovní postup zatím nebyl spuštěn.
+runs.empty_commit_message=(prázdná zpráva commitu)
+
+workflow.disable=Zakázat pracovní postup
+workflow.disable_success=Pracovní postup „%s“ byl úspěšně deaktivován.
+workflow.enable=Povolit pracovní postup
+workflow.enable_success=Pracovní postup „%s“ byl úspěšně aktivován.
+workflow.disabled=Pracovní postup je zakázán.
 
 
-
+variables=Proměnné
+variables.management=Správa proměnných
+variables.creation=Přidat proměnnou
+variables.none=Zatím nejsou žádné proměnné.
+variables.deletion=Odstranit proměnnou
+variables.deletion.description=Odstranění proměnné je trvalé a nelze jej vrátit zpět. Pokračovat?
+variables.description=Proměnné budou předány určitým akcím a nelze je přečíst jinak.
+variables.id_not_exist=Proměnná s id %d neexistuje.
+variables.edit=Upravit proměnnou
+variables.deletion.failed=Nepodařilo se odstranit proměnnou.
+variables.deletion.success=Proměnná byla odstraněna.
+variables.creation.failed=Přidání proměnné se nezdařilo.
+variables.creation.success=Proměnná „%s“ byla přidána.
+variables.update.failed=Úprava proměnné se nezdařila.
+variables.update.success=Proměnná byla upravena.
 
 [projects]
+type-1.display_name=Samostatný projekt
+type-2.display_name=Projekt repozitíře
 type-3.display_name=Projekt organizace
 
 [git.filemode]
+changed_filemode=%[1]s → %[2]s
 ; Ordered by git filemode value, ascending. E.g. directory has "040000", normal file has "100644", …
+directory=Adresář
+normal_file=Normální soubor
+executable_file=Spustitelný soubor
 symbolic_link=Symbolický odkaz
+submodule=Submodul
 

From 6f159f4c1ce73e57fd35dfd7396718fc3c4b18d5 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Fri, 16 Feb 2024 03:20:50 +0100
Subject: [PATCH 08/28] Update JS and PY dependencies (#29184)

- Update all excluding `@mcaptcha/vanilla-glue` and
`eslint-plugin-array-func`
- Tested pdf, chart.js, swagger

(cherry picked from commit 8f9c9d3a5fa185f4a61f71e49f15b6d5e611b44a)
---
 package-lock.json | 733 +++++++++++++++++++++++++---------------------
 package.json      |  16 +-
 poetry.lock       |   8 +-
 pyproject.toml    |   2 +-
 4 files changed, 404 insertions(+), 355 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index 764ae51f9d..48da8124e0 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -18,8 +18,8 @@
         "@webcomponents/custom-elements": "1.6.0",
         "add-asset-webpack-plugin": "2.0.1",
         "ansi_up": "6.0.2",
-        "asciinema-player": "3.6.3",
-        "chart.js": "4.3.0",
+        "asciinema-player": "3.6.4",
+        "chart.js": "4.4.1",
         "chartjs-adapter-dayjs-4": "1.0.4",
         "chartjs-plugin-zoom": "2.0.1",
         "clippie": "4.0.6",
@@ -39,22 +39,22 @@
         "minimatch": "9.0.3",
         "monaco-editor": "0.46.0",
         "monaco-editor-webpack-plugin": "7.1.0",
-        "pdfobject": "2.2.12",
+        "pdfobject": "2.3.0",
         "pretty-ms": "9.0.0",
         "sortablejs": "1.15.2",
-        "swagger-ui-dist": "5.11.3",
+        "swagger-ui-dist": "5.11.6",
         "throttle-debounce": "5.0.0",
         "tinycolor2": "1.6.0",
         "tippy.js": "6.3.7",
         "toastify-js": "1.12.0",
         "tributejs": "5.1.3",
         "uint8-to-base64": "0.2.0",
-        "vue": "3.4.18",
+        "vue": "3.4.19",
         "vue-bar-graph": "2.0.0",
         "vue-chartjs": "5.3.0",
         "vue-loader": "17.4.2",
         "vue3-calendar-heatmap": "2.0.5",
-        "webpack": "5.90.1",
+        "webpack": "5.90.2",
         "webpack-cli": "5.1.4",
         "wrap-ansi": "9.0.0"
       },
@@ -62,7 +62,7 @@
         "@eslint-community/eslint-plugin-eslint-comments": "4.1.0",
         "@playwright/test": "1.41.2",
         "@stoplight/spectral-cli": "6.11.0",
-        "@stylistic/eslint-plugin-js": "1.6.1",
+        "@stylistic/eslint-plugin-js": "1.6.2",
         "@stylistic/stylelint-plugin": "2.0.0",
         "@vitejs/plugin-vue": "5.0.4",
         "eslint": "8.56.0",
@@ -72,7 +72,7 @@
         "eslint-plugin-no-jquery": "2.7.0",
         "eslint-plugin-no-use-extend-native": "0.5.0",
         "eslint-plugin-regexp": "2.2.0",
-        "eslint-plugin-sonarjs": "0.23.0",
+        "eslint-plugin-sonarjs": "0.24.0",
         "eslint-plugin-unicorn": "51.0.1",
         "eslint-plugin-vitest": "0.3.22",
         "eslint-plugin-vitest-globals": "1.4.0",
@@ -1221,9 +1221,9 @@
       }
     },
     "node_modules/@jridgewell/resolve-uri": {
-      "version": "3.1.1",
-      "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz",
-      "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==",
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+      "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
       "engines": {
         "node": ">=6.0.0"
       }
@@ -1456,9 +1456,9 @@
       "dev": true
     },
     "node_modules/@rollup/rollup-android-arm-eabi": {
-      "version": "4.9.6",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.9.6.tgz",
-      "integrity": "sha512-MVNXSSYN6QXOulbHpLMKYi60ppyO13W9my1qogeiAqtjb2yR4LSmfU2+POvDkLzhjYLXz9Rf9+9a3zFHW1Lecg==",
+      "version": "4.11.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.11.0.tgz",
+      "integrity": "sha512-BV+u2QSfK3i1o6FucqJh5IK9cjAU6icjFFhvknzFgu472jzl0bBojfDAkJLBEsHFMo+YZg6rthBvBBt8z12IBQ==",
       "cpu": [
         "arm"
       ],
@@ -1469,9 +1469,9 @@
       ]
     },
     "node_modules/@rollup/rollup-android-arm64": {
-      "version": "4.9.6",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.9.6.tgz",
-      "integrity": "sha512-T14aNLpqJ5wzKNf5jEDpv5zgyIqcpn1MlwCrUXLrwoADr2RkWA0vOWP4XxbO9aiO3dvMCQICZdKeDrFl7UMClw==",
+      "version": "4.11.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.11.0.tgz",
+      "integrity": "sha512-0ij3iw7sT5jbcdXofWO2NqDNjSVVsf6itcAkV2I6Xsq4+6wjW1A8rViVB67TfBEan7PV2kbLzT8rhOVWLI2YXw==",
       "cpu": [
         "arm64"
       ],
@@ -1482,9 +1482,9 @@
       ]
     },
     "node_modules/@rollup/rollup-darwin-arm64": {
-      "version": "4.9.6",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.9.6.tgz",
-      "integrity": "sha512-CqNNAyhRkTbo8VVZ5R85X73H3R5NX9ONnKbXuHisGWC0qRbTTxnF1U4V9NafzJbgGM0sHZpdO83pLPzq8uOZFw==",
+      "version": "4.11.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.11.0.tgz",
+      "integrity": "sha512-yPLs6RbbBMupArf6qv1UDk6dzZvlH66z6NLYEwqTU0VHtss1wkI4UYeeMS7TVj5QRVvaNAWYKP0TD/MOeZ76Zg==",
       "cpu": [
         "arm64"
       ],
@@ -1495,9 +1495,9 @@
       ]
     },
     "node_modules/@rollup/rollup-darwin-x64": {
-      "version": "4.9.6",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.9.6.tgz",
-      "integrity": "sha512-zRDtdJuRvA1dc9Mp6BWYqAsU5oeLixdfUvkTHuiYOHwqYuQ4YgSmi6+/lPvSsqc/I0Omw3DdICx4Tfacdzmhog==",
+      "version": "4.11.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.11.0.tgz",
+      "integrity": "sha512-OvqIgwaGAwnASzXaZEeoJY3RltOFg+WUbdkdfoluh2iqatd090UeOG3A/h0wNZmE93dDew9tAtXgm3/+U/B6bw==",
       "cpu": [
         "x64"
       ],
@@ -1508,9 +1508,9 @@
       ]
     },
     "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
-      "version": "4.9.6",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.9.6.tgz",
-      "integrity": "sha512-oNk8YXDDnNyG4qlNb6is1ojTOGL/tRhbbKeE/YuccItzerEZT68Z9gHrY3ROh7axDc974+zYAPxK5SH0j/G+QQ==",
+      "version": "4.11.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.11.0.tgz",
+      "integrity": "sha512-X17s4hZK3QbRmdAuLd2EE+qwwxL8JxyVupEqAkxKPa/IgX49ZO+vf0ka69gIKsaYeo6c1CuwY3k8trfDtZ9dFg==",
       "cpu": [
         "arm"
       ],
@@ -1521,9 +1521,9 @@
       ]
     },
     "node_modules/@rollup/rollup-linux-arm64-gnu": {
-      "version": "4.9.6",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.9.6.tgz",
-      "integrity": "sha512-Z3O60yxPtuCYobrtzjo0wlmvDdx2qZfeAWTyfOjEDqd08kthDKexLpV97KfAeUXPosENKd8uyJMRDfFMxcYkDQ==",
+      "version": "4.11.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.11.0.tgz",
+      "integrity": "sha512-673Lu9EJwxVB9NfYeA4AdNu0FOHz7g9t6N1DmT7bZPn1u6bTF+oZjj+fuxUcrfxWXE0r2jxl5QYMa9cUOj9NFg==",
       "cpu": [
         "arm64"
       ],
@@ -1534,9 +1534,9 @@
       ]
     },
     "node_modules/@rollup/rollup-linux-arm64-musl": {
-      "version": "4.9.6",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.9.6.tgz",
-      "integrity": "sha512-gpiG0qQJNdYEVad+1iAsGAbgAnZ8j07FapmnIAQgODKcOTjLEWM9sRb+MbQyVsYCnA0Im6M6QIq6ax7liws6eQ==",
+      "version": "4.11.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.11.0.tgz",
+      "integrity": "sha512-yFW2msTAQNpPJaMmh2NpRalr1KXI7ZUjlN6dY/FhWlOclMrZezm5GIhy3cP4Ts2rIAC+IPLAjNibjp1BsxCVGg==",
       "cpu": [
         "arm64"
       ],
@@ -1547,9 +1547,9 @@
       ]
     },
     "node_modules/@rollup/rollup-linux-riscv64-gnu": {
-      "version": "4.9.6",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.9.6.tgz",
-      "integrity": "sha512-+uCOcvVmFUYvVDr27aiyun9WgZk0tXe7ThuzoUTAukZJOwS5MrGbmSlNOhx1j80GdpqbOty05XqSl5w4dQvcOA==",
+      "version": "4.11.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.11.0.tgz",
+      "integrity": "sha512-kKT9XIuhbvYgiA3cPAGntvrBgzhWkGpBMzuk1V12Xuoqg7CI41chye4HU0vLJnGf9MiZzfNh4I7StPeOzOWJfA==",
       "cpu": [
         "riscv64"
       ],
@@ -1560,9 +1560,9 @@
       ]
     },
     "node_modules/@rollup/rollup-linux-x64-gnu": {
-      "version": "4.9.6",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.9.6.tgz",
-      "integrity": "sha512-HUNqM32dGzfBKuaDUBqFB7tP6VMN74eLZ33Q9Y1TBqRDn+qDonkAUyKWwF9BR9unV7QUzffLnz9GrnKvMqC/fw==",
+      "version": "4.11.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.11.0.tgz",
+      "integrity": "sha512-6q4ESWlyTO+erp1PSCmASac+ixaDv11dBk1fqyIuvIUc/CmRAX2Zk+2qK1FGo5q7kyDcjHCFVwgGFCGIZGVwCA==",
       "cpu": [
         "x64"
       ],
@@ -1573,9 +1573,9 @@
       ]
     },
     "node_modules/@rollup/rollup-linux-x64-musl": {
-      "version": "4.9.6",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.9.6.tgz",
-      "integrity": "sha512-ch7M+9Tr5R4FK40FHQk8VnML0Szi2KRujUgHXd/HjuH9ifH72GUmw6lStZBo3c3GB82vHa0ZoUfjfcM7JiiMrQ==",
+      "version": "4.11.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.11.0.tgz",
+      "integrity": "sha512-vIAQUmXeMLmaDN78HSE4Kh6xqof2e3TJUKr+LPqXWU4NYNON0MDN9h2+t4KHrPAQNmU3w1GxBQ/n01PaWFwa5w==",
       "cpu": [
         "x64"
       ],
@@ -1586,9 +1586,9 @@
       ]
     },
     "node_modules/@rollup/rollup-win32-arm64-msvc": {
-      "version": "4.9.6",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.9.6.tgz",
-      "integrity": "sha512-VD6qnR99dhmTQ1mJhIzXsRcTBvTjbfbGGwKAHcu+52cVl15AC/kplkhxzW/uT0Xl62Y/meBKDZvoJSJN+vTeGA==",
+      "version": "4.11.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.11.0.tgz",
+      "integrity": "sha512-LVXo9dDTGPr0nezMdqa1hK4JeoMZ02nstUxGYY/sMIDtTYlli1ZxTXBYAz3vzuuvKO4X6NBETciIh7N9+abT1g==",
       "cpu": [
         "arm64"
       ],
@@ -1599,9 +1599,9 @@
       ]
     },
     "node_modules/@rollup/rollup-win32-ia32-msvc": {
-      "version": "4.9.6",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.9.6.tgz",
-      "integrity": "sha512-J9AFDq/xiRI58eR2NIDfyVmTYGyIZmRcvcAoJ48oDld/NTR8wyiPUu2X/v1navJ+N/FGg68LEbX3Ejd6l8B7MQ==",
+      "version": "4.11.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.11.0.tgz",
+      "integrity": "sha512-xZVt6K70Gr3I7nUhug2dN6VRR1ibot3rXqXS3wo+8JP64t7djc3lBFyqO4GiVrhNaAIhUCJtwQ/20dr0h0thmQ==",
       "cpu": [
         "ia32"
       ],
@@ -1612,9 +1612,9 @@
       ]
     },
     "node_modules/@rollup/rollup-win32-x64-msvc": {
-      "version": "4.9.6",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.9.6.tgz",
-      "integrity": "sha512-jqzNLhNDvIZOrt69Ce4UjGRpXJBzhUBzawMwnaDAwyHriki3XollsewxWzOzz+4yOFDkuJHtTsZFwMxhYJWmLQ==",
+      "version": "4.11.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.11.0.tgz",
+      "integrity": "sha512-f3I7h9oTg79UitEco9/2bzwdciYkWr8pITs3meSDSlr1TdvQ7IxkQaaYN2YqZXX5uZhiYL+VuYDmHwNzhx+HOg==",
       "cpu": [
         "x64"
       ],
@@ -2082,11 +2082,12 @@
       "dev": true
     },
     "node_modules/@stylistic/eslint-plugin-js": {
-      "version": "1.6.1",
-      "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-js/-/eslint-plugin-js-1.6.1.tgz",
-      "integrity": "sha512-gHRxkbA5p8S1fnChE7Yf5NFltRZCzbCuQOcoTe93PSKBC4GqVjZmlWUSLz9pJKHvDAUTjWkfttWHIOaFYPEhRQ==",
+      "version": "1.6.2",
+      "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-js/-/eslint-plugin-js-1.6.2.tgz",
+      "integrity": "sha512-ndT6X2KgWGxv8101pdMOxL8pihlYIHcOv3ICd70cgaJ9exwkPn8hJj4YQwslxoAlre1TFHnXd/G1/hYXgDrjIA==",
       "dev": true,
       "dependencies": {
+        "@types/eslint": "^8.56.2",
         "acorn": "^8.11.3",
         "escape-string-regexp": "^4.0.0",
         "eslint-visitor-keys": "^3.4.3",
@@ -2225,9 +2226,9 @@
       "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g=="
     },
     "node_modules/@types/node": {
-      "version": "20.11.14",
-      "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.14.tgz",
-      "integrity": "sha512-w3yWCcwULefjP9DmDDsgUskrMoOy5Z8MiwKHr1FvqGPtx7CvJzQvxD7eKpxNtklQxLruxSXWddyeRtyud0RcXQ==",
+      "version": "20.11.19",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.19.tgz",
+      "integrity": "sha512-7xMnVEcZFu0DikYjWOlRq7NTPETrm7teqUT2WkQjrTIkEgUyyGdWsj/Zg8bEJt5TNklzbPD1X3fqfsHw3SpapQ==",
       "dependencies": {
         "undici-types": "~5.26.4"
       }
@@ -2245,9 +2246,9 @@
       "dev": true
     },
     "node_modules/@types/semver": {
-      "version": "7.5.6",
-      "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.6.tgz",
-      "integrity": "sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==",
+      "version": "7.5.7",
+      "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.7.tgz",
+      "integrity": "sha512-/wdoPq1QqkSj9/QOeKkFquEuPzQbHTWAMPH/PaUMB+JuR31lXhlWXRZ52IpfDYVlDOUBvX09uBrPwxGT1hjNBg==",
       "dev": true
     },
     "node_modules/@types/tern": {
@@ -2512,36 +2513,36 @@
       }
     },
     "node_modules/@vue/compiler-core": {
-      "version": "3.4.18",
-      "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.18.tgz",
-      "integrity": "sha512-F7YK8lMK0iv6b9/Gdk15A67wM0KKZvxDxed0RR60C1z9tIJTKta+urs4j0RTN5XqHISzI3etN3mX0uHhjmoqjQ==",
+      "version": "3.4.19",
+      "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.19.tgz",
+      "integrity": "sha512-gj81785z0JNzRcU0Mq98E56e4ltO1yf8k5PQ+tV/7YHnbZkrM0fyFyuttnN8ngJZjbpofWE/m4qjKBiLl8Ju4w==",
       "dependencies": {
         "@babel/parser": "^7.23.9",
-        "@vue/shared": "3.4.18",
+        "@vue/shared": "3.4.19",
         "entities": "^4.5.0",
         "estree-walker": "^2.0.2",
         "source-map-js": "^1.0.2"
       }
     },
     "node_modules/@vue/compiler-dom": {
-      "version": "3.4.18",
-      "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.18.tgz",
-      "integrity": "sha512-24Eb8lcMfInefvQ6YlEVS18w5Q66f4+uXWVA+yb7praKbyjHRNuKVWGuinfSSjM0ZIiPi++QWukhkgznBaqpEA==",
+      "version": "3.4.19",
+      "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.19.tgz",
+      "integrity": "sha512-vm6+cogWrshjqEHTzIDCp72DKtea8Ry/QVpQRYoyTIg9k7QZDX6D8+HGURjtmatfgM8xgCFtJJaOlCaRYRK3QA==",
       "dependencies": {
-        "@vue/compiler-core": "3.4.18",
-        "@vue/shared": "3.4.18"
+        "@vue/compiler-core": "3.4.19",
+        "@vue/shared": "3.4.19"
       }
     },
     "node_modules/@vue/compiler-sfc": {
-      "version": "3.4.18",
-      "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.18.tgz",
-      "integrity": "sha512-rG5tqtnzwrVpMqAQ7FHtvHaV70G6LLfJIWLYZB/jZ9m/hrnZmIQh+H3ewnC5onwe/ibljm9+ZupxeElzqCkTAw==",
+      "version": "3.4.19",
+      "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.19.tgz",
+      "integrity": "sha512-LQ3U4SN0DlvV0xhr1lUsgLCYlwQfUfetyPxkKYu7dkfvx7g3ojrGAkw0AERLOKYXuAGnqFsEuytkdcComei3Yg==",
       "dependencies": {
         "@babel/parser": "^7.23.9",
-        "@vue/compiler-core": "3.4.18",
-        "@vue/compiler-dom": "3.4.18",
-        "@vue/compiler-ssr": "3.4.18",
-        "@vue/shared": "3.4.18",
+        "@vue/compiler-core": "3.4.19",
+        "@vue/compiler-dom": "3.4.19",
+        "@vue/compiler-ssr": "3.4.19",
+        "@vue/shared": "3.4.19",
         "estree-walker": "^2.0.2",
         "magic-string": "^0.30.6",
         "postcss": "^8.4.33",
@@ -2560,57 +2561,57 @@
       }
     },
     "node_modules/@vue/compiler-ssr": {
-      "version": "3.4.18",
-      "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.18.tgz",
-      "integrity": "sha512-hSlv20oUhPxo2UYUacHgGaxtqP0tvFo6ixxxD6JlXIkwzwoZ9eKK6PFQN4hNK/R13JlNyldwWt/fqGBKgWJ6nQ==",
+      "version": "3.4.19",
+      "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.19.tgz",
+      "integrity": "sha512-P0PLKC4+u4OMJ8sinba/5Z/iDT84uMRRlrWzadgLA69opCpI1gG4N55qDSC+dedwq2fJtzmGald05LWR5TFfLw==",
       "dependencies": {
-        "@vue/compiler-dom": "3.4.18",
-        "@vue/shared": "3.4.18"
+        "@vue/compiler-dom": "3.4.19",
+        "@vue/shared": "3.4.19"
       }
     },
     "node_modules/@vue/reactivity": {
-      "version": "3.4.18",
-      "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.18.tgz",
-      "integrity": "sha512-7uda2/I0jpLiRygprDo5Jxs2HJkOVXcOMlyVlY54yRLxoycBpwGJRwJT9EdGB4adnoqJDXVT2BilUAYwI7qvmg==",
+      "version": "3.4.19",
+      "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.19.tgz",
+      "integrity": "sha512-+VcwrQvLZgEclGZRHx4O2XhyEEcKaBi50WbxdVItEezUf4fqRh838Ix6amWTdX0CNb/b6t3Gkz3eOebfcSt+UA==",
       "dependencies": {
-        "@vue/shared": "3.4.18"
+        "@vue/shared": "3.4.19"
       }
     },
     "node_modules/@vue/runtime-core": {
-      "version": "3.4.18",
-      "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.18.tgz",
-      "integrity": "sha512-7mU9diCa+4e+8/wZ7Udw5pwTH10A11sZ1nldmHOUKJnzCwvZxfJqAtw31mIf4T5H2FsLCSBQT3xgioA9vIjyDQ==",
+      "version": "3.4.19",
+      "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.19.tgz",
+      "integrity": "sha512-/Z3tFwOrerJB/oyutmJGoYbuoadphDcJAd5jOuJE86THNZji9pYjZroQ2NFsZkTxOq0GJbb+s2kxTYToDiyZzw==",
       "dependencies": {
-        "@vue/reactivity": "3.4.18",
-        "@vue/shared": "3.4.18"
+        "@vue/reactivity": "3.4.19",
+        "@vue/shared": "3.4.19"
       }
     },
     "node_modules/@vue/runtime-dom": {
-      "version": "3.4.18",
-      "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.18.tgz",
-      "integrity": "sha512-2y1Mkzcw1niSfG7z3Qx+2ir9Gb4hdTkZe5p/I8x1aTIKQE0vY0tPAEUPhZm5tx6183gG3D/KwHG728UR0sIufA==",
+      "version": "3.4.19",
+      "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.19.tgz",
+      "integrity": "sha512-IyZzIDqfNCF0OyZOauL+F4yzjMPN2rPd8nhqPP2N1lBn3kYqJpPHHru+83Rkvo2lHz5mW+rEeIMEF9qY3PB94g==",
       "dependencies": {
-        "@vue/runtime-core": "3.4.18",
-        "@vue/shared": "3.4.18",
+        "@vue/runtime-core": "3.4.19",
+        "@vue/shared": "3.4.19",
         "csstype": "^3.1.3"
       }
     },
     "node_modules/@vue/server-renderer": {
-      "version": "3.4.18",
-      "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.18.tgz",
-      "integrity": "sha512-YJd1wa7mzUN3NRqLEsrwEYWyO+PUBSROIGlCc3J/cvn7Zu6CxhNLgXa8Z4zZ5ja5/nviYO79J1InoPeXgwBTZA==",
+      "version": "3.4.19",
+      "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.19.tgz",
+      "integrity": "sha512-eAj2p0c429RZyyhtMRnttjcSToch+kTWxFPHlzGMkR28ZbF1PDlTcmGmlDxccBuqNd9iOQ7xPRPAGgPVj+YpQw==",
       "dependencies": {
-        "@vue/compiler-ssr": "3.4.18",
-        "@vue/shared": "3.4.18"
+        "@vue/compiler-ssr": "3.4.19",
+        "@vue/shared": "3.4.19"
       },
       "peerDependencies": {
-        "vue": "3.4.18"
+        "vue": "3.4.19"
       }
     },
     "node_modules/@vue/shared": {
-      "version": "3.4.18",
-      "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.18.tgz",
-      "integrity": "sha512-CxouGFxxaW5r1WbrSmWwck3No58rApXgRSBxrqgnY1K+jk20F6DrXJkHdH9n4HVT+/B6G2CAn213Uq3npWiy8Q=="
+      "version": "3.4.19",
+      "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.19.tgz",
+      "integrity": "sha512-/KliRRHMF6LoiThEy+4c1Z4KB/gbPrGjWwJR+crg2otgrf/egKzRaCPvJ51S5oetgsgXLfc4Rm5ZgrKHZrtMSw=="
     },
     "node_modules/@webassemblyjs/ast": {
       "version": "1.11.6",
@@ -2975,13 +2976,16 @@
       "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
     },
     "node_modules/array-buffer-byte-length": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz",
-      "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==",
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz",
+      "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==",
       "dev": true,
       "dependencies": {
-        "call-bind": "^1.0.2",
-        "is-array-buffer": "^3.0.1"
+        "call-bind": "^1.0.5",
+        "is-array-buffer": "^3.0.4"
+      },
+      "engines": {
+        "node": ">= 0.4"
       },
       "funding": {
         "url": "https://github.com/sponsors/ljharb"
@@ -3005,17 +3009,18 @@
       }
     },
     "node_modules/arraybuffer.prototype.slice": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.2.tgz",
-      "integrity": "sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw==",
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz",
+      "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==",
       "dev": true,
       "dependencies": {
-        "array-buffer-byte-length": "^1.0.0",
-        "call-bind": "^1.0.2",
-        "define-properties": "^1.2.0",
-        "es-abstract": "^1.22.1",
-        "get-intrinsic": "^1.2.1",
-        "is-array-buffer": "^3.0.2",
+        "array-buffer-byte-length": "^1.0.1",
+        "call-bind": "^1.0.5",
+        "define-properties": "^1.2.1",
+        "es-abstract": "^1.22.3",
+        "es-errors": "^1.2.1",
+        "get-intrinsic": "^1.2.3",
+        "is-array-buffer": "^3.0.4",
         "is-shared-array-buffer": "^1.0.2"
       },
       "engines": {
@@ -3035,9 +3040,9 @@
       }
     },
     "node_modules/asciinema-player": {
-      "version": "3.6.3",
-      "resolved": "https://registry.npmjs.org/asciinema-player/-/asciinema-player-3.6.3.tgz",
-      "integrity": "sha512-62aDgLpbuduhmpFfNgPOzf6fOluACLsftVnjpWJjUXX6dqhqTckFqWoJ+ayA0XjSlZ9l9wXTcJqRqvAAIpMblg==",
+      "version": "3.6.4",
+      "resolved": "https://registry.npmjs.org/asciinema-player/-/asciinema-player-3.6.4.tgz",
+      "integrity": "sha512-yyMHTjoDuz82/BYPrc3J5GjOtlNI5t2VHTZWss8BmRcY/6nXv+Vilip+XzwIyRBa3/2SSn9FJIEg8bJXBc9o4w==",
       "dependencies": {
         "@babel/runtime": "^7.21.0",
         "solid-js": "^1.3.0"
@@ -3101,9 +3106,9 @@
       }
     },
     "node_modules/available-typed-arrays": {
-      "version": "1.0.5",
-      "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz",
-      "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==",
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.6.tgz",
+      "integrity": "sha512-j1QzY8iPNPG4o4xmO3ptzpRxTciqD3MgEHtifP/YnJpIo58Xu+ne4BejlbkuaLfXn/nz6HFiw29bLpj2PNMdGg==",
       "dev": true,
       "engines": {
         "node": ">= 0.4"
@@ -3170,9 +3175,9 @@
       }
     },
     "node_modules/browserslist": {
-      "version": "4.22.3",
-      "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.3.tgz",
-      "integrity": "sha512-UAp55yfwNv0klWNapjs/ktHoguxuQNGnOzxYmfnXIS+8AsRDZkSDxg7R1AX3GKzn078SBI5dzwzj/Yx0Or0e3A==",
+      "version": "4.23.0",
+      "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz",
+      "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==",
       "funding": [
         {
           "type": "opencollective",
@@ -3188,8 +3193,8 @@
         }
       ],
       "dependencies": {
-        "caniuse-lite": "^1.0.30001580",
-        "electron-to-chromium": "^1.4.648",
+        "caniuse-lite": "^1.0.30001587",
+        "electron-to-chromium": "^1.4.668",
         "node-releases": "^2.0.14",
         "update-browserslist-db": "^1.0.13"
       },
@@ -3256,14 +3261,19 @@
       }
     },
     "node_modules/call-bind": {
-      "version": "1.0.5",
-      "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz",
-      "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==",
+      "version": "1.0.7",
+      "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz",
+      "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==",
       "dev": true,
       "dependencies": {
+        "es-define-property": "^1.0.0",
+        "es-errors": "^1.3.0",
         "function-bind": "^1.1.2",
-        "get-intrinsic": "^1.2.1",
-        "set-function-length": "^1.1.1"
+        "get-intrinsic": "^1.2.4",
+        "set-function-length": "^1.2.1"
+      },
+      "engines": {
+        "node": ">= 0.4"
       },
       "funding": {
         "url": "https://github.com/sponsors/ljharb"
@@ -3279,9 +3289,9 @@
       }
     },
     "node_modules/caniuse-lite": {
-      "version": "1.0.30001581",
-      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001581.tgz",
-      "integrity": "sha512-whlTkwhqV2tUmP3oYhtNfaWGYHDdS3JYFQBKXxcUR9qqPWsRhFHhoISO2Xnl/g0xyKzht9mI1LZpiNWfMzHixQ==",
+      "version": "1.0.30001587",
+      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001587.tgz",
+      "integrity": "sha512-HMFNotUmLXn71BQxg8cijvqxnIAofforZOwGsxyXJ0qugTdspUF4sPSJ2vhgprHCB996tIDzEq1ubumPDV8ULA==",
       "funding": [
         {
           "type": "opencollective",
@@ -3340,9 +3350,9 @@
       }
     },
     "node_modules/chart.js": {
-      "version": "4.3.0",
-      "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.3.0.tgz",
-      "integrity": "sha512-ynG0E79xGfMaV2xAHdbhwiPLczxnNNnasrmPEXriXsPJGjmhOBYzFVEsB65w2qMDz+CaBJJuJD0inE/ab/h36g==",
+      "version": "4.4.1",
+      "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.1.tgz",
+      "integrity": "sha512-C74QN1bxwV1v2PEujhmKjOZ7iUM4w6BWs23Md/6aOZZSlwMzeCIDGuZay++rBgChYru7/+QFeoQW0fQoP534Dg==",
       "dependencies": {
         "@kurkle/color": "^0.3.0"
       },
@@ -3572,12 +3582,12 @@
       "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
     },
     "node_modules/core-js-compat": {
-      "version": "3.35.1",
-      "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.35.1.tgz",
-      "integrity": "sha512-sftHa5qUJY3rs9Zht1WEnmkvXputCyDBczPnr7QDgL8n3qrF3CMXY4VPSYtOLLiOUJcah2WNXREd48iOl6mQIw==",
+      "version": "3.36.0",
+      "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.36.0.tgz",
+      "integrity": "sha512-iV9Pd/PsgjNWBXeq8XRtWVSgz2tKAfhfvBs7qxYty+RlRd+OCksaWmOnc4JKrTc1cToXL1N0s3l/vwlxPtdElw==",
       "dev": true,
       "dependencies": {
-        "browserslist": "^4.22.2"
+        "browserslist": "^4.22.3"
       },
       "funding": {
         "type": "opencollective",
@@ -4327,17 +4337,20 @@
       "dev": true
     },
     "node_modules/define-data-property": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz",
-      "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==",
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
+      "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
       "dev": true,
       "dependencies": {
-        "get-intrinsic": "^1.2.1",
-        "gopd": "^1.0.1",
-        "has-property-descriptors": "^1.0.0"
+        "es-define-property": "^1.0.0",
+        "es-errors": "^1.3.0",
+        "gopd": "^1.0.1"
       },
       "engines": {
         "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
       }
     },
     "node_modules/define-properties": {
@@ -4392,9 +4405,9 @@
       }
     },
     "node_modules/diff": {
-      "version": "5.1.0",
-      "resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz",
-      "integrity": "sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==",
+      "version": "5.2.0",
+      "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz",
+      "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==",
       "engines": {
         "node": ">=0.3.1"
       }
@@ -4520,9 +4533,9 @@
       }
     },
     "node_modules/electron-to-chromium": {
-      "version": "1.4.653",
-      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.653.tgz",
-      "integrity": "sha512-wA2A2LQCqnEwQAvwADQq3KpMpNwgAUBnRmrFgRzHnPhbQUFArTR32Ab46f4p0MovDLcg4uqd4nCsN2hTltslpA=="
+      "version": "1.4.671",
+      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.671.tgz",
+      "integrity": "sha512-UUlE+/rWbydmp+FW8xlnnTA5WNA0ZZd2XL8CuMS72rh+k4y1f8+z6yk3UQhEwqHQWj6IBdL78DwWOdGMvYfQyA=="
     },
     "node_modules/elkjs": {
       "version": "0.9.1",
@@ -4575,9 +4588,9 @@
       }
     },
     "node_modules/envinfo": {
-      "version": "7.11.0",
-      "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.11.0.tgz",
-      "integrity": "sha512-G9/6xF1FPbIw0TtalAMaVPpiq2aDEuKLXM314jPVAO9r2fo2a4BLqMNkmRS7O/xPPZ+COAhGIz3ETvHEV3eUcg==",
+      "version": "7.11.1",
+      "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.11.1.tgz",
+      "integrity": "sha512-8PiZgZNIB4q/Lw4AhOvAfB/ityHAd2bli3lESSWmWSzSsl5dKpy5N1d1Rfkd2teq/g9xN90lc6o98DOjMeYHpg==",
       "bin": {
         "envinfo": "dist/cli.js"
       },
@@ -4595,50 +4608,52 @@
       }
     },
     "node_modules/es-abstract": {
-      "version": "1.22.3",
-      "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.3.tgz",
-      "integrity": "sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA==",
+      "version": "1.22.4",
+      "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.4.tgz",
+      "integrity": "sha512-vZYJlk2u6qHYxBOTjAeg7qUxHdNfih64Uu2J8QqWgXZ2cri0ZpJAkzDUK/q593+mvKwlxyaxr6F1Q+3LKoQRgg==",
       "dev": true,
       "dependencies": {
-        "array-buffer-byte-length": "^1.0.0",
-        "arraybuffer.prototype.slice": "^1.0.2",
-        "available-typed-arrays": "^1.0.5",
-        "call-bind": "^1.0.5",
-        "es-set-tostringtag": "^2.0.1",
+        "array-buffer-byte-length": "^1.0.1",
+        "arraybuffer.prototype.slice": "^1.0.3",
+        "available-typed-arrays": "^1.0.6",
+        "call-bind": "^1.0.7",
+        "es-define-property": "^1.0.0",
+        "es-errors": "^1.3.0",
+        "es-set-tostringtag": "^2.0.2",
         "es-to-primitive": "^1.2.1",
         "function.prototype.name": "^1.1.6",
-        "get-intrinsic": "^1.2.2",
-        "get-symbol-description": "^1.0.0",
+        "get-intrinsic": "^1.2.4",
+        "get-symbol-description": "^1.0.2",
         "globalthis": "^1.0.3",
         "gopd": "^1.0.1",
-        "has-property-descriptors": "^1.0.0",
+        "has-property-descriptors": "^1.0.2",
         "has-proto": "^1.0.1",
         "has-symbols": "^1.0.3",
-        "hasown": "^2.0.0",
-        "internal-slot": "^1.0.5",
-        "is-array-buffer": "^3.0.2",
+        "hasown": "^2.0.1",
+        "internal-slot": "^1.0.7",
+        "is-array-buffer": "^3.0.4",
         "is-callable": "^1.2.7",
         "is-negative-zero": "^2.0.2",
         "is-regex": "^1.1.4",
         "is-shared-array-buffer": "^1.0.2",
         "is-string": "^1.0.7",
-        "is-typed-array": "^1.1.12",
+        "is-typed-array": "^1.1.13",
         "is-weakref": "^1.0.2",
         "object-inspect": "^1.13.1",
         "object-keys": "^1.1.1",
-        "object.assign": "^4.1.4",
-        "regexp.prototype.flags": "^1.5.1",
-        "safe-array-concat": "^1.0.1",
-        "safe-regex-test": "^1.0.0",
+        "object.assign": "^4.1.5",
+        "regexp.prototype.flags": "^1.5.2",
+        "safe-array-concat": "^1.1.0",
+        "safe-regex-test": "^1.0.3",
         "string.prototype.trim": "^1.2.8",
         "string.prototype.trimend": "^1.0.7",
         "string.prototype.trimstart": "^1.0.7",
-        "typed-array-buffer": "^1.0.0",
+        "typed-array-buffer": "^1.0.1",
         "typed-array-byte-length": "^1.0.0",
         "typed-array-byte-offset": "^1.0.0",
         "typed-array-length": "^1.0.4",
         "unbox-primitive": "^1.0.2",
-        "which-typed-array": "^1.1.13"
+        "which-typed-array": "^1.1.14"
       },
       "engines": {
         "node": ">= 0.4"
@@ -4648,18 +4663,18 @@
       }
     },
     "node_modules/es-aggregate-error": {
-      "version": "1.0.11",
-      "resolved": "https://registry.npmjs.org/es-aggregate-error/-/es-aggregate-error-1.0.11.tgz",
-      "integrity": "sha512-DCiZiNlMlbvofET/cE55My387NiLvuGToBEZDdK9U2G3svDCjL8WOgO5Il6lO83nQ8qmag/R9nArdpaFQ/m3lA==",
+      "version": "1.0.12",
+      "resolved": "https://registry.npmjs.org/es-aggregate-error/-/es-aggregate-error-1.0.12.tgz",
+      "integrity": "sha512-j0PupcmELoVbYS2NNrsn5zcLLEsryQwP02x8fRawh7c2eEaPHwJFAxltZsqV7HJjsF57+SMpYyVRWgbVLfOagg==",
       "dev": true,
       "dependencies": {
-        "define-data-property": "^1.1.0",
+        "define-data-property": "^1.1.1",
         "define-properties": "^1.2.1",
-        "es-abstract": "^1.22.1",
-        "function-bind": "^1.1.1",
-        "get-intrinsic": "^1.2.1",
+        "es-abstract": "^1.22.3",
+        "es-errors": "^1.1.0",
+        "function-bind": "^1.1.2",
         "globalthis": "^1.0.3",
-        "has-property-descriptors": "^1.0.0",
+        "has-property-descriptors": "^1.0.1",
         "set-function-name": "^2.0.1"
       },
       "engines": {
@@ -4669,6 +4684,27 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/es-define-property": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz",
+      "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==",
+      "dev": true,
+      "dependencies": {
+        "get-intrinsic": "^1.2.4"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/es-errors": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+      "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+      "dev": true,
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
     "node_modules/es-module-lexer": {
       "version": "1.4.1",
       "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.4.1.tgz",
@@ -4760,9 +4796,9 @@
       }
     },
     "node_modules/escalade": {
-      "version": "3.1.1",
-      "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
-      "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz",
+      "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==",
       "engines": {
         "node": ">=6"
       }
@@ -5020,12 +5056,12 @@
       }
     },
     "node_modules/eslint-plugin-sonarjs": {
-      "version": "0.23.0",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-sonarjs/-/eslint-plugin-sonarjs-0.23.0.tgz",
-      "integrity": "sha512-z44T3PBf9W7qQ/aR+NmofOTyg6HLhSEZOPD4zhStqBpLoMp8GYhFksuUBnCxbnf1nfISpKBVkQhiBLFI/F4Wlg==",
+      "version": "0.24.0",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-sonarjs/-/eslint-plugin-sonarjs-0.24.0.tgz",
+      "integrity": "sha512-87zp50mbbNrSTuoEOebdRQBPa0mdejA5UEjyuScyIw8hEpEjfWP89Qhkq5xVZfVyVSRQKZc9alVm7yRKQvvUmg==",
       "dev": true,
       "engines": {
-        "node": ">=14"
+        "node": ">=16"
       },
       "peerDependencies": {
         "eslint": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0"
@@ -5391,9 +5427,9 @@
       }
     },
     "node_modules/fastq": {
-      "version": "1.17.0",
-      "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.0.tgz",
-      "integrity": "sha512-zGygtijUMT7jnk3h26kUms3BkSDp4IfIKjmnqI2tvx6nuBfiF1UqOxbnLfzdv+apBy+53oaImsKtMw/xYbW+1w==",
+      "version": "1.17.1",
+      "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz",
+      "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==",
       "dependencies": {
         "reusify": "^1.0.4"
       }
@@ -5648,16 +5684,20 @@
       }
     },
     "node_modules/get-intrinsic": {
-      "version": "1.2.2",
-      "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz",
-      "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==",
+      "version": "1.2.4",
+      "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz",
+      "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==",
       "dev": true,
       "dependencies": {
+        "es-errors": "^1.3.0",
         "function-bind": "^1.1.2",
         "has-proto": "^1.0.1",
         "has-symbols": "^1.0.3",
         "hasown": "^2.0.0"
       },
+      "engines": {
+        "node": ">= 0.4"
+      },
       "funding": {
         "url": "https://github.com/sponsors/ljharb"
       }
@@ -5706,13 +5746,14 @@
       }
     },
     "node_modules/get-symbol-description": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz",
-      "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==",
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz",
+      "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==",
       "dev": true,
       "dependencies": {
-        "call-bind": "^1.0.2",
-        "get-intrinsic": "^1.1.1"
+        "call-bind": "^1.0.5",
+        "es-errors": "^1.3.0",
+        "get-intrinsic": "^1.2.4"
       },
       "engines": {
         "node": ">= 0.4"
@@ -5912,9 +5953,9 @@
       "dev": true
     },
     "node_modules/gsap": {
-      "version": "3.12.2",
-      "resolved": "https://registry.npmjs.org/gsap/-/gsap-3.12.2.tgz",
-      "integrity": "sha512-EkYnpG8qHgYBFAwsgsGEqvT1WUidX0tt/ijepx7z8EUJHElykg91RvW1XbkT59T0gZzzszOpjQv7SE41XuIXyQ=="
+      "version": "3.12.5",
+      "resolved": "https://registry.npmjs.org/gsap/-/gsap-3.12.5.tgz",
+      "integrity": "sha512-srBfnk4n+Oe/ZnMIOXt3gT605BX9x5+rh/prT2F1SsNJsU1XuMiP0E2aptW481OnonOGACZWBqseH5Z7csHxhQ=="
     },
     "node_modules/hammerjs": {
       "version": "2.0.8",
@@ -5942,12 +5983,12 @@
       }
     },
     "node_modules/has-property-descriptors": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz",
-      "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==",
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
+      "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
       "dev": true,
       "dependencies": {
-        "get-intrinsic": "^1.2.2"
+        "es-define-property": "^1.0.0"
       },
       "funding": {
         "url": "https://github.com/sponsors/ljharb"
@@ -5978,12 +6019,12 @@
       }
     },
     "node_modules/has-tostringtag": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz",
-      "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==",
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
+      "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
       "dev": true,
       "dependencies": {
-        "has-symbols": "^1.0.2"
+        "has-symbols": "^1.0.3"
       },
       "engines": {
         "node": ">= 0.4"
@@ -5998,9 +6039,9 @@
       "integrity": "sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg=="
     },
     "node_modules/hasown": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz",
-      "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==",
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.1.tgz",
+      "integrity": "sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==",
       "dependencies": {
         "function-bind": "^1.1.2"
       },
@@ -6077,9 +6118,9 @@
       "integrity": "sha512-UgchasltTCrTuU2DQLom3ohHrBvwr7OqpwyAVJ9VxtNBng4XKkVsqrv0Qr3srqvM9ZNI3f1MmvVQQqK7KW/bTA=="
     },
     "node_modules/http-proxy-agent": {
-      "version": "7.0.0",
-      "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.0.tgz",
-      "integrity": "sha512-+ZT+iBxVUQ1asugqnD6oWoRiS25AkjNfG085dKJGtGxkdwLQrMKU5wJr2bOOFAXzKcTuqq+7fZlTMgG3SRfIYQ==",
+      "version": "7.0.2",
+      "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz",
+      "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==",
       "dev": true,
       "dependencies": {
         "agent-base": "^7.1.0",
@@ -6090,9 +6131,9 @@
       }
     },
     "node_modules/https-proxy-agent": {
-      "version": "7.0.2",
-      "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.2.tgz",
-      "integrity": "sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA==",
+      "version": "7.0.4",
+      "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.4.tgz",
+      "integrity": "sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==",
       "dev": true,
       "dependencies": {
         "agent-base": "^7.0.2",
@@ -6153,9 +6194,9 @@
       ]
     },
     "node_modules/ignore": {
-      "version": "5.3.0",
-      "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz",
-      "integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==",
+      "version": "5.3.1",
+      "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz",
+      "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==",
       "dev": true,
       "engines": {
         "node": ">= 4"
@@ -6247,12 +6288,12 @@
       }
     },
     "node_modules/internal-slot": {
-      "version": "1.0.6",
-      "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.6.tgz",
-      "integrity": "sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg==",
+      "version": "1.0.7",
+      "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz",
+      "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==",
       "dev": true,
       "dependencies": {
-        "get-intrinsic": "^1.2.2",
+        "es-errors": "^1.3.0",
         "hasown": "^2.0.0",
         "side-channel": "^1.0.4"
       },
@@ -6277,14 +6318,16 @@
       }
     },
     "node_modules/is-array-buffer": {
-      "version": "3.0.2",
-      "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz",
-      "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==",
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz",
+      "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==",
       "dev": true,
       "dependencies": {
         "call-bind": "^1.0.2",
-        "get-intrinsic": "^1.2.0",
-        "is-typed-array": "^1.1.10"
+        "get-intrinsic": "^1.2.1"
+      },
+      "engines": {
+        "node": ">= 0.4"
       },
       "funding": {
         "url": "https://github.com/sponsors/ljharb"
@@ -6582,12 +6625,12 @@
       }
     },
     "node_modules/is-typed-array": {
-      "version": "1.1.12",
-      "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz",
-      "integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==",
+      "version": "1.1.13",
+      "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz",
+      "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==",
       "dev": true,
       "dependencies": {
-        "which-typed-array": "^1.1.11"
+        "which-typed-array": "^1.1.14"
       },
       "engines": {
         "node": ">= 0.4"
@@ -6693,9 +6736,9 @@
       "dev": true
     },
     "node_modules/js-tokens": {
-      "version": "8.0.2",
-      "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-8.0.2.tgz",
-      "integrity": "sha512-Olnt+V7xYdvGze9YTbGFZIfQXuGV4R3nQwwl8BrtgaPE/wq8UFpUHWuTNc05saowhSr1ZO6tx+V6RjE9D5YQog==",
+      "version": "8.0.3",
+      "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-8.0.3.tgz",
+      "integrity": "sha512-UfJMcSJc+SEXEl9lH/VLHSZbThQyLpw1vLO1Lb+j4RWDvG3N2f7yj3PVQA3cmkTBNldJ9eFnM+xEXxHIXrYiJw==",
       "dev": true
     },
     "node_modules/js-types": {
@@ -7344,9 +7387,9 @@
       "dev": true
     },
     "node_modules/meow": {
-      "version": "13.1.0",
-      "resolved": "https://registry.npmjs.org/meow/-/meow-13.1.0.tgz",
-      "integrity": "sha512-o5R/R3Tzxq0PJ3v3qcQJtSvSE9nKOLSAaDuuoMzDVuGTwHdccMWcYomh9Xolng2tjT6O/Y83d+0coVGof6tqmA==",
+      "version": "13.2.0",
+      "resolved": "https://registry.npmjs.org/meow/-/meow-13.2.0.tgz",
+      "integrity": "sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==",
       "dev": true,
       "engines": {
         "node": ">=18"
@@ -8421,9 +8464,9 @@
       }
     },
     "node_modules/pdfobject": {
-      "version": "2.2.12",
-      "resolved": "https://registry.npmjs.org/pdfobject/-/pdfobject-2.2.12.tgz",
-      "integrity": "sha512-D0oyD/sj8j82AMaJhoyMaY1aD5TkbpU3FbJC6w9/cpJlZRpYHqAkutXw1Ca/FKjYPZmTAu58uGIfgOEaDlbY8A=="
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/pdfobject/-/pdfobject-2.3.0.tgz",
+      "integrity": "sha512-w/9pXDXTDs3IDmOri/w8lM/w6LHR0/F4fcBLLzH+4csSoyshQ5su0TE7k0FLHZO7aOjVLDGecqd1M89+PVpVAA=="
     },
     "node_modules/picocolors": {
       "version": "1.0.0",
@@ -8566,9 +8609,9 @@
       }
     },
     "node_modules/postcss": {
-      "version": "8.4.33",
-      "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.33.tgz",
-      "integrity": "sha512-Kkpbhhdjw2qQs2O2DGX+8m5OVqEcbB9HRBvuYM9pgrjEFUg30A9LmXNlTAUj4S9kgtGyrMbTzVjH7E+s5Re2yg==",
+      "version": "8.4.35",
+      "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz",
+      "integrity": "sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==",
       "funding": [
         {
           "type": "opencollective",
@@ -9025,14 +9068,15 @@
       }
     },
     "node_modules/regexp.prototype.flags": {
-      "version": "1.5.1",
-      "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz",
-      "integrity": "sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==",
+      "version": "1.5.2",
+      "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz",
+      "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==",
       "dev": true,
       "dependencies": {
-        "call-bind": "^1.0.2",
-        "define-properties": "^1.2.0",
-        "set-function-name": "^2.0.0"
+        "call-bind": "^1.0.6",
+        "define-properties": "^1.2.1",
+        "es-errors": "^1.3.0",
+        "set-function-name": "^2.0.1"
       },
       "engines": {
         "node": ">= 0.4"
@@ -9287,13 +9331,13 @@
       ]
     },
     "node_modules/safe-regex-test": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.2.tgz",
-      "integrity": "sha512-83S9w6eFq12BBIJYvjMux6/dkirb8+4zJRA9cxNBVb7Wq5fJBW+Xze48WqR8pxua7bDuAaaAxtVVd4Idjp1dBQ==",
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz",
+      "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==",
       "dev": true,
       "dependencies": {
-        "call-bind": "^1.0.5",
-        "get-intrinsic": "^1.2.2",
+        "call-bind": "^1.0.6",
+        "es-errors": "^1.3.0",
         "is-regex": "^1.1.4"
       },
       "engines": {
@@ -9365,9 +9409,9 @@
       }
     },
     "node_modules/semver": {
-      "version": "7.5.4",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
-      "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
+      "version": "7.6.0",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz",
+      "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==",
       "dependencies": {
         "lru-cache": "^6.0.0"
       },
@@ -9406,14 +9450,15 @@
       }
     },
     "node_modules/set-function-length": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.0.tgz",
-      "integrity": "sha512-4DBHDoyHlM1IRPGYcoxexgh67y4ueR53FKV1yyxwFMY7aCqcN/38M1+SwZ/qJQ8iLv7+ck385ot4CcisOAPT9w==",
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.1.tgz",
+      "integrity": "sha512-j4t6ccc+VsKwYHso+kElc5neZpjtq9EnRICFZtWyBsLojhmeF/ZBd/elqm22WJh/BziDe/SBiOeAt0m2mfLD0g==",
       "dev": true,
       "dependencies": {
-        "define-data-property": "^1.1.1",
+        "define-data-property": "^1.1.2",
+        "es-errors": "^1.3.0",
         "function-bind": "^1.1.2",
-        "get-intrinsic": "^1.2.2",
+        "get-intrinsic": "^1.2.3",
         "gopd": "^1.0.1",
         "has-property-descriptors": "^1.0.1"
       },
@@ -9466,14 +9511,18 @@
       }
     },
     "node_modules/side-channel": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
-      "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.5.tgz",
+      "integrity": "sha512-QcgiIWV4WV7qWExbN5llt6frQB/lBven9pqliLXfGPB+K9ZYXxDozp0wLkHS24kWCm+6YXH/f0HhnObZnZOBnQ==",
       "dev": true,
       "dependencies": {
-        "call-bind": "^1.0.0",
-        "get-intrinsic": "^1.0.2",
-        "object-inspect": "^1.9.0"
+        "call-bind": "^1.0.6",
+        "es-errors": "^1.3.0",
+        "get-intrinsic": "^1.2.4",
+        "object-inspect": "^1.13.1"
+      },
+      "engines": {
+        "node": ">= 0.4"
       },
       "funding": {
         "url": "https://github.com/sponsors/ljharb"
@@ -9536,9 +9585,9 @@
       }
     },
     "node_modules/solid-js": {
-      "version": "1.8.12",
-      "resolved": "https://registry.npmjs.org/solid-js/-/solid-js-1.8.12.tgz",
-      "integrity": "sha512-sLE/i6M9FSWlov3a2pTC5ISzanH2aKwqXTZj+bbFt4SUrVb4iGEa7fpILBMOxsQjkv3eXqEk6JVLlogOdTe0UQ==",
+      "version": "1.8.15",
+      "resolved": "https://registry.npmjs.org/solid-js/-/solid-js-1.8.15.tgz",
+      "integrity": "sha512-d0QP/efr3UVcwGgWVPveQQ0IHOH6iU7yUhc2piy8arNG8wxKmvUy1kFxyF8owpmfCWGB87usDKMaVnsNYZm+Vw==",
       "dependencies": {
         "csstype": "^3.1.0",
         "seroval": "^1.0.3",
@@ -9619,9 +9668,9 @@
       }
     },
     "node_modules/spdx-exceptions": {
-      "version": "2.4.0",
-      "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.4.0.tgz",
-      "integrity": "sha512-hcjppoJ68fhxA/cjbN4T8N6uCUejN8yFw69ttpqtBeCbF3u13n7mb31NB9jKwGTTWWnt9IbRA/mf1FprYS8wfw=="
+      "version": "2.5.0",
+      "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz",
+      "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w=="
     },
     "node_modules/spdx-expression-parse": {
       "version": "3.0.1",
@@ -9641,9 +9690,9 @@
       }
     },
     "node_modules/spdx-license-ids": {
-      "version": "3.0.16",
-      "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.16.tgz",
-      "integrity": "sha512-eWN+LnM3GR6gPu35WxNgbGl8rmY1AEmoMDvL/QD6zYmPWgywxWqJWNdLGT+ke8dKNWrcYgYjPpG5gbTfghP8rw=="
+      "version": "3.0.17",
+      "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.17.tgz",
+      "integrity": "sha512-sh8PWc/ftMqAAdFiBu6Fy6JUOYjqDJBJvIhpfDMyHrr0Rbp5liZqd4TjtQ/RgfLjKFZb+LMx5hpml5qOWy0qvg=="
     },
     "node_modules/spdx-ranges": {
       "version": "2.1.1",
@@ -10162,9 +10211,9 @@
       }
     },
     "node_modules/swagger-ui-dist": {
-      "version": "5.11.3",
-      "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.11.3.tgz",
-      "integrity": "sha512-vQ+Pe73xt7vMVbX40L6nHu4sDmNCM6A+eMVJPGvKrifHQ4LO3smH0jCiiefKzsVl7OlOcVEnrZ9IFzYwElfMkA=="
+      "version": "5.11.6",
+      "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.11.6.tgz",
+      "integrity": "sha512-K5BpYuMoPpJY7NwCHIWohH6tU9o0fs1+plNT5KJ+3BBlVEh4H1CpeKJV8o91lpscVY9oqb2jmaAassnW3wVoTg=="
     },
     "node_modules/symbol-tree": {
       "version": "3.2.4",
@@ -10209,9 +10258,9 @@
       }
     },
     "node_modules/terser": {
-      "version": "5.27.0",
-      "resolved": "https://registry.npmjs.org/terser/-/terser-5.27.0.tgz",
-      "integrity": "sha512-bi1HRwVRskAjheeYl291n3JC4GgO/Ty4z1nVs5AAsmonJulGxpSektecnNedrwK9C7vpvVtcX3cw00VSLt7U2A==",
+      "version": "5.27.1",
+      "resolved": "https://registry.npmjs.org/terser/-/terser-5.27.1.tgz",
+      "integrity": "sha512-29wAr6UU/oQpnTw5HoadwjUZnFQXGdOfj0LjZ4sVxzqwHh/QVkvr7m8y9WoR4iN3FRitVduTc6KdjcW38Npsug==",
       "dependencies": {
         "@jridgewell/source-map": "^0.3.3",
         "acorn": "^8.8.2",
@@ -10343,9 +10392,9 @@
       }
     },
     "node_modules/tinyspy": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-2.2.0.tgz",
-      "integrity": "sha512-d2eda04AN/cPOR89F7Xv5bK/jrQEhmcLFe6HFldoeO9AJtps+fqEnh486vnT/8y4bw38pSyxDcTCAq+Ks2aJTg==",
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-2.2.1.tgz",
+      "integrity": "sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==",
       "dev": true,
       "engines": {
         "node": ">=14.0.0"
@@ -10476,14 +10525,14 @@
       }
     },
     "node_modules/typed-array-buffer": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz",
-      "integrity": "sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==",
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.1.tgz",
+      "integrity": "sha512-RSqu1UEuSlrBhHTWC8O9FnPjOduNs4M7rJ4pRKoEjtx1zUNOPN2sSXHLDX+Y2WPbHIxbvg4JFo2DNAEfPIKWoQ==",
       "dev": true,
       "dependencies": {
-        "call-bind": "^1.0.2",
-        "get-intrinsic": "^1.2.1",
-        "is-typed-array": "^1.1.10"
+        "call-bind": "^1.0.6",
+        "es-errors": "^1.3.0",
+        "is-typed-array": "^1.1.13"
       },
       "engines": {
         "node": ">= 0.4"
@@ -10555,9 +10604,9 @@
       }
     },
     "node_modules/typo-js": {
-      "version": "1.2.3",
-      "resolved": "https://registry.npmjs.org/typo-js/-/typo-js-1.2.3.tgz",
-      "integrity": "sha512-67Hyl94beZX8gmTap7IDPrG5hy2cHftgsCAcGvE1tzuxGT+kRB+zSBin0wIMwysYw8RUCBCvv9UfQl8TNM75dA=="
+      "version": "1.2.4",
+      "resolved": "https://registry.npmjs.org/typo-js/-/typo-js-1.2.4.tgz",
+      "integrity": "sha512-Oy/k+tFle5NAA3J/yrrYGfvEnPVrDZ8s8/WCwjUE75k331QyKIsFss7byQ/PzBmXLY6h1moRnZbnaxWBe3I3CA=="
     },
     "node_modules/uc.micro": {
       "version": "2.0.0",
@@ -10745,13 +10794,13 @@
       }
     },
     "node_modules/vite": {
-      "version": "5.0.12",
-      "resolved": "https://registry.npmjs.org/vite/-/vite-5.0.12.tgz",
-      "integrity": "sha512-4hsnEkG3q0N4Tzf1+t6NdN9dg/L3BM+q8SWgbSPnJvrgH2kgdyzfVJwbR1ic69/4uMJJ/3dqDZZE5/WwqW8U1w==",
+      "version": "5.1.3",
+      "resolved": "https://registry.npmjs.org/vite/-/vite-5.1.3.tgz",
+      "integrity": "sha512-UfmUD36DKkqhi/F75RrxvPpry+9+tTkrXfMNZD+SboZqBCMsxKtO52XeGzzuh7ioz+Eo/SYDBbdb0Z7vgcDJew==",
       "dev": true,
       "dependencies": {
         "esbuild": "^0.19.3",
-        "postcss": "^8.4.32",
+        "postcss": "^8.4.35",
         "rollup": "^4.2.0"
       },
       "bin": {
@@ -10848,9 +10897,9 @@
       }
     },
     "node_modules/vite/node_modules/rollup": {
-      "version": "4.9.6",
-      "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.9.6.tgz",
-      "integrity": "sha512-05lzkCS2uASX0CiLFybYfVkwNbKZG5NFQ6Go0VWyogFTXXbR039UVsegViTntkk4OglHBdF54ccApXRRuXRbsg==",
+      "version": "4.11.0",
+      "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.11.0.tgz",
+      "integrity": "sha512-2xIbaXDXjf3u2tajvA5xROpib7eegJ9Y/uPlSFhXLNpK9ampCczXAhLEb5yLzJyG3LAdI1NWtNjDXiLyniNdjQ==",
       "dev": true,
       "dependencies": {
         "@types/estree": "1.0.5"
@@ -10863,19 +10912,19 @@
         "npm": ">=8.0.0"
       },
       "optionalDependencies": {
-        "@rollup/rollup-android-arm-eabi": "4.9.6",
-        "@rollup/rollup-android-arm64": "4.9.6",
-        "@rollup/rollup-darwin-arm64": "4.9.6",
-        "@rollup/rollup-darwin-x64": "4.9.6",
-        "@rollup/rollup-linux-arm-gnueabihf": "4.9.6",
-        "@rollup/rollup-linux-arm64-gnu": "4.9.6",
-        "@rollup/rollup-linux-arm64-musl": "4.9.6",
-        "@rollup/rollup-linux-riscv64-gnu": "4.9.6",
-        "@rollup/rollup-linux-x64-gnu": "4.9.6",
-        "@rollup/rollup-linux-x64-musl": "4.9.6",
-        "@rollup/rollup-win32-arm64-msvc": "4.9.6",
-        "@rollup/rollup-win32-ia32-msvc": "4.9.6",
-        "@rollup/rollup-win32-x64-msvc": "4.9.6",
+        "@rollup/rollup-android-arm-eabi": "4.11.0",
+        "@rollup/rollup-android-arm64": "4.11.0",
+        "@rollup/rollup-darwin-arm64": "4.11.0",
+        "@rollup/rollup-darwin-x64": "4.11.0",
+        "@rollup/rollup-linux-arm-gnueabihf": "4.11.0",
+        "@rollup/rollup-linux-arm64-gnu": "4.11.0",
+        "@rollup/rollup-linux-arm64-musl": "4.11.0",
+        "@rollup/rollup-linux-riscv64-gnu": "4.11.0",
+        "@rollup/rollup-linux-x64-gnu": "4.11.0",
+        "@rollup/rollup-linux-x64-musl": "4.11.0",
+        "@rollup/rollup-win32-arm64-msvc": "4.11.0",
+        "@rollup/rollup-win32-ia32-msvc": "4.11.0",
+        "@rollup/rollup-win32-x64-msvc": "4.11.0",
         "fsevents": "~2.3.2"
       }
     },
@@ -10958,15 +11007,15 @@
       }
     },
     "node_modules/vue": {
-      "version": "3.4.18",
-      "resolved": "https://registry.npmjs.org/vue/-/vue-3.4.18.tgz",
-      "integrity": "sha512-0zLRYamFRe0wF4q2L3O24KQzLyLpL64ye1RUToOgOxuWZsb/FhaNRdGmeozdtVYLz6tl94OXLaK7/WQIrVCw1A==",
+      "version": "3.4.19",
+      "resolved": "https://registry.npmjs.org/vue/-/vue-3.4.19.tgz",
+      "integrity": "sha512-W/7Fc9KUkajFU8dBeDluM4sRGc/aa4YJnOYck8dkjgZoXtVsn3OeTGni66FV1l3+nvPA7VBFYtPioaGKUmEADw==",
       "dependencies": {
-        "@vue/compiler-dom": "3.4.18",
-        "@vue/compiler-sfc": "3.4.18",
-        "@vue/runtime-dom": "3.4.18",
-        "@vue/server-renderer": "3.4.18",
-        "@vue/shared": "3.4.18"
+        "@vue/compiler-dom": "3.4.19",
+        "@vue/compiler-sfc": "3.4.19",
+        "@vue/runtime-dom": "3.4.19",
+        "@vue/server-renderer": "3.4.19",
+        "@vue/shared": "3.4.19"
       },
       "peerDependencies": {
         "typescript": "*"
@@ -11100,9 +11149,9 @@
       }
     },
     "node_modules/webpack": {
-      "version": "5.90.1",
-      "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.90.1.tgz",
-      "integrity": "sha512-SstPdlAC5IvgFnhiRok8hqJo/+ArAbNv7rhU4fnWGHNVfN59HSQFaxZDSAL3IFG2YmqxuRs+IU33milSxbPlog==",
+      "version": "5.90.2",
+      "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.90.2.tgz",
+      "integrity": "sha512-ziXu8ABGr0InCMEYFnHrYweinHK2PWrMqnwdHk2oK3rRhv/1B+2FnfwYv5oD+RrknK/Pp/Hmyvu+eAsaMYhzCw==",
       "dependencies": {
         "@types/eslint-scope": "^3.7.3",
         "@types/estree": "^1.0.5",
@@ -11362,16 +11411,16 @@
       }
     },
     "node_modules/which-typed-array": {
-      "version": "1.1.13",
-      "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.13.tgz",
-      "integrity": "sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow==",
+      "version": "1.1.14",
+      "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.14.tgz",
+      "integrity": "sha512-VnXFiIW8yNn9kIHN88xvZ4yOWchftKDsRJ8fEPacX/wl1lOvBrhsJ/OeJCXq7B0AaijRuqgzSKalJoPk+D8MPg==",
       "dev": true,
       "dependencies": {
-        "available-typed-arrays": "^1.0.5",
-        "call-bind": "^1.0.4",
+        "available-typed-arrays": "^1.0.6",
+        "call-bind": "^1.0.5",
         "for-each": "^0.3.3",
         "gopd": "^1.0.1",
-        "has-tostringtag": "^1.0.0"
+        "has-tostringtag": "^1.0.1"
       },
       "engines": {
         "node": ">= 0.4"
diff --git a/package.json b/package.json
index dbb57b1624..ac79741711 100644
--- a/package.json
+++ b/package.json
@@ -17,8 +17,8 @@
     "@webcomponents/custom-elements": "1.6.0",
     "add-asset-webpack-plugin": "2.0.1",
     "ansi_up": "6.0.2",
-    "asciinema-player": "3.6.3",
-    "chart.js": "4.3.0",
+    "asciinema-player": "3.6.4",
+    "chart.js": "4.4.1",
     "chartjs-adapter-dayjs-4": "1.0.4",
     "chartjs-plugin-zoom": "2.0.1",
     "clippie": "4.0.6",
@@ -38,22 +38,22 @@
     "minimatch": "9.0.3",
     "monaco-editor": "0.46.0",
     "monaco-editor-webpack-plugin": "7.1.0",
-    "pdfobject": "2.2.12",
+    "pdfobject": "2.3.0",
     "pretty-ms": "9.0.0",
     "sortablejs": "1.15.2",
-    "swagger-ui-dist": "5.11.3",
+    "swagger-ui-dist": "5.11.6",
     "throttle-debounce": "5.0.0",
     "tinycolor2": "1.6.0",
     "tippy.js": "6.3.7",
     "toastify-js": "1.12.0",
     "tributejs": "5.1.3",
     "uint8-to-base64": "0.2.0",
-    "vue": "3.4.18",
+    "vue": "3.4.19",
     "vue-bar-graph": "2.0.0",
     "vue-chartjs": "5.3.0",
     "vue-loader": "17.4.2",
     "vue3-calendar-heatmap": "2.0.5",
-    "webpack": "5.90.1",
+    "webpack": "5.90.2",
     "webpack-cli": "5.1.4",
     "wrap-ansi": "9.0.0"
   },
@@ -61,7 +61,7 @@
     "@eslint-community/eslint-plugin-eslint-comments": "4.1.0",
     "@playwright/test": "1.41.2",
     "@stoplight/spectral-cli": "6.11.0",
-    "@stylistic/eslint-plugin-js": "1.6.1",
+    "@stylistic/eslint-plugin-js": "1.6.2",
     "@stylistic/stylelint-plugin": "2.0.0",
     "@vitejs/plugin-vue": "5.0.4",
     "eslint": "8.56.0",
@@ -71,7 +71,7 @@
     "eslint-plugin-no-jquery": "2.7.0",
     "eslint-plugin-no-use-extend-native": "0.5.0",
     "eslint-plugin-regexp": "2.2.0",
-    "eslint-plugin-sonarjs": "0.23.0",
+    "eslint-plugin-sonarjs": "0.24.0",
     "eslint-plugin-unicorn": "51.0.1",
     "eslint-plugin-vitest": "0.3.22",
     "eslint-plugin-vitest-globals": "1.4.0",
diff --git a/poetry.lock b/poetry.lock
index 4897496a40..4cb58c6ef2 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -342,13 +342,13 @@ telegram = ["requests"]
 
 [[package]]
 name = "yamllint"
-version = "1.34.0"
+version = "1.35.0"
 description = "A linter for YAML files."
 optional = false
 python-versions = ">=3.8"
 files = [
-    {file = "yamllint-1.34.0-py3-none-any.whl", hash = "sha256:33b813f6ff2ffad2e57a288281098392b85f7463ce1f3d5cd45aa848b916a806"},
-    {file = "yamllint-1.34.0.tar.gz", hash = "sha256:7f0a6a41e8aab3904878da4ae34b6248b6bc74634e0d3a90f0fb2d7e723a3d4f"},
+    {file = "yamllint-1.35.0-py3-none-any.whl", hash = "sha256:601b0adaaac6d9bacb16a2e612e7ee8d23caf941ceebf9bfe2cff0f196266004"},
+    {file = "yamllint-1.35.0.tar.gz", hash = "sha256:9bc99c3e9fe89b4c6ee26e17aa817cf2d14390de6577cb6e2e6ed5f72120c835"},
 ]
 
 [package.dependencies]
@@ -361,4 +361,4 @@ dev = ["doc8", "flake8", "flake8-import-order", "rstcheck[sphinx]", "sphinx"]
 [metadata]
 lock-version = "2.0"
 python-versions = "^3.8"
-content-hash = "e4ea4301a70487379fce7008493d15c005af3aada7d88fbf0bd3167147ec6502"
+content-hash = "ba1c2c4235872f67354b5f52aa5bf0cd616354961530d9dc907f9fba28cc1ece"
diff --git a/pyproject.toml b/pyproject.toml
index 8d8d22088e..ef763da24d 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -9,7 +9,7 @@ python = "^3.8"
 
 [tool.poetry.group.dev.dependencies]
 djlint = "1.34.1"
-yamllint = "1.34.0"
+yamllint = "1.35.0"
 
 [tool.djlint]
 profile="golang"

From 3adfb6cb7277a719bd1cd6977f6ebb9d860b5972 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Fri, 16 Feb 2024 03:27:45 +0100
Subject: [PATCH 09/28] Avoid vue warning in dev mode (#29188)

`vue` currently outputs a warning for this undefined variable during
development, which is apparently caused by a bug in `vue-cli`.
Workaround by setting this variable.

Ref: https://github.com/vuejs/vue-cli/pull/7443
Ref: https://stackoverflow.com/a/77765007/808699
(cherry picked from commit e9a1ffba2c294f74d985870e9b7b5b07e9000857)
---
 webpack.config.js | 1 +
 1 file changed, 1 insertion(+)

diff --git a/webpack.config.js b/webpack.config.js
index 3779e860d9..066c26a686 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -176,6 +176,7 @@ export default {
     new DefinePlugin({
       __VUE_OPTIONS_API__: true, // at the moment, many Vue components still use the Vue Options API
       __VUE_PROD_DEVTOOLS__: false, // do not enable devtools support in production
+      __VUE_PROD_HYDRATION_MISMATCH_DETAILS__: false, // https://github.com/vuejs/vue-cli/pull/7443
     }),
     new VueLoaderPlugin(),
     new MiniCssExtractPlugin({

From 4f050f358a15dd51903e01b330a5419b2ac06693 Mon Sep 17 00:00:00 2001
From: Yarden Shoham <git@yardenshoham.com>
Date: Fri, 16 Feb 2024 04:52:25 +0200
Subject: [PATCH 10/28] Auto-update the system status in admin dashboard
 (#29163)

- Refactor the system status list into its own template
- Change the backend to return only the system status if htmx initiated
the request
- `hx-get="{{$.Link}}/system_status`: reuse the backend handler
- `hx-swap="innerHTML"`: replace the `<div>`'s innerHTML (essentially
the new template)
- `hx-trigger="every 5s"`: call every 5 seconds
- `hx-indicator=".divider"`: the `is-loading` class shouldn't be added
to the div during the request, so set it on an element it has no effect
on
- Render "Since Last GC Time" with `<relative-time>`, so we send a
timestamp

# Auto-update in action GIF

![action](https://github.com/go-gitea/gitea/assets/20454870/c6e1f220-f0fb-4460-ac3b-59f315e30e29)

---------

Signed-off-by: Yarden Shoham <git@yardenshoham.com>
Co-authored-by: silverwind <me@silverwind.io>
(cherry picked from commit c70f65e83bc1876fb368fd117d342573ff18a9e8)
---
 package-lock.json                  |  6 +++
 package.json                       |  1 +
 routers/web/admin/admin.go         | 26 +++++++-----
 routers/web/web.go                 |  1 +
 templates/admin/dashboard.tmpl     | 66 ++----------------------------
 templates/admin/system_status.tmpl | 62 ++++++++++++++++++++++++++++
 templates/base/head.tmpl           |  2 +-
 web_src/js/htmx.js                 |  3 ++
 webpack.config.js                  |  4 ++
 9 files changed, 97 insertions(+), 74 deletions(-)
 create mode 100644 templates/admin/system_status.tmpl

diff --git a/package-lock.json b/package-lock.json
index 48da8124e0..13f03b8d28 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -31,6 +31,7 @@
         "escape-goat": "4.0.0",
         "fast-glob": "3.3.2",
         "htmx.org": "1.9.10",
+        "idiomorph": "0.3.0",
         "jquery": "3.7.1",
         "katex": "0.16.9",
         "license-checker-webpack-plugin": "0.2.1",
@@ -6174,6 +6175,11 @@
         "postcss": "^8.1.0"
       }
     },
+    "node_modules/idiomorph": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/idiomorph/-/idiomorph-0.3.0.tgz",
+      "integrity": "sha512-UhV1Ey5xCxIwR9B+OgIjQa+1Jx99XQ1vQHUsKBU1RpQzCx1u+b+N6SOXgf5mEJDqemUI/ffccu6+71l2mJUsRA=="
+    },
     "node_modules/ieee754": {
       "version": "1.2.1",
       "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
diff --git a/package.json b/package.json
index ac79741711..3d753a567c 100644
--- a/package.json
+++ b/package.json
@@ -30,6 +30,7 @@
     "escape-goat": "4.0.0",
     "fast-glob": "3.3.2",
     "htmx.org": "1.9.10",
+    "idiomorph": "0.3.0",
     "jquery": "3.7.1",
     "katex": "0.16.9",
     "license-checker-webpack-plugin": "0.2.1",
diff --git a/routers/web/admin/admin.go b/routers/web/admin/admin.go
index d31cb1cd25..9fbd429f74 100644
--- a/routers/web/admin/admin.go
+++ b/routers/web/admin/admin.go
@@ -28,13 +28,14 @@ import (
 )
 
 const (
-	tplDashboard   base.TplName = "admin/dashboard"
-	tplSelfCheck   base.TplName = "admin/self_check"
-	tplCron        base.TplName = "admin/cron"
-	tplQueue       base.TplName = "admin/queue"
-	tplStacktrace  base.TplName = "admin/stacktrace"
-	tplQueueManage base.TplName = "admin/queue_manage"
-	tplStats       base.TplName = "admin/stats"
+	tplDashboard    base.TplName = "admin/dashboard"
+	tplSystemStatus base.TplName = "admin/system_status"
+	tplSelfCheck    base.TplName = "admin/self_check"
+	tplCron         base.TplName = "admin/cron"
+	tplQueue        base.TplName = "admin/queue"
+	tplStacktrace   base.TplName = "admin/stacktrace"
+	tplQueueManage  base.TplName = "admin/queue_manage"
+	tplStats        base.TplName = "admin/stats"
 )
 
 var sysStatus struct {
@@ -72,7 +73,7 @@ var sysStatus struct {
 
 	// Garbage collector statistics.
 	NextGC       string // next run in HeapAlloc time (bytes)
-	LastGC       string // last run in absolute time (ns)
+	LastGCTime   string // last run time
 	PauseTotalNs string
 	PauseNs      string // circular buffer of recent GC pause times, most recent at [(NumGC+255)%256]
 	NumGC        uint32
@@ -110,7 +111,7 @@ func updateSystemStatus() {
 	sysStatus.OtherSys = base.FileSize(int64(m.OtherSys))
 
 	sysStatus.NextGC = base.FileSize(int64(m.NextGC))
-	sysStatus.LastGC = fmt.Sprintf("%.1fs", float64(time.Now().UnixNano()-int64(m.LastGC))/1000/1000/1000)
+	sysStatus.LastGCTime = time.Unix(0, int64(m.LastGC)).Format(time.RFC3339)
 	sysStatus.PauseTotalNs = fmt.Sprintf("%.1fs", float64(m.PauseTotalNs)/1000/1000/1000)
 	sysStatus.PauseNs = fmt.Sprintf("%.3fs", float64(m.PauseNs[(m.NumGC+255)%256])/1000/1000/1000)
 	sysStatus.NumGC = m.NumGC
@@ -132,7 +133,6 @@ func Dashboard(ctx *context.Context) {
 	ctx.Data["PageIsAdminDashboard"] = true
 	ctx.Data["NeedUpdate"] = updatechecker.GetNeedUpdate(ctx)
 	ctx.Data["RemoteVersion"] = updatechecker.GetRemoteVersion(ctx)
-	// FIXME: update periodically
 	updateSystemStatus()
 	ctx.Data["SysStatus"] = sysStatus
 	ctx.Data["SSH"] = setting.SSH
@@ -140,6 +140,12 @@ func Dashboard(ctx *context.Context) {
 	ctx.HTML(http.StatusOK, tplDashboard)
 }
 
+func SystemStatus(ctx *context.Context) {
+	updateSystemStatus()
+	ctx.Data["SysStatus"] = sysStatus
+	ctx.HTML(http.StatusOK, tplSystemStatus)
+}
+
 // DashboardPost run an admin operation
 func DashboardPost(ctx *context.Context) {
 	form := web.GetForm(ctx).(*forms.AdminDashboardForm)
diff --git a/routers/web/web.go b/routers/web/web.go
index cdec6759fd..400bed9288 100644
--- a/routers/web/web.go
+++ b/routers/web/web.go
@@ -681,6 +681,7 @@ func registerRoutes(m *web.Route) {
 	// ***** START: Admin *****
 	m.Group("/admin", func() {
 		m.Get("", admin.Dashboard)
+		m.Get("/system_status", admin.SystemStatus)
 		m.Post("", web.Bind(forms.AdminDashboardForm{}), admin.DashboardPost)
 
 		if setting.Database.Type.IsMySQL() || setting.Database.Type.IsMSSQL() {
diff --git a/templates/admin/dashboard.tmpl b/templates/admin/dashboard.tmpl
index f43b4c5385..8088315f17 100644
--- a/templates/admin/dashboard.tmpl
+++ b/templates/admin/dashboard.tmpl
@@ -75,69 +75,9 @@
 		<h4 class="ui top attached header">
 			{{ctx.Locale.Tr "admin.dashboard.system_status"}}
 		</h4>
-		<div class="ui attached table segment">
-			<dl class="admin-dl-horizontal">
-				<dt>{{ctx.Locale.Tr "admin.dashboard.server_uptime"}}</dt>
-				<dd><relative-time format="duration" datetime="{{.SysStatus.StartTime}}">{{.SysStatus.StartTime}}</relative-time></dd>
-				<dt>{{ctx.Locale.Tr "admin.dashboard.current_goroutine"}}</dt>
-				<dd>{{.SysStatus.NumGoroutine}}</dd>
-				<div class="divider"></div>
-				<dt>{{ctx.Locale.Tr "admin.dashboard.current_memory_usage"}}</dt>
-				<dd>{{.SysStatus.MemAllocated}}</dd>
-				<dt>{{ctx.Locale.Tr "admin.dashboard.total_memory_allocated"}}</dt>
-				<dd>{{.SysStatus.MemTotal}}</dd>
-				<dt>{{ctx.Locale.Tr "admin.dashboard.memory_obtained"}}</dt>
-				<dd>{{.SysStatus.MemSys}}</dd>
-				<dt>{{ctx.Locale.Tr "admin.dashboard.pointer_lookup_times"}}</dt>
-				<dd>{{.SysStatus.Lookups}}</dd>
-				<dt>{{ctx.Locale.Tr "admin.dashboard.memory_allocate_times"}}</dt>
-				<dd>{{.SysStatus.MemMallocs}}</dd>
-				<dt>{{ctx.Locale.Tr "admin.dashboard.memory_free_times"}}</dt>
-				<dd>{{.SysStatus.MemFrees}}</dd>
-				<div class="divider"></div>
-				<dt>{{ctx.Locale.Tr "admin.dashboard.current_heap_usage"}}</dt>
-				<dd>{{.SysStatus.HeapAlloc}}</dd>
-				<dt>{{ctx.Locale.Tr "admin.dashboard.heap_memory_obtained"}}</dt>
-				<dd>{{.SysStatus.HeapSys}}</dd>
-				<dt>{{ctx.Locale.Tr "admin.dashboard.heap_memory_idle"}}</dt>
-				<dd>{{.SysStatus.HeapIdle}}</dd>
-				<dt>{{ctx.Locale.Tr "admin.dashboard.heap_memory_in_use"}}</dt>
-				<dd>{{.SysStatus.HeapInuse}}</dd>
-				<dt>{{ctx.Locale.Tr "admin.dashboard.heap_memory_released"}}</dt>
-				<dd>{{.SysStatus.HeapReleased}}</dd>
-				<dt>{{ctx.Locale.Tr "admin.dashboard.heap_objects"}}</dt>
-				<dd>{{.SysStatus.HeapObjects}}</dd>
-				<div class="divider"></div>
-				<dt>{{ctx.Locale.Tr "admin.dashboard.bootstrap_stack_usage"}}</dt>
-				<dd>{{.SysStatus.StackInuse}}</dd>
-				<dt>{{ctx.Locale.Tr "admin.dashboard.stack_memory_obtained"}}</dt>
-				<dd>{{.SysStatus.StackSys}}</dd>
-				<dt>{{ctx.Locale.Tr "admin.dashboard.mspan_structures_usage"}}</dt>
-				<dd>{{.SysStatus.MSpanInuse}}</dd>
-				<dt>{{ctx.Locale.Tr "admin.dashboard.mspan_structures_obtained"}}</dt>
-				<dd>{{.SysStatus.MSpanSys}}</dd>
-				<dt>{{ctx.Locale.Tr "admin.dashboard.mcache_structures_usage"}}</dt>
-				<dd>{{.SysStatus.MCacheInuse}}</dd>
-				<dt>{{ctx.Locale.Tr "admin.dashboard.mcache_structures_obtained"}}</dt>
-				<dd>{{.SysStatus.MCacheSys}}</dd>
-				<dt>{{ctx.Locale.Tr "admin.dashboard.profiling_bucket_hash_table_obtained"}}</dt>
-				<dd>{{.SysStatus.BuckHashSys}}</dd>
-				<dt>{{ctx.Locale.Tr "admin.dashboard.gc_metadata_obtained"}}</dt>
-				<dd>{{.SysStatus.GCSys}}</dd>
-				<dt>{{ctx.Locale.Tr "admin.dashboard.other_system_allocation_obtained"}}</dt>
-				<dd>{{.SysStatus.OtherSys}}</dd>
-				<div class="divider"></div>
-				<dt>{{ctx.Locale.Tr "admin.dashboard.next_gc_recycle"}}</dt>
-				<dd>{{.SysStatus.NextGC}}</dd>
-				<dt>{{ctx.Locale.Tr "admin.dashboard.last_gc_time"}}</dt>
-				<dd>{{.SysStatus.LastGC}}</dd>
-				<dt>{{ctx.Locale.Tr "admin.dashboard.total_gc_pause"}}</dt>
-				<dd>{{.SysStatus.PauseTotalNs}}</dd>
-				<dt>{{ctx.Locale.Tr "admin.dashboard.last_gc_pause"}}</dt>
-				<dd>{{.SysStatus.PauseNs}}</dd>
-				<dt>{{ctx.Locale.Tr "admin.dashboard.gc_times"}}</dt>
-				<dd>{{.SysStatus.NumGC}}</dd>
-			</dl>
+		{{/* TODO: make these stats work in multi-server deployments, likely needs per-server stats in DB */}}
+		<div hx-get="{{$.Link}}/system_status" hx-swap="morph:innerHTML" hx-trigger="every 5s" hx-indicator=".divider" class="ui attached table segment">
+			{{template "admin/system_status" .}}
 		</div>
 	</div>
 {{template "admin/layout_footer" .}}
diff --git a/templates/admin/system_status.tmpl b/templates/admin/system_status.tmpl
new file mode 100644
index 0000000000..7b5c9be6cc
--- /dev/null
+++ b/templates/admin/system_status.tmpl
@@ -0,0 +1,62 @@
+<dl class="admin-dl-horizontal">
+	<dt>{{ctx.Locale.Tr "admin.dashboard.server_uptime"}}</dt>
+	<dd><relative-time format="duration" datetime="{{.SysStatus.StartTime}}">{{.SysStatus.StartTime}}</relative-time></dd>
+	<dt>{{ctx.Locale.Tr "admin.dashboard.current_goroutine"}}</dt>
+	<dd>{{.SysStatus.NumGoroutine}}</dd>
+	<div class="divider"></div>
+	<dt>{{ctx.Locale.Tr "admin.dashboard.current_memory_usage"}}</dt>
+	<dd>{{.SysStatus.MemAllocated}}</dd>
+	<dt>{{ctx.Locale.Tr "admin.dashboard.total_memory_allocated"}}</dt>
+	<dd>{{.SysStatus.MemTotal}}</dd>
+	<dt>{{ctx.Locale.Tr "admin.dashboard.memory_obtained"}}</dt>
+	<dd>{{.SysStatus.MemSys}}</dd>
+	<dt>{{ctx.Locale.Tr "admin.dashboard.pointer_lookup_times"}}</dt>
+	<dd>{{.SysStatus.Lookups}}</dd>
+	<dt>{{ctx.Locale.Tr "admin.dashboard.memory_allocate_times"}}</dt>
+	<dd>{{.SysStatus.MemMallocs}}</dd>
+	<dt>{{ctx.Locale.Tr "admin.dashboard.memory_free_times"}}</dt>
+	<dd>{{.SysStatus.MemFrees}}</dd>
+	<div class="divider"></div>
+	<dt>{{ctx.Locale.Tr "admin.dashboard.current_heap_usage"}}</dt>
+	<dd>{{.SysStatus.HeapAlloc}}</dd>
+	<dt>{{ctx.Locale.Tr "admin.dashboard.heap_memory_obtained"}}</dt>
+	<dd>{{.SysStatus.HeapSys}}</dd>
+	<dt>{{ctx.Locale.Tr "admin.dashboard.heap_memory_idle"}}</dt>
+	<dd>{{.SysStatus.HeapIdle}}</dd>
+	<dt>{{ctx.Locale.Tr "admin.dashboard.heap_memory_in_use"}}</dt>
+	<dd>{{.SysStatus.HeapInuse}}</dd>
+	<dt>{{ctx.Locale.Tr "admin.dashboard.heap_memory_released"}}</dt>
+	<dd>{{.SysStatus.HeapReleased}}</dd>
+	<dt>{{ctx.Locale.Tr "admin.dashboard.heap_objects"}}</dt>
+	<dd>{{.SysStatus.HeapObjects}}</dd>
+	<div class="divider"></div>
+	<dt>{{ctx.Locale.Tr "admin.dashboard.bootstrap_stack_usage"}}</dt>
+	<dd>{{.SysStatus.StackInuse}}</dd>
+	<dt>{{ctx.Locale.Tr "admin.dashboard.stack_memory_obtained"}}</dt>
+	<dd>{{.SysStatus.StackSys}}</dd>
+	<dt>{{ctx.Locale.Tr "admin.dashboard.mspan_structures_usage"}}</dt>
+	<dd>{{.SysStatus.MSpanInuse}}</dd>
+	<dt>{{ctx.Locale.Tr "admin.dashboard.mspan_structures_obtained"}}</dt>
+	<dd>{{.SysStatus.MSpanSys}}</dd>
+	<dt>{{ctx.Locale.Tr "admin.dashboard.mcache_structures_usage"}}</dt>
+	<dd>{{.SysStatus.MCacheInuse}}</dd>
+	<dt>{{ctx.Locale.Tr "admin.dashboard.mcache_structures_obtained"}}</dt>
+	<dd>{{.SysStatus.MCacheSys}}</dd>
+	<dt>{{ctx.Locale.Tr "admin.dashboard.profiling_bucket_hash_table_obtained"}}</dt>
+	<dd>{{.SysStatus.BuckHashSys}}</dd>
+	<dt>{{ctx.Locale.Tr "admin.dashboard.gc_metadata_obtained"}}</dt>
+	<dd>{{.SysStatus.GCSys}}</dd>
+	<dt>{{ctx.Locale.Tr "admin.dashboard.other_system_allocation_obtained"}}</dt>
+	<dd>{{.SysStatus.OtherSys}}</dd>
+	<div class="divider"></div>
+	<dt>{{ctx.Locale.Tr "admin.dashboard.next_gc_recycle"}}</dt>
+	<dd>{{.SysStatus.NextGC}}</dd>
+	<dt>{{ctx.Locale.Tr "admin.dashboard.last_gc_time"}}</dt>
+	<dd><relative-time format="duration" datetime="{{.SysStatus.LastGCTime}}">{{.SysStatus.LastGCTime}}</relative-time></dd>
+	<dt>{{ctx.Locale.Tr "admin.dashboard.total_gc_pause"}}</dt>
+	<dd>{{.SysStatus.PauseTotalNs}}</dd>
+	<dt>{{ctx.Locale.Tr "admin.dashboard.last_gc_pause"}}</dt>
+	<dd>{{.SysStatus.PauseNs}}</dd>
+	<dt>{{ctx.Locale.Tr "admin.dashboard.gc_times"}}</dt>
+	<dd>{{.SysStatus.NumGC}}</dd>
+</dl>
diff --git a/templates/base/head.tmpl b/templates/base/head.tmpl
index 21addc7176..d865d58b8e 100644
--- a/templates/base/head.tmpl
+++ b/templates/base/head.tmpl
@@ -30,7 +30,7 @@
 	{{template "base/head_style" .}}
 	{{template "custom/header" .}}
 </head>
-<body hx-headers='{"x-csrf-token": "{{.CsrfToken}}"}' hx-swap="outerHTML" hx-push-url="false">
+<body hx-headers='{"x-csrf-token": "{{.CsrfToken}}"}' hx-swap="outerHTML" hx-ext="morph" hx-push-url="false">
 	{{ctx.DataRaceCheck $.Context}}
 	{{template "custom/body_outer_pre" .}}
 
diff --git a/web_src/js/htmx.js b/web_src/js/htmx.js
index 92400d1cbe..5ca3018308 100644
--- a/web_src/js/htmx.js
+++ b/web_src/js/htmx.js
@@ -1,6 +1,9 @@
 import * as htmx from 'htmx.org';
 import {showErrorToast} from './modules/toast.js';
 
+// https://github.com/bigskysoftware/idiomorph#htmx
+import 'idiomorph/dist/idiomorph-ext.js';
+
 // https://htmx.org/reference/#config
 htmx.config.requestClass = 'is-loading';
 htmx.config.scrollIntoViewOnBoost = false;
diff --git a/webpack.config.js b/webpack.config.js
index 066c26a686..d4700ebe2b 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -173,6 +173,9 @@ export default {
     ],
   },
   plugins: [
+    new webpack.ProvidePlugin({ // for htmx extensions
+      htmx: 'htmx.org',
+    }),
     new DefinePlugin({
       __VUE_OPTIONS_API__: true, // at the moment, many Vue components still use the Vue Options API
       __VUE_PROD_DEVTOOLS__: false, // do not enable devtools support in production
@@ -211,6 +214,7 @@ export default {
       override: {
         'khroma@*': {licenseName: 'MIT'}, // https://github.com/fabiospampinato/khroma/pull/33
         'htmx.org@1.9.10': {licenseName: 'BSD-2-Clause'}, // "BSD 2-Clause" -> "BSD-2-Clause"
+        'idiomorph@0.3.0': {licenseName: 'BSD-2-Clause'}, // "BSD 2-Clause" -> "BSD-2-Clause"
       },
       emitError: true,
       allow: '(Apache-2.0 OR BSD-2-Clause OR BSD-3-Clause OR MIT OR ISC OR CPAL-1.0 OR Unlicense OR EPL-1.0 OR EPL-2.0)',

From 800fa60ee577eaa2e26f91cbea2534c20c2bcd09 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Fri, 16 Feb 2024 04:17:34 +0100
Subject: [PATCH 11/28] Disable parallel Make execution (#29186)

Ref:
https://www.gnu.org/software/make/manual/html_node/Parallel-Disable.html

> If the .NOTPARALLEL special target with no prerequisites is specified
anywhere then the entire instance of make will be run serially,
regardless of the parallel setting

(cherry picked from commit 69ed1a4afbc9604cabe83041de31752dd5d101ee)

Conflicts:
	README.md
---
 Makefile | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/Makefile b/Makefile
index 6d75494390..ffffb1faf0 100644
--- a/Makefile
+++ b/Makefile
@@ -1023,3 +1023,8 @@ docker:
 
 # This endif closes the if at the top of the file
 endif
+
+# Disable parallel execution because it would break some targets that don't
+# specify exact dependencies like 'backend' which does currently not depend
+# on 'frontend' to enable Node.js-less builds from source tarballs.
+.NOTPARALLEL:

From 78f6b29248cc5b2fc8426c869ede30faa2e9cbeb Mon Sep 17 00:00:00 2001
From: yp05327 <576951401@qq.com>
Date: Fri, 16 Feb 2024 18:50:20 +0900
Subject: [PATCH 12/28] Fix gitea-action user avatar broken on edited menu
 (#29190)

Fix #29178

(cherry picked from commit 8e2831611c06e84dd8fedf7a0b2cce9f98d4188f)
---
 models/issues/content_history.go | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/models/issues/content_history.go b/models/issues/content_history.go
index 8c333bc6dd..8b00adda99 100644
--- a/models/issues/content_history.go
+++ b/models/issues/content_history.go
@@ -161,7 +161,11 @@ func FetchIssueContentHistoryList(dbCtx context.Context, issueID, commentID int6
 	}
 
 	for _, item := range res {
-		item.UserAvatarLink = avatars.GenerateUserAvatarFastLink(item.UserName, 0)
+		if item.UserID > 0 {
+			item.UserAvatarLink = avatars.GenerateUserAvatarFastLink(item.UserName, 0)
+		} else {
+			item.UserAvatarLink = avatars.DefaultAvatarLink()
+		}
 	}
 	return res, nil
 }

From f35d468b4396176b3274abcaa69a4f7867275539 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Fri, 16 Feb 2024 14:27:00 +0100
Subject: [PATCH 13/28] Refactor request function (#29187)

- Remove and prevent use of `body` argument, it is not used anywhere
- Remove uppercasing of method, we can require it to be uppercase

(cherry picked from commit c40ee6fb7382bc2d1398dc685f98a0277d3bfb68)
---
 web_src/js/modules/fetch.js | 20 +++++++++-----------
 1 file changed, 9 insertions(+), 11 deletions(-)

diff --git a/web_src/js/modules/fetch.js b/web_src/js/modules/fetch.js
index b3529d27fc..2191a8d4db 100644
--- a/web_src/js/modules/fetch.js
+++ b/web_src/js/modules/fetch.js
@@ -8,19 +8,17 @@ const safeMethods = new Set(['GET', 'HEAD', 'OPTIONS', 'TRACE']);
 // fetch wrapper, use below method name functions and the `data` option to pass in data
 // which will automatically set an appropriate headers. For json content, only object
 // and array types are currently supported.
-export function request(url, {method = 'GET', headers = {}, data, body, ...other} = {}) {
-  let contentType;
-  if (!body) {
-    if (data instanceof FormData || data instanceof URLSearchParams) {
-      body = data;
-    } else if (isObject(data) || Array.isArray(data)) {
-      contentType = 'application/json';
-      body = JSON.stringify(data);
-    }
+export function request(url, {method = 'GET', data, headers = {}, ...other} = {}) {
+  let body, contentType;
+  if (data instanceof FormData || data instanceof URLSearchParams) {
+    body = data;
+  } else if (isObject(data) || Array.isArray(data)) {
+    contentType = 'application/json';
+    body = JSON.stringify(data);
   }
 
   const headersMerged = new Headers({
-    ...(!safeMethods.has(method.toUpperCase()) && {'x-csrf-token': csrfToken}),
+    ...(!safeMethods.has(method) && {'x-csrf-token': csrfToken}),
     ...(contentType && {'content-type': contentType}),
   });
 
@@ -31,8 +29,8 @@ export function request(url, {method = 'GET', headers = {}, data, body, ...other
   return fetch(url, {
     method,
     headers: headersMerged,
-    ...(body && {body}),
     ...other,
+    ...(body && {body}),
   });
 }
 

From 2c336646f10cdaa51de48979e872c5a82da7550e Mon Sep 17 00:00:00 2001
From: Yarden Shoham <git@yardenshoham.com>
Date: Fri, 16 Feb 2024 15:34:29 +0200
Subject: [PATCH 14/28] Remove jQuery from SSH key form parser (#29193)

- Switched to plain JavaScript
- Tested the SSH key title functionality and it works as before

# Demo using JavaScript without jQuery

![action](https://github.com/go-gitea/gitea/assets/20454870/4785c13d-8d30-448e-b74a-263935e2769f)

---------

Signed-off-by: Yarden Shoham <git@yardenshoham.com>
Co-authored-by: silverwind <me@silverwind.io>
(cherry picked from commit 236e12184404998c8edf7efa6de7fccf9d0ee814)
---
 web_src/js/features/sshkey-helper.js | 14 ++++++--------
 1 file changed, 6 insertions(+), 8 deletions(-)

diff --git a/web_src/js/features/sshkey-helper.js b/web_src/js/features/sshkey-helper.js
index 099b54d3a6..3960eefe8e 100644
--- a/web_src/js/features/sshkey-helper.js
+++ b/web_src/js/features/sshkey-helper.js
@@ -1,12 +1,10 @@
-import $ from 'jquery';
-
 export function initSshKeyFormParser() {
-// Parse SSH Key
-  $('#ssh-key-content').on('change paste keyup', function () {
-    const arrays = $(this).val().split(' ');
-    const $title = $('#ssh-key-title');
-    if ($title.val() === '' && arrays.length === 3 && arrays[2] !== '') {
-      $title.val(arrays[2]);
+  // Parse SSH Key
+  document.getElementById('ssh-key-content')?.addEventListener('input', function () {
+    const arrays = this.value.split(' ');
+    const title = document.getElementById('ssh-key-title');
+    if (!title.value && arrays.length === 3 && arrays[2] !== '') {
+      title.value = arrays[2];
     }
   });
 }

From 2e887067dfd49cda98ca8f427b1609084f732442 Mon Sep 17 00:00:00 2001
From: Yarden Shoham <git@yardenshoham.com>
Date: Fri, 16 Feb 2024 15:59:48 +0200
Subject: [PATCH 15/28] Reference labels by IDs instead of names in `keys`
 settings (#29194)

Here's the spec for the `for` attribute:
https://html.spec.whatwg.org/multipage/forms.html#attr-label-for

Signed-off-by: Yarden Shoham <git@yardenshoham.com>
(cherry picked from commit 7132a0ba75d6fe734d9f950f217a5ceb81375328)
---
 templates/repo/settings/deploy_keys.tmpl    | 4 ++--
 templates/user/settings/keys_gpg.tmpl       | 4 ++--
 templates/user/settings/keys_principal.tmpl | 2 +-
 templates/user/settings/keys_ssh.tmpl       | 4 ++--
 4 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/templates/repo/settings/deploy_keys.tmpl b/templates/repo/settings/deploy_keys.tmpl
index c5d2d2a04a..a283150c60 100644
--- a/templates/repo/settings/deploy_keys.tmpl
+++ b/templates/repo/settings/deploy_keys.tmpl
@@ -18,11 +18,11 @@
 						{{ctx.Locale.Tr "repo.settings.deploy_key_desc"}}
 					</div>
 					<div class="field {{if .Err_Title}}error{{end}}">
-						<label for="title">{{ctx.Locale.Tr "repo.settings.title"}}</label>
+						<label for="ssh-key-title">{{ctx.Locale.Tr "repo.settings.title"}}</label>
 						<input id="ssh-key-title" name="title" value="{{.title}}" autofocus required>
 					</div>
 					<div class="field {{if .Err_Content}}error{{end}}">
-						<label for="content">{{ctx.Locale.Tr "repo.settings.deploy_key_content"}}</label>
+						<label for="ssh-key-content">{{ctx.Locale.Tr "repo.settings.deploy_key_content"}}</label>
 						<textarea id="ssh-key-content" name="content" placeholder="{{ctx.Locale.Tr "settings.key_content_ssh_placeholder"}}" required>{{.content}}</textarea>
 					</div>
 					<div class="field">
diff --git a/templates/user/settings/keys_gpg.tmpl b/templates/user/settings/keys_gpg.tmpl
index d11d18e6f5..e8bba69f6d 100644
--- a/templates/user/settings/keys_gpg.tmpl
+++ b/templates/user/settings/keys_gpg.tmpl
@@ -10,7 +10,7 @@
 			{{.CsrfTokenHtml}}
 			<input type="hidden" name="title" value="none">
 			<div class="field {{if .Err_Content}}error{{end}}">
-				<label for="content">{{ctx.Locale.Tr "settings.key_content"}}</label>
+				<label for="gpg-key-content">{{ctx.Locale.Tr "settings.key_content"}}</label>
 				<textarea id="gpg-key-content" name="content" placeholder="{{ctx.Locale.Tr "settings.key_content_gpg_placeholder"}}" required>{{.content}}</textarea>
 			</div>
 			{{if .Err_Signature}}
@@ -26,7 +26,7 @@
 					</div>
 				</div>
 				<div class="field">
-					<label for="signature">{{ctx.Locale.Tr "settings.gpg_token_signature"}}</label>
+					<label for="gpg-key-signature">{{ctx.Locale.Tr "settings.gpg_token_signature"}}</label>
 					<textarea id="gpg-key-signature" name="signature" placeholder="{{ctx.Locale.Tr "settings.key_signature_gpg_placeholder"}}" required>{{.signature}}</textarea>
 				</div>
 			{{end}}
diff --git a/templates/user/settings/keys_principal.tmpl b/templates/user/settings/keys_principal.tmpl
index 513afc2b61..a7ab12dd78 100644
--- a/templates/user/settings/keys_principal.tmpl
+++ b/templates/user/settings/keys_principal.tmpl
@@ -44,7 +44,7 @@
 			<form class="ui form" action="{{.Link}}" method="post">
 				{{.CsrfTokenHtml}}
 				<div class="field {{if .Err_Content}}error{{end}}">
-					<label for="content">{{ctx.Locale.Tr "settings.principal_content"}}</label>
+					<label for="ssh-principal-content">{{ctx.Locale.Tr "settings.principal_content"}}</label>
 					<input id="ssh-principal-content" name="content" value="{{.content}}" autofocus required>
 				</div>
 				<input name="title" type="hidden" value="principal">
diff --git a/templates/user/settings/keys_ssh.tmpl b/templates/user/settings/keys_ssh.tmpl
index 01afb54c82..9a49cc4e8b 100644
--- a/templates/user/settings/keys_ssh.tmpl
+++ b/templates/user/settings/keys_ssh.tmpl
@@ -11,11 +11,11 @@
 		<form class="ui form" action="{{.Link}}" method="post">
 			{{.CsrfTokenHtml}}
 			<div class="field {{if .Err_Title}}error{{end}}">
-				<label for="title">{{ctx.Locale.Tr "settings.key_name"}}</label>
+				<label for="ssh-key-title">{{ctx.Locale.Tr "settings.key_name"}}</label>
 				<input id="ssh-key-title" name="title" value="{{.title}}" autofocus required maxlength="50">
 			</div>
 			<div class="field {{if .Err_Content}}error{{end}}">
-				<label for="content">{{ctx.Locale.Tr "settings.key_content"}}</label>
+				<label for="ssh-key-content">{{ctx.Locale.Tr "settings.key_content"}}</label>
 				<textarea id="ssh-key-content" name="content" class="js-quick-submit" placeholder="{{ctx.Locale.Tr "settings.key_content_ssh_placeholder"}}" required>{{.content}}</textarea>
 			</div>
 			<input name="type" type="hidden" value="ssh">

From 1bf7b701180cf3d0de9ff3085aa152575bdd0940 Mon Sep 17 00:00:00 2001
From: Yarden Shoham <git@yardenshoham.com>
Date: Fri, 16 Feb 2024 17:48:01 +0200
Subject: [PATCH 16/28] Remove jQuery from organization rename prompt toggle
 (#29195)

- Switched to plain JavaScript
- Tested the organization rename prompt toggling functionality and it
works as before

# Demo using JavaScript without jQuery

![action](https://github.com/go-gitea/gitea/assets/20454870/e6f641b0-aa46-4b85-9693-0d608cca855e)

---------

Signed-off-by: Yarden Shoham <git@yardenshoham.com>
Co-authored-by: silverwind <me@silverwind.io>
(cherry picked from commit 5902372e63db2d3f31150251dfffdb305fa9aaee)
---
 web_src/js/features/common-organization.js | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/web_src/js/features/common-organization.js b/web_src/js/features/common-organization.js
index 352e824b05..a950af3adf 100644
--- a/web_src/js/features/common-organization.js
+++ b/web_src/js/features/common-organization.js
@@ -1,14 +1,15 @@
-import $ from 'jquery';
 import {initCompLabelEdit} from './comp/LabelEdit.js';
 import {toggleElem} from '../utils/dom.js';
 
 export function initCommonOrganization() {
-  if ($('.organization').length === 0) {
+  if (!document.querySelectorAll('.organization').length) {
     return;
   }
 
-  $('.organization.settings.options #org_name').on('input', function () {
-    const nameChanged = $(this).val().toLowerCase() !== $(this).attr('data-org-name').toLowerCase();
+  const orgNameInput = document.querySelector('.organization.settings.options #org_name');
+  if (!orgNameInput) return;
+  orgNameInput.addEventListener('input', function () {
+    const nameChanged = this.value.toLowerCase() !== this.getAttribute('data-org-name').toLowerCase();
     toggleElem('#org-name-change-prompt', nameChanged);
   });
 

From 6a4b83fb9d1fb650e9ccf0ab0ad0c1e052aa5515 Mon Sep 17 00:00:00 2001
From: Yarden Shoham <git@yardenshoham.com>
Date: Fri, 16 Feb 2024 17:52:50 +0200
Subject: [PATCH 17/28] Remove jQuery from username change prompt and fix its
 detection (#29197)

- Switched to plain JavaScript
- Tested the user rename prompt toggling functionality and it works as
before
- Fixed bug that allowed pasting with the mouse to avoid the prompt

# Before

![before](https://github.com/go-gitea/gitea/assets/20454870/aa300ad7-612b-461e-bbb2-3f74b3b83ede)

# After

![after](https://github.com/go-gitea/gitea/assets/20454870/f2b5a51b-7b39-43c7-8a4a-62f1f77acae4)

---------

Signed-off-by: Yarden Shoham <git@yardenshoham.com>
Co-authored-by: silverwind <me@silverwind.io>
(cherry picked from commit 0768842ef56758b3290406656c5ebbd605358f6e)
---
 web_src/js/features/user-settings.js | 29 ++++++++++++++--------------
 1 file changed, 15 insertions(+), 14 deletions(-)

diff --git a/web_src/js/features/user-settings.js b/web_src/js/features/user-settings.js
index d49bf39275..0dd908f34a 100644
--- a/web_src/js/features/user-settings.js
+++ b/web_src/js/features/user-settings.js
@@ -1,18 +1,19 @@
-import $ from 'jquery';
 import {hideElem, showElem} from '../utils/dom.js';
 
 export function initUserSettings() {
-  if ($('.user.settings.profile').length > 0) {
-    $('#username').on('keyup', function () {
-      const $prompt = $('#name-change-prompt');
-      const $prompt_redirect = $('#name-change-redirect-prompt');
-      if ($(this).val().toString().toLowerCase() !== $(this).data('name').toString().toLowerCase()) {
-        showElem($prompt);
-        showElem($prompt_redirect);
-      } else {
-        hideElem($prompt);
-        hideElem($prompt_redirect);
-      }
-    });
-  }
+  if (document.querySelectorAll('.user.settings.profile').length === 0) return;
+
+  const usernameInput = document.getElementById('username');
+  if (!usernameInput) return;
+  usernameInput.addEventListener('input', function () {
+    const prompt = document.getElementById('name-change-prompt');
+    const promptRedirect = document.getElementById('name-change-redirect-prompt');
+    if (this.value.toLowerCase() !== this.getAttribute('data-name').toLowerCase()) {
+      showElem(prompt);
+      showElem(promptRedirect);
+    } else {
+      hideElem(prompt);
+      hideElem(promptRedirect);
+    }
+  });
 }

From 1d4ce531fdf447cbe3a3e146816ee0f129e9823d Mon Sep 17 00:00:00 2001
From: Yarden Shoham <git@yardenshoham.com>
Date: Fri, 16 Feb 2024 18:03:52 +0200
Subject: [PATCH 18/28] Fix `initCompLabelEdit` not being called (#29198)

Fix broken `if` from https://github.com/go-gitea/gitea/pull/29195

Signed-off-by: Yarden Shoham <git@yardenshoham.com>
(cherry picked from commit 2d8756a9607ee6029ad7a44985e9751988d5fdaa)
---
 web_src/js/features/common-organization.js | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/web_src/js/features/common-organization.js b/web_src/js/features/common-organization.js
index a950af3adf..442714a3d6 100644
--- a/web_src/js/features/common-organization.js
+++ b/web_src/js/features/common-organization.js
@@ -6,9 +6,7 @@ export function initCommonOrganization() {
     return;
   }
 
-  const orgNameInput = document.querySelector('.organization.settings.options #org_name');
-  if (!orgNameInput) return;
-  orgNameInput.addEventListener('input', function () {
+  document.querySelector('.organization.settings.options #org_name')?.addEventListener('input', function () {
     const nameChanged = this.value.toLowerCase() !== this.getAttribute('data-org-name').toLowerCase();
     toggleElem('#org-name-change-prompt', nameChanged);
   });

From 4b69d9e46d790f8ac9fbf96b5d265b2467cded90 Mon Sep 17 00:00:00 2001
From: Yarden Shoham <git@yardenshoham.com>
Date: Fri, 16 Feb 2024 22:03:50 +0200
Subject: [PATCH 19/28] Remove jQuery from the "quick submit" handler (#29200)

- Switched to plain JavaScript
- Tested the quick submit functionality and it works as before

# Demo using JavaScript without jQuery

![action](https://github.com/go-gitea/gitea/assets/20454870/abbd6c49-ad0f-4f95-b4ba-e969b85a46e8)

---------

Signed-off-by: Yarden Shoham <git@yardenshoham.com>
(cherry picked from commit d8d4b33b31d959e4b600cc90a7fa1779b69cadf5)
---
 web_src/js/features/comp/QuickSubmit.js | 9 +--------
 1 file changed, 1 insertion(+), 8 deletions(-)

diff --git a/web_src/js/features/comp/QuickSubmit.js b/web_src/js/features/comp/QuickSubmit.js
index 2587375a71..e6d7080bcf 100644
--- a/web_src/js/features/comp/QuickSubmit.js
+++ b/web_src/js/features/comp/QuickSubmit.js
@@ -1,5 +1,3 @@
-import $ from 'jquery';
-
 export function handleGlobalEnterQuickSubmit(target) {
   const form = target.closest('form');
   if (form) {
@@ -8,14 +6,9 @@ export function handleGlobalEnterQuickSubmit(target) {
       return;
     }
 
-    if (form.classList.contains('form-fetch-action')) {
-      form.dispatchEvent(new SubmitEvent('submit', {bubbles: true, cancelable: true}));
-      return;
-    }
-
     // here use the event to trigger the submit event (instead of calling `submit()` method directly)
     // otherwise the `areYouSure` handler won't be executed, then there will be an annoying "confirm to leave" dialog
-    $(form).trigger('submit');
+    form.dispatchEvent(new SubmitEvent('submit', {bubbles: true, cancelable: true}));
   } else {
     // if no form, then the editor is for an AJAX request, dispatch an event to the target, let the target's event handler to do the AJAX request.
     // the 'ce-' prefix means this is a CustomEvent

From 59ab49a7eb7c59899c0a7add1942cb3fd4345366 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Fri, 16 Feb 2024 22:41:23 +0100
Subject: [PATCH 20/28] Add `eslint-plugin-github` and fix issues (#29201)

This plugin has a few useful rules. The only thing I dislike about it is
that it pulls in a rather big number of dependencies for react-related
rules we don't use, but it can't really be avoided.

Rule docs:
https://github.com/github/eslint-plugin-github?tab=readme-ov-file#rules

(cherry picked from commit 26b17537e651fe93ef9b64f961633cb4c0b8c2c3)
---
 .eslintrc.yaml                         |  24 +
 build/generate-images.js               |   6 +-
 build/generate-svg.js                  |   6 +-
 package-lock.json                      | 954 +++++++++++++++++++++++++
 package.json                           |   1 +
 web_src/js/features/repo-code.js       |   4 +-
 web_src/js/features/repo-issue-list.js |  14 +-
 web_src/js/features/repo-issue.js      |  10 +-
 web_src/js/features/repo-legacy.js     |  45 +-
 9 files changed, 1026 insertions(+), 38 deletions(-)

diff --git a/.eslintrc.yaml b/.eslintrc.yaml
index ed0309dbea..ab9c218849 100644
--- a/.eslintrc.yaml
+++ b/.eslintrc.yaml
@@ -12,6 +12,7 @@ plugins:
   - "@eslint-community/eslint-plugin-eslint-comments"
   - "@stylistic/eslint-plugin-js"
   - eslint-plugin-array-func
+  - eslint-plugin-github
   - eslint-plugin-i
   - eslint-plugin-jquery
   - eslint-plugin-no-jquery
@@ -209,6 +210,29 @@ rules:
   func-names: [0]
   func-style: [0]
   getter-return: [2]
+  github/a11y-aria-label-is-well-formatted: [0]
+  github/a11y-no-title-attribute: [0]
+  github/a11y-no-visually-hidden-interactive-element: [0]
+  github/a11y-role-supports-aria-props: [0]
+  github/a11y-svg-has-accessible-name: [0]
+  github/array-foreach: [0]
+  github/async-currenttarget: [2]
+  github/async-preventdefault: [2]
+  github/authenticity-token: [0]
+  github/get-attribute: [0]
+  github/js-class-name: [0]
+  github/no-blur: [0]
+  github/no-d-none: [0]
+  github/no-dataset: [2]
+  github/no-dynamic-script-tag: [2]
+  github/no-implicit-buggy-globals: [2]
+  github/no-inner-html: [0]
+  github/no-innerText: [2]
+  github/no-then: [2]
+  github/no-useless-passive: [2]
+  github/prefer-observers: [2]
+  github/require-passive-events: [2]
+  github/unescaped-html-literal: [0]
   grouped-accessor-pairs: [2]
   guard-for-in: [0]
   id-blacklist: [0]
diff --git a/build/generate-images.js b/build/generate-images.js
index a3a0f8d8f3..09e3e068af 100755
--- a/build/generate-images.js
+++ b/build/generate-images.js
@@ -79,4 +79,8 @@ async function main() {
   ]);
 }
 
-main().then(exit).catch(exit);
+try {
+  exit(await main());
+} catch (err) {
+  exit(err);
+}
diff --git a/build/generate-svg.js b/build/generate-svg.js
index b845da9367..2c0a5e37ba 100755
--- a/build/generate-svg.js
+++ b/build/generate-svg.js
@@ -63,4 +63,8 @@ async function main() {
   ]);
 }
 
-main().then(exit).catch(exit);
+try {
+  exit(await main());
+} catch (err) {
+  exit(err);
+}
diff --git a/package-lock.json b/package-lock.json
index 13f03b8d28..f1f8cc4705 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -68,6 +68,7 @@
         "@vitejs/plugin-vue": "5.0.4",
         "eslint": "8.56.0",
         "eslint-plugin-array-func": "4.0.0",
+        "eslint-plugin-github": "4.10.1",
         "eslint-plugin-i": "2.29.1",
         "eslint-plugin-jquery": "1.5.1",
         "eslint-plugin-no-jquery": "2.7.0",
@@ -1022,6 +1023,12 @@
         "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
       }
     },
+    "node_modules/@github/browserslist-config": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/@github/browserslist-config/-/browserslist-config-1.0.0.tgz",
+      "integrity": "sha512-gIhjdJp/c2beaIWWIlsXdqXVRUz3r2BxBCpfz/F3JXHvSAQ1paMYjLH+maEATtENg+k5eLV7gA+9yPp762ieuw==",
+      "dev": true
+    },
     "node_modules/@github/combobox-nav": {
       "version": "2.3.1",
       "resolved": "https://registry.npmjs.org/@github/combobox-nav/-/combobox-nav-2.3.1.tgz",
@@ -1380,6 +1387,18 @@
         "node": ">=14"
       }
     },
+    "node_modules/@pkgr/core": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz",
+      "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==",
+      "dev": true,
+      "engines": {
+        "node": "^12.20.0 || ^14.18.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/unts"
+      }
+    },
     "node_modules/@playwright/test": {
       "version": "1.41.2",
       "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.41.2.tgz",
@@ -2208,6 +2227,12 @@
       "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
       "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="
     },
+    "node_modules/@types/json5": {
+      "version": "0.0.29",
+      "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
+      "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==",
+      "dev": true
+    },
     "node_modules/@types/marked": {
       "version": "4.3.2",
       "resolved": "https://registry.npmjs.org/@types/marked/-/marked-4.3.2.tgz",
@@ -2271,6 +2296,69 @@
       "integrity": "sha512-XOfUup9r3Y06nFAZh3WvO0rBU4OtlfPB/vgxpjg+NRdGU6CN6djdc6OEiH+PcqHCY6eFLo9Ista73uarf4gnBg==",
       "dev": true
     },
+    "node_modules/@typescript-eslint/eslint-plugin": {
+      "version": "6.21.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz",
+      "integrity": "sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==",
+      "dev": true,
+      "dependencies": {
+        "@eslint-community/regexpp": "^4.5.1",
+        "@typescript-eslint/scope-manager": "6.21.0",
+        "@typescript-eslint/type-utils": "6.21.0",
+        "@typescript-eslint/utils": "6.21.0",
+        "@typescript-eslint/visitor-keys": "6.21.0",
+        "debug": "^4.3.4",
+        "graphemer": "^1.4.0",
+        "ignore": "^5.2.4",
+        "natural-compare": "^1.4.0",
+        "semver": "^7.5.4",
+        "ts-api-utils": "^1.0.1"
+      },
+      "engines": {
+        "node": "^16.0.0 || >=18.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      },
+      "peerDependencies": {
+        "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha",
+        "eslint": "^7.0.0 || ^8.0.0"
+      },
+      "peerDependenciesMeta": {
+        "typescript": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@typescript-eslint/parser": {
+      "version": "6.21.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz",
+      "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==",
+      "dev": true,
+      "dependencies": {
+        "@typescript-eslint/scope-manager": "6.21.0",
+        "@typescript-eslint/types": "6.21.0",
+        "@typescript-eslint/typescript-estree": "6.21.0",
+        "@typescript-eslint/visitor-keys": "6.21.0",
+        "debug": "^4.3.4"
+      },
+      "engines": {
+        "node": "^16.0.0 || >=18.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      },
+      "peerDependencies": {
+        "eslint": "^7.0.0 || ^8.0.0"
+      },
+      "peerDependenciesMeta": {
+        "typescript": {
+          "optional": true
+        }
+      }
+    },
     "node_modules/@typescript-eslint/scope-manager": {
       "version": "6.21.0",
       "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz",
@@ -2288,6 +2376,33 @@
         "url": "https://opencollective.com/typescript-eslint"
       }
     },
+    "node_modules/@typescript-eslint/type-utils": {
+      "version": "6.21.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz",
+      "integrity": "sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==",
+      "dev": true,
+      "dependencies": {
+        "@typescript-eslint/typescript-estree": "6.21.0",
+        "@typescript-eslint/utils": "6.21.0",
+        "debug": "^4.3.4",
+        "ts-api-utils": "^1.0.1"
+      },
+      "engines": {
+        "node": "^16.0.0 || >=18.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      },
+      "peerDependencies": {
+        "eslint": "^7.0.0 || ^8.0.0"
+      },
+      "peerDependenciesMeta": {
+        "typescript": {
+          "optional": true
+        }
+      }
+    },
     "node_modules/@typescript-eslint/types": {
       "version": "6.21.0",
       "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz",
@@ -2976,6 +3091,15 @@
       "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
       "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
     },
+    "node_modules/aria-query": {
+      "version": "5.3.0",
+      "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz",
+      "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==",
+      "dev": true,
+      "dependencies": {
+        "dequal": "^2.0.3"
+      }
+    },
     "node_modules/array-buffer-byte-length": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz",
@@ -3000,6 +3124,25 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/array-includes": {
+      "version": "3.1.7",
+      "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.7.tgz",
+      "integrity": "sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==",
+      "dev": true,
+      "dependencies": {
+        "call-bind": "^1.0.2",
+        "define-properties": "^1.2.0",
+        "es-abstract": "^1.22.1",
+        "get-intrinsic": "^1.2.1",
+        "is-string": "^1.0.7"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
     "node_modules/array-union": {
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
@@ -3009,6 +3152,80 @@
         "node": ">=8"
       }
     },
+    "node_modules/array.prototype.filter": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/array.prototype.filter/-/array.prototype.filter-1.0.3.tgz",
+      "integrity": "sha512-VizNcj/RGJiUyQBgzwxzE5oHdeuXY5hSbbmKMlphj1cy1Vl7Pn2asCGbSrru6hSQjmCzqTBPVWAF/whmEOVHbw==",
+      "dev": true,
+      "dependencies": {
+        "call-bind": "^1.0.2",
+        "define-properties": "^1.2.0",
+        "es-abstract": "^1.22.1",
+        "es-array-method-boxes-properly": "^1.0.0",
+        "is-string": "^1.0.7"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/array.prototype.findlastindex": {
+      "version": "1.2.4",
+      "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.4.tgz",
+      "integrity": "sha512-hzvSHUshSpCflDR1QMUBLHGHP1VIEBegT4pix9H/Z92Xw3ySoy6c2qh7lJWTJnRJ8JCZ9bJNCgTyYaJGcJu6xQ==",
+      "dev": true,
+      "dependencies": {
+        "call-bind": "^1.0.5",
+        "define-properties": "^1.2.1",
+        "es-abstract": "^1.22.3",
+        "es-errors": "^1.3.0",
+        "es-shim-unscopables": "^1.0.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/array.prototype.flat": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz",
+      "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==",
+      "dev": true,
+      "dependencies": {
+        "call-bind": "^1.0.2",
+        "define-properties": "^1.2.0",
+        "es-abstract": "^1.22.1",
+        "es-shim-unscopables": "^1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/array.prototype.flatmap": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz",
+      "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==",
+      "dev": true,
+      "dependencies": {
+        "call-bind": "^1.0.2",
+        "define-properties": "^1.2.0",
+        "es-abstract": "^1.22.1",
+        "es-shim-unscopables": "^1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
     "node_modules/arraybuffer.prototype.slice": {
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz",
@@ -3070,6 +3287,12 @@
         "node": ">=4"
       }
     },
+    "node_modules/ast-types-flow": {
+      "version": "0.0.8",
+      "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz",
+      "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==",
+      "dev": true
+    },
     "node_modules/astral-regex": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz",
@@ -3088,6 +3311,15 @@
         "astring": "bin/astring"
       }
     },
+    "node_modules/asynciterator.prototype": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/asynciterator.prototype/-/asynciterator.prototype-1.0.0.tgz",
+      "integrity": "sha512-wwHYEIS0Q80f5mosx3L/dfG5t5rjEa9Ft51GTaNt862EnpyGHpgz2RkZvLPp1oF5TnAiTohkEKVEu8pQPJI7Vg==",
+      "dev": true,
+      "dependencies": {
+        "has-symbols": "^1.0.3"
+      }
+    },
     "node_modules/asynckit": {
       "version": "0.4.0",
       "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
@@ -3118,6 +3350,24 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/axe-core": {
+      "version": "4.7.0",
+      "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.7.0.tgz",
+      "integrity": "sha512-M0JtH+hlOL5pLQwHOLNYZaXuhqmvS8oExsqB1SBYgA4Dk7u/xx+YdGHXaK5pyUfed5mYXdlYiphWq3G8cRi5JQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/axobject-query": {
+      "version": "3.2.1",
+      "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.1.tgz",
+      "integrity": "sha512-jsyHu61e6N4Vbz/v18DHwWYKK0bSWLqn47eeDSKPB7m8tqMHF9YJ+mhIk2lVteyZrY8tnSj/jHOv4YiTCuCJgg==",
+      "dev": true,
+      "dependencies": {
+        "dequal": "^2.0.3"
+      }
+    },
     "node_modules/balanced-match": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
@@ -4243,6 +4493,12 @@
         "lodash-es": "^4.17.21"
       }
     },
+    "node_modules/damerau-levenshtein": {
+      "version": "1.0.8",
+      "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz",
+      "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==",
+      "dev": true
+    },
     "node_modules/data-uri-to-buffer": {
       "version": "2.0.2",
       "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-2.0.2.tgz",
@@ -4685,6 +4941,12 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/es-array-method-boxes-properly": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz",
+      "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==",
+      "dev": true
+    },
     "node_modules/es-define-property": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz",
@@ -4706,6 +4968,32 @@
         "node": ">= 0.4"
       }
     },
+    "node_modules/es-iterator-helpers": {
+      "version": "1.0.17",
+      "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.17.tgz",
+      "integrity": "sha512-lh7BsUqelv4KUbR5a/ZTaGGIMLCjPGPqJ6q+Oq24YP0RdyptX1uzm4vvaqzk7Zx3bpl/76YLTTDj9L7uYQ92oQ==",
+      "dev": true,
+      "dependencies": {
+        "asynciterator.prototype": "^1.0.0",
+        "call-bind": "^1.0.7",
+        "define-properties": "^1.2.1",
+        "es-abstract": "^1.22.4",
+        "es-errors": "^1.3.0",
+        "es-set-tostringtag": "^2.0.2",
+        "function-bind": "^1.1.2",
+        "get-intrinsic": "^1.2.4",
+        "globalthis": "^1.0.3",
+        "has-property-descriptors": "^1.0.2",
+        "has-proto": "^1.0.1",
+        "has-symbols": "^1.0.3",
+        "internal-slot": "^1.0.7",
+        "iterator.prototype": "^1.1.2",
+        "safe-array-concat": "^1.1.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
     "node_modules/es-module-lexer": {
       "version": "1.4.1",
       "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.4.1.tgz",
@@ -4725,6 +5013,15 @@
         "node": ">= 0.4"
       }
     },
+    "node_modules/es-shim-unscopables": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz",
+      "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==",
+      "dev": true,
+      "dependencies": {
+        "hasown": "^2.0.0"
+      }
+    },
     "node_modules/es-to-primitive": {
       "version": "1.2.1",
       "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
@@ -4897,6 +5194,18 @@
         "eslint": ">=6.0.0"
       }
     },
+    "node_modules/eslint-config-prettier": {
+      "version": "9.1.0",
+      "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz",
+      "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==",
+      "dev": true,
+      "bin": {
+        "eslint-config-prettier": "bin/cli.js"
+      },
+      "peerDependencies": {
+        "eslint": ">=7.0.0"
+      }
+    },
     "node_modules/eslint-import-resolver-node": {
       "version": "0.3.9",
       "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz",
@@ -4955,6 +5264,92 @@
         "eslint": ">=8.40.0"
       }
     },
+    "node_modules/eslint-plugin-escompat": {
+      "version": "3.4.0",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-escompat/-/eslint-plugin-escompat-3.4.0.tgz",
+      "integrity": "sha512-ufTPv8cwCxTNoLnTZBFTQ5SxU2w7E7wiMIS7PSxsgP1eAxFjtSaoZ80LRn64hI8iYziE6kJG6gX/ZCJVxh48Bg==",
+      "dev": true,
+      "dependencies": {
+        "browserslist": "^4.21.0"
+      },
+      "peerDependencies": {
+        "eslint": ">=5.14.1"
+      }
+    },
+    "node_modules/eslint-plugin-eslint-comments": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-eslint-comments/-/eslint-plugin-eslint-comments-3.2.0.tgz",
+      "integrity": "sha512-0jkOl0hfojIHHmEHgmNdqv4fmh7300NdpA9FFpF7zaoLvB/QeXOGNLIo86oAveJFrfB1p05kC8hpEMHM8DwWVQ==",
+      "dev": true,
+      "dependencies": {
+        "escape-string-regexp": "^1.0.5",
+        "ignore": "^5.0.5"
+      },
+      "engines": {
+        "node": ">=6.5.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/mysticatea"
+      },
+      "peerDependencies": {
+        "eslint": ">=4.19.1"
+      }
+    },
+    "node_modules/eslint-plugin-eslint-comments/node_modules/escape-string-regexp": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+      "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.8.0"
+      }
+    },
+    "node_modules/eslint-plugin-filenames": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-filenames/-/eslint-plugin-filenames-1.3.2.tgz",
+      "integrity": "sha512-tqxJTiEM5a0JmRCUYQmxw23vtTxrb2+a3Q2mMOPhFxvt7ZQQJmdiuMby9B/vUAuVMghyP7oET+nIf6EO6CBd/w==",
+      "dev": true,
+      "dependencies": {
+        "lodash.camelcase": "4.3.0",
+        "lodash.kebabcase": "4.1.1",
+        "lodash.snakecase": "4.1.1",
+        "lodash.upperfirst": "4.3.1"
+      },
+      "peerDependencies": {
+        "eslint": "*"
+      }
+    },
+    "node_modules/eslint-plugin-github": {
+      "version": "4.10.1",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-github/-/eslint-plugin-github-4.10.1.tgz",
+      "integrity": "sha512-1AqQBockOM+m0ZUpwfjWtX0lWdX5cRi/hwJnSNvXoOmz/Hh+ULH6QFz6ENWueTWjoWpgPv0af3bj+snps6o4og==",
+      "dev": true,
+      "dependencies": {
+        "@github/browserslist-config": "^1.0.0",
+        "@typescript-eslint/eslint-plugin": "^6.0.0",
+        "@typescript-eslint/parser": "^6.0.0",
+        "aria-query": "^5.3.0",
+        "eslint-config-prettier": ">=8.0.0",
+        "eslint-plugin-escompat": "^3.3.3",
+        "eslint-plugin-eslint-comments": "^3.2.0",
+        "eslint-plugin-filenames": "^1.3.2",
+        "eslint-plugin-i18n-text": "^1.0.1",
+        "eslint-plugin-import": "^2.25.2",
+        "eslint-plugin-jsx-a11y": "^6.7.1",
+        "eslint-plugin-no-only-tests": "^3.0.0",
+        "eslint-plugin-prettier": "^5.0.0",
+        "eslint-rule-documentation": ">=1.0.0",
+        "jsx-ast-utils": "^3.3.2",
+        "prettier": "^3.0.0",
+        "svg-element-attributes": "^1.3.1"
+      },
+      "bin": {
+        "eslint-ignore-errors": "bin/eslint-ignore-errors.js"
+      },
+      "peerDependencies": {
+        "eslint": "^8.0.1"
+      }
+    },
     "node_modules/eslint-plugin-i": {
       "version": "2.29.1",
       "resolved": "https://registry.npmjs.org/eslint-plugin-i/-/eslint-plugin-i-2.29.1.tgz",
@@ -5002,6 +5397,98 @@
         "node": "*"
       }
     },
+    "node_modules/eslint-plugin-i18n-text": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-i18n-text/-/eslint-plugin-i18n-text-1.0.1.tgz",
+      "integrity": "sha512-3G3UetST6rdqhqW9SfcfzNYMpQXS7wNkJvp6dsXnjzGiku6Iu5hl3B0kmk6lIcFPwYjhQIY+tXVRtK9TlGT7RA==",
+      "dev": true,
+      "peerDependencies": {
+        "eslint": ">=5.0.0"
+      }
+    },
+    "node_modules/eslint-plugin-import": {
+      "version": "2.29.1",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz",
+      "integrity": "sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==",
+      "dev": true,
+      "dependencies": {
+        "array-includes": "^3.1.7",
+        "array.prototype.findlastindex": "^1.2.3",
+        "array.prototype.flat": "^1.3.2",
+        "array.prototype.flatmap": "^1.3.2",
+        "debug": "^3.2.7",
+        "doctrine": "^2.1.0",
+        "eslint-import-resolver-node": "^0.3.9",
+        "eslint-module-utils": "^2.8.0",
+        "hasown": "^2.0.0",
+        "is-core-module": "^2.13.1",
+        "is-glob": "^4.0.3",
+        "minimatch": "^3.1.2",
+        "object.fromentries": "^2.0.7",
+        "object.groupby": "^1.0.1",
+        "object.values": "^1.1.7",
+        "semver": "^6.3.1",
+        "tsconfig-paths": "^3.15.0"
+      },
+      "engines": {
+        "node": ">=4"
+      },
+      "peerDependencies": {
+        "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8"
+      }
+    },
+    "node_modules/eslint-plugin-import/node_modules/brace-expansion": {
+      "version": "1.1.11",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+      "dev": true,
+      "dependencies": {
+        "balanced-match": "^1.0.0",
+        "concat-map": "0.0.1"
+      }
+    },
+    "node_modules/eslint-plugin-import/node_modules/debug": {
+      "version": "3.2.7",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+      "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+      "dev": true,
+      "dependencies": {
+        "ms": "^2.1.1"
+      }
+    },
+    "node_modules/eslint-plugin-import/node_modules/doctrine": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
+      "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
+      "dev": true,
+      "dependencies": {
+        "esutils": "^2.0.2"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/eslint-plugin-import/node_modules/minimatch": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+      "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+      "dev": true,
+      "dependencies": {
+        "brace-expansion": "^1.1.7"
+      },
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/eslint-plugin-import/node_modules/semver": {
+      "version": "6.3.1",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+      "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+      "dev": true,
+      "bin": {
+        "semver": "bin/semver.js"
+      }
+    },
     "node_modules/eslint-plugin-jquery": {
       "version": "1.5.1",
       "resolved": "https://registry.npmjs.org/eslint-plugin-jquery/-/eslint-plugin-jquery-1.5.1.tgz",
@@ -5011,6 +5498,64 @@
         "eslint": ">=5.4.0"
       }
     },
+    "node_modules/eslint-plugin-jsx-a11y": {
+      "version": "6.8.0",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.8.0.tgz",
+      "integrity": "sha512-Hdh937BS3KdwwbBaKd5+PLCOmYY6U4f2h9Z2ktwtNKvIdIEu137rjYbcb9ApSbVJfWxANNuiKTD/9tOKjK9qOA==",
+      "dev": true,
+      "dependencies": {
+        "@babel/runtime": "^7.23.2",
+        "aria-query": "^5.3.0",
+        "array-includes": "^3.1.7",
+        "array.prototype.flatmap": "^1.3.2",
+        "ast-types-flow": "^0.0.8",
+        "axe-core": "=4.7.0",
+        "axobject-query": "^3.2.1",
+        "damerau-levenshtein": "^1.0.8",
+        "emoji-regex": "^9.2.2",
+        "es-iterator-helpers": "^1.0.15",
+        "hasown": "^2.0.0",
+        "jsx-ast-utils": "^3.3.5",
+        "language-tags": "^1.0.9",
+        "minimatch": "^3.1.2",
+        "object.entries": "^1.1.7",
+        "object.fromentries": "^2.0.7"
+      },
+      "engines": {
+        "node": ">=4.0"
+      },
+      "peerDependencies": {
+        "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8"
+      }
+    },
+    "node_modules/eslint-plugin-jsx-a11y/node_modules/brace-expansion": {
+      "version": "1.1.11",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+      "dev": true,
+      "dependencies": {
+        "balanced-match": "^1.0.0",
+        "concat-map": "0.0.1"
+      }
+    },
+    "node_modules/eslint-plugin-jsx-a11y/node_modules/emoji-regex": {
+      "version": "9.2.2",
+      "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
+      "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
+      "dev": true
+    },
+    "node_modules/eslint-plugin-jsx-a11y/node_modules/minimatch": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+      "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+      "dev": true,
+      "dependencies": {
+        "brace-expansion": "^1.1.7"
+      },
+      "engines": {
+        "node": "*"
+      }
+    },
     "node_modules/eslint-plugin-no-jquery": {
       "version": "2.7.0",
       "resolved": "https://registry.npmjs.org/eslint-plugin-no-jquery/-/eslint-plugin-no-jquery-2.7.0.tgz",
@@ -5020,6 +5565,15 @@
         "eslint": ">=2.3.0"
       }
     },
+    "node_modules/eslint-plugin-no-only-tests": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-no-only-tests/-/eslint-plugin-no-only-tests-3.1.0.tgz",
+      "integrity": "sha512-Lf4YW/bL6Un1R6A76pRZyE1dl1vr31G/ev8UzIc/geCgFWyrKil8hVjYqWVKGB/UIGmb6Slzs9T0wNezdSVegw==",
+      "dev": true,
+      "engines": {
+        "node": ">=5.0.0"
+      }
+    },
     "node_modules/eslint-plugin-no-use-extend-native": {
       "version": "0.5.0",
       "resolved": "https://registry.npmjs.org/eslint-plugin-no-use-extend-native/-/eslint-plugin-no-use-extend-native-0.5.0.tgz",
@@ -5035,6 +5589,36 @@
         "node": ">=6.0.0"
       }
     },
+    "node_modules/eslint-plugin-prettier": {
+      "version": "5.1.3",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.1.3.tgz",
+      "integrity": "sha512-C9GCVAs4Eq7ZC/XFQHITLiHJxQngdtraXaM+LoUFoFp/lHNl2Zn8f3WQbe9HvTBBQ9YnKFB0/2Ajdqwo5D1EAw==",
+      "dev": true,
+      "dependencies": {
+        "prettier-linter-helpers": "^1.0.0",
+        "synckit": "^0.8.6"
+      },
+      "engines": {
+        "node": "^14.18.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint-plugin-prettier"
+      },
+      "peerDependencies": {
+        "@types/eslint": ">=8.0.0",
+        "eslint": ">=8.0.0",
+        "eslint-config-prettier": "*",
+        "prettier": ">=3.0.0"
+      },
+      "peerDependenciesMeta": {
+        "@types/eslint": {
+          "optional": true
+        },
+        "eslint-config-prettier": {
+          "optional": true
+        }
+      }
+    },
     "node_modules/eslint-plugin-regexp": {
       "version": "2.2.0",
       "resolved": "https://registry.npmjs.org/eslint-plugin-regexp/-/eslint-plugin-regexp-2.2.0.tgz",
@@ -5191,6 +5775,15 @@
         "eslint": ">=5"
       }
     },
+    "node_modules/eslint-rule-documentation": {
+      "version": "1.0.23",
+      "resolved": "https://registry.npmjs.org/eslint-rule-documentation/-/eslint-rule-documentation-1.0.23.tgz",
+      "integrity": "sha512-pWReu3fkohwyvztx/oQWWgld2iad25TfUdi6wvhhaDPIQjHU/pyvlKgXFw1kX31SQK2Nq9MH+vRDWB0ZLy8fYw==",
+      "dev": true,
+      "engines": {
+        "node": ">=4.0.0"
+      }
+    },
     "node_modules/eslint-scope": {
       "version": "7.2.2",
       "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz",
@@ -6345,6 +6938,21 @@
       "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
       "dev": true
     },
+    "node_modules/is-async-function": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.0.0.tgz",
+      "integrity": "sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==",
+      "dev": true,
+      "dependencies": {
+        "has-tostringtag": "^1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
     "node_modules/is-bigint": {
       "version": "1.0.4",
       "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz",
@@ -6434,6 +7042,18 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/is-finalizationregistry": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.0.2.tgz",
+      "integrity": "sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==",
+      "dev": true,
+      "dependencies": {
+        "call-bind": "^1.0.2"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
     "node_modules/is-fullwidth-code-point": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
@@ -6442,6 +7062,21 @@
         "node": ">=8"
       }
     },
+    "node_modules/is-generator-function": {
+      "version": "1.0.10",
+      "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz",
+      "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==",
+      "dev": true,
+      "dependencies": {
+        "has-tostringtag": "^1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
     "node_modules/is-get-set-prop": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/is-get-set-prop/-/is-get-set-prop-1.0.0.tgz",
@@ -6472,6 +7107,15 @@
         "js-types": "^1.0.0"
       }
     },
+    "node_modules/is-map": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz",
+      "integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==",
+      "dev": true,
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
     "node_modules/is-negative-zero": {
       "version": "2.0.2",
       "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz",
@@ -6576,6 +7220,15 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/is-set": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz",
+      "integrity": "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==",
+      "dev": true,
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
     "node_modules/is-shared-array-buffer": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz",
@@ -6654,6 +7307,15 @@
         "is-potential-custom-element-name": "^1.0.0"
       }
     },
+    "node_modules/is-weakmap": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz",
+      "integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==",
+      "dev": true,
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
     "node_modules/is-weakref": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz",
@@ -6666,6 +7328,19 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/is-weakset": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.2.tgz",
+      "integrity": "sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==",
+      "dev": true,
+      "dependencies": {
+        "call-bind": "^1.0.2",
+        "get-intrinsic": "^1.1.1"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
     "node_modules/isarray": {
       "version": "2.0.5",
       "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
@@ -6685,6 +7360,19 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/iterator.prototype": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.2.tgz",
+      "integrity": "sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==",
+      "dev": true,
+      "dependencies": {
+        "define-properties": "^1.2.1",
+        "get-intrinsic": "^1.2.1",
+        "has-symbols": "^1.0.3",
+        "reflect.getprototypeof": "^1.0.4",
+        "set-function-name": "^2.0.1"
+      }
+    },
     "node_modules/jackspeak": {
       "version": "2.3.6",
       "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz",
@@ -6915,6 +7603,21 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/jsx-ast-utils": {
+      "version": "3.3.5",
+      "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz",
+      "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==",
+      "dev": true,
+      "dependencies": {
+        "array-includes": "^3.1.6",
+        "array.prototype.flat": "^1.3.1",
+        "object.assign": "^4.1.4",
+        "object.values": "^1.1.6"
+      },
+      "engines": {
+        "node": ">=4.0"
+      }
+    },
     "node_modules/just-extend": {
       "version": "5.1.1",
       "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-5.1.1.tgz",
@@ -6971,6 +7674,24 @@
       "integrity": "sha512-Ne7wqW7/9Cz54PDt4I3tcV+hAyat8ypyOGzYRJQfdxnnjeWsTxt1cy8pjvvKeI5kfXuyvULyeeAvwvvtAX3ayQ==",
       "dev": true
     },
+    "node_modules/language-subtag-registry": {
+      "version": "0.3.22",
+      "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz",
+      "integrity": "sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w==",
+      "dev": true
+    },
+    "node_modules/language-tags": {
+      "version": "1.0.9",
+      "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz",
+      "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==",
+      "dev": true,
+      "dependencies": {
+        "language-subtag-registry": "^0.3.20"
+      },
+      "engines": {
+        "node": ">=0.10"
+      }
+    },
     "node_modules/layout-base": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-1.0.2.tgz",
@@ -7140,12 +7861,30 @@
       "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz",
       "integrity": "sha512-xYHt68QRoYGjeeM/XOE1uJtvXQAgvszfBhjV4yvsQH0u2i9I6cI6c6/eG4Hh3UAOVn0y/xAXwmTzEay49Q//HA=="
     },
+    "node_modules/lodash.camelcase": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
+      "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==",
+      "dev": true
+    },
+    "node_modules/lodash.kebabcase": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz",
+      "integrity": "sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==",
+      "dev": true
+    },
     "node_modules/lodash.merge": {
       "version": "4.6.2",
       "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
       "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
       "dev": true
     },
+    "node_modules/lodash.snakecase": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz",
+      "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==",
+      "dev": true
+    },
     "node_modules/lodash.sortedlastindex": {
       "version": "4.1.0",
       "resolved": "https://registry.npmjs.org/lodash.sortedlastindex/-/lodash.sortedlastindex-4.1.0.tgz",
@@ -7181,6 +7920,12 @@
       "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==",
       "dev": true
     },
+    "node_modules/lodash.upperfirst": {
+      "version": "4.3.1",
+      "resolved": "https://registry.npmjs.org/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz",
+      "integrity": "sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==",
+      "dev": true
+    },
     "node_modules/loupe": {
       "version": "2.3.7",
       "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz",
@@ -8260,6 +9005,67 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/object.entries": {
+      "version": "1.1.7",
+      "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.7.tgz",
+      "integrity": "sha512-jCBs/0plmPsOnrKAfFQXRG2NFjlhZgjjcBLSmTnEhU8U6vVTsVe8ANeQJCHTl3gSsI4J+0emOoCgoKlmQPMgmA==",
+      "dev": true,
+      "dependencies": {
+        "call-bind": "^1.0.2",
+        "define-properties": "^1.2.0",
+        "es-abstract": "^1.22.1"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/object.fromentries": {
+      "version": "2.0.7",
+      "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.7.tgz",
+      "integrity": "sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==",
+      "dev": true,
+      "dependencies": {
+        "call-bind": "^1.0.2",
+        "define-properties": "^1.2.0",
+        "es-abstract": "^1.22.1"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/object.groupby": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.2.tgz",
+      "integrity": "sha512-bzBq58S+x+uo0VjurFT0UktpKHOZmv4/xePiOA1nbB9pMqpGK7rUPNgf+1YC+7mE+0HzhTMqNUuCqvKhj6FnBw==",
+      "dev": true,
+      "dependencies": {
+        "array.prototype.filter": "^1.0.3",
+        "call-bind": "^1.0.5",
+        "define-properties": "^1.2.1",
+        "es-abstract": "^1.22.3",
+        "es-errors": "^1.0.0"
+      }
+    },
+    "node_modules/object.values": {
+      "version": "1.1.7",
+      "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.7.tgz",
+      "integrity": "sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==",
+      "dev": true,
+      "dependencies": {
+        "call-bind": "^1.0.2",
+        "define-properties": "^1.2.0",
+        "es-abstract": "^1.22.1"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
     "node_modules/once": {
       "version": "1.4.0",
       "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
@@ -8804,6 +9610,33 @@
         "node": ">= 0.8.0"
       }
     },
+    "node_modules/prettier": {
+      "version": "3.2.5",
+      "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz",
+      "integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==",
+      "dev": true,
+      "bin": {
+        "prettier": "bin/prettier.cjs"
+      },
+      "engines": {
+        "node": ">=14"
+      },
+      "funding": {
+        "url": "https://github.com/prettier/prettier?sponsor=1"
+      }
+    },
+    "node_modules/prettier-linter-helpers": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz",
+      "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==",
+      "dev": true,
+      "dependencies": {
+        "fast-diff": "^1.1.2"
+      },
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
     "node_modules/pretty-format": {
       "version": "29.7.0",
       "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz",
@@ -9046,6 +9879,27 @@
         "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
       }
     },
+    "node_modules/reflect.getprototypeof": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.5.tgz",
+      "integrity": "sha512-62wgfC8dJWrmxv44CA36pLDnP6KKl3Vhxb7PL+8+qrrFMMoJij4vgiMP8zV4O8+CBMXY1mHxI5fITGHXFHVmQQ==",
+      "dev": true,
+      "dependencies": {
+        "call-bind": "^1.0.5",
+        "define-properties": "^1.2.1",
+        "es-abstract": "^1.22.3",
+        "es-errors": "^1.0.0",
+        "get-intrinsic": "^1.2.3",
+        "globalthis": "^1.0.3",
+        "which-builtin-type": "^1.1.3"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
     "node_modules/regenerator-runtime": {
       "version": "0.14.1",
       "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
@@ -9834,6 +10688,15 @@
         "node": ">=8"
       }
     },
+    "node_modules/strip-bom": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
+      "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==",
+      "dev": true,
+      "engines": {
+        "node": ">=4"
+      }
+    },
     "node_modules/strip-final-newline": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz",
@@ -10176,6 +11039,16 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/svg-element-attributes": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/svg-element-attributes/-/svg-element-attributes-1.3.1.tgz",
+      "integrity": "sha512-Bh05dSOnJBf3miNMqpsormfNtfidA/GxQVakhtn0T4DECWKeXQRQUceYjJ+OxYiiLdGe4Jo9iFV8wICFapFeIA==",
+      "dev": true,
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/wooorm"
+      }
+    },
     "node_modules/svg-tags": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/svg-tags/-/svg-tags-1.0.0.tgz",
@@ -10239,6 +11112,22 @@
         "node": ">=14"
       }
     },
+    "node_modules/synckit": {
+      "version": "0.8.8",
+      "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.8.tgz",
+      "integrity": "sha512-HwOKAP7Wc5aRGYdKH+dw0PRRpbO841v2DENBtjnR5HFWoiNByAl7vrx3p0G/rCyYXQsrxqtX48TImFtPcIHSpQ==",
+      "dev": true,
+      "dependencies": {
+        "@pkgr/core": "^0.1.0",
+        "tslib": "^2.6.2"
+      },
+      "engines": {
+        "node": "^14.18.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/unts"
+      }
+    },
     "node_modules/table": {
       "version": "6.8.1",
       "resolved": "https://registry.npmjs.org/table/-/table-6.8.1.tgz",
@@ -10491,6 +11380,30 @@
         "node": ">=6.10"
       }
     },
+    "node_modules/tsconfig-paths": {
+      "version": "3.15.0",
+      "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz",
+      "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==",
+      "dev": true,
+      "dependencies": {
+        "@types/json5": "^0.0.29",
+        "json5": "^1.0.2",
+        "minimist": "^1.2.6",
+        "strip-bom": "^3.0.0"
+      }
+    },
+    "node_modules/tsconfig-paths/node_modules/json5": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz",
+      "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==",
+      "dev": true,
+      "dependencies": {
+        "minimist": "^1.2.0"
+      },
+      "bin": {
+        "json5": "lib/cli.js"
+      }
+    },
     "node_modules/tslib": {
       "version": "2.6.2",
       "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
@@ -11416,6 +12329,47 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/which-builtin-type": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.1.3.tgz",
+      "integrity": "sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw==",
+      "dev": true,
+      "dependencies": {
+        "function.prototype.name": "^1.1.5",
+        "has-tostringtag": "^1.0.0",
+        "is-async-function": "^2.0.0",
+        "is-date-object": "^1.0.5",
+        "is-finalizationregistry": "^1.0.2",
+        "is-generator-function": "^1.0.10",
+        "is-regex": "^1.1.4",
+        "is-weakref": "^1.0.2",
+        "isarray": "^2.0.5",
+        "which-boxed-primitive": "^1.0.2",
+        "which-collection": "^1.0.1",
+        "which-typed-array": "^1.1.9"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/which-collection": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz",
+      "integrity": "sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==",
+      "dev": true,
+      "dependencies": {
+        "is-map": "^2.0.1",
+        "is-set": "^2.0.1",
+        "is-weakmap": "^2.0.1",
+        "is-weakset": "^2.0.1"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
     "node_modules/which-typed-array": {
       "version": "1.1.14",
       "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.14.tgz",
diff --git a/package.json b/package.json
index 3d753a567c..fdea78ca29 100644
--- a/package.json
+++ b/package.json
@@ -67,6 +67,7 @@
     "@vitejs/plugin-vue": "5.0.4",
     "eslint": "8.56.0",
     "eslint-plugin-array-func": "4.0.0",
+    "eslint-plugin-github": "4.10.1",
     "eslint-plugin-i": "2.29.1",
     "eslint-plugin-jquery": "1.5.1",
     "eslint-plugin-no-jquery": "2.7.0",
diff --git a/web_src/js/features/repo-code.js b/web_src/js/features/repo-code.js
index 306f38829f..a142313211 100644
--- a/web_src/js/features/repo-code.js
+++ b/web_src/js/features/repo-code.js
@@ -194,7 +194,7 @@ export function initRepoCodeView() {
     const blob = await $.get(`${url}?${query}&anchor=${anchor}`);
     currentTarget.closest('tr').outerHTML = blob;
   });
-  $(document).on('click', '.copy-line-permalink', async (e) => {
-    await clippie(toAbsoluteUrl(e.currentTarget.getAttribute('data-url')));
+  $(document).on('click', '.copy-line-permalink', async ({currentTarget}) => {
+    await clippie(toAbsoluteUrl(currentTarget.getAttribute('data-url')));
   });
 }
diff --git a/web_src/js/features/repo-issue-list.js b/web_src/js/features/repo-issue-list.js
index ca20cfbe38..efc7671204 100644
--- a/web_src/js/features/repo-issue-list.js
+++ b/web_src/js/features/repo-issue-list.js
@@ -69,16 +69,12 @@ function initRepoIssueListCheckboxes() {
       }
     }
 
-    updateIssuesMeta(
-      url,
-      action,
-      issueIDs,
-      elementId,
-    ).then(() => {
+    try {
+      await updateIssuesMeta(url, action, issueIDs, elementId);
       window.location.reload();
-    }).catch((reason) => {
-      showErrorToast(reason.responseJSON.error);
-    });
+    } catch (err) {
+      showErrorToast(err.responseJSON?.error ?? err.message);
+    }
   });
 }
 
diff --git a/web_src/js/features/repo-issue.js b/web_src/js/features/repo-issue.js
index 6908e0c912..3437565c80 100644
--- a/web_src/js/features/repo-issue.js
+++ b/web_src/js/features/repo-issue.js
@@ -344,19 +344,15 @@ export async function updateIssuesMeta(url, action, issueIds, elementId) {
 export function initRepoIssueComments() {
   if ($('.repository.view.issue .timeline').length === 0) return;
 
-  $('.re-request-review').on('click', function (e) {
+  $('.re-request-review').on('click', async function (e) {
     e.preventDefault();
     const url = $(this).data('update-url');
     const issueId = $(this).data('issue-id');
     const id = $(this).data('id');
     const isChecked = $(this).hasClass('checked');
 
-    updateIssuesMeta(
-      url,
-      isChecked ? 'detach' : 'attach',
-      issueId,
-      id,
-    ).then(() => window.location.reload());
+    await updateIssuesMeta(url, isChecked ? 'detach' : 'attach', issueId, id);
+    window.location.reload();
   });
 
   $(document).on('click', (event) => {
diff --git a/web_src/js/features/repo-legacy.js b/web_src/js/features/repo-legacy.js
index 08fe21190a..ce1bff11a2 100644
--- a/web_src/js/features/repo-legacy.js
+++ b/web_src/js/features/repo-legacy.js
@@ -205,12 +205,15 @@ export function initRepoCommentForm() {
     $listMenu.find('.no-select.item').on('click', function (e) {
       e.preventDefault();
       if (hasUpdateAction) {
-        updateIssuesMeta(
-          $listMenu.data('update-url'),
-          'clear',
-          $listMenu.data('issue-id'),
-          '',
-        ).then(reloadConfirmDraftComment);
+        (async () => {
+          await updateIssuesMeta(
+            $listMenu.data('update-url'),
+            'clear',
+            $listMenu.data('issue-id'),
+            '',
+          );
+          reloadConfirmDraftComment();
+        })();
       }
 
       $(this).parent().find('.item').each(function () {
@@ -248,12 +251,15 @@ export function initRepoCommentForm() {
 
       $(this).addClass('selected active');
       if (hasUpdateAction) {
-        updateIssuesMeta(
-          $menu.data('update-url'),
-          '',
-          $menu.data('issue-id'),
-          $(this).data('id'),
-        ).then(reloadConfirmDraftComment);
+        (async () => {
+          await updateIssuesMeta(
+            $menu.data('update-url'),
+            '',
+            $menu.data('issue-id'),
+            $(this).data('id'),
+          );
+          reloadConfirmDraftComment();
+        })();
       }
 
       let icon = '';
@@ -281,12 +287,15 @@ export function initRepoCommentForm() {
       });
 
       if (hasUpdateAction) {
-        updateIssuesMeta(
-          $menu.data('update-url'),
-          '',
-          $menu.data('issue-id'),
-          $(this).data('id'),
-        ).then(reloadConfirmDraftComment);
+        (async () => {
+          await updateIssuesMeta(
+            $menu.data('update-url'),
+            '',
+            $menu.data('issue-id'),
+            $(this).data('id'),
+          );
+          reloadConfirmDraftComment();
+        })();
       }
 
       $list.find('.selected').html('');

From 1ad195aa40c2d9a93a9631c4112be25179baf15f Mon Sep 17 00:00:00 2001
From: GiteaBot <teabot@gitea.io>
Date: Sat, 17 Feb 2024 00:23:24 +0000
Subject: [PATCH 21/28] [skip ci] Updated translations via Crowdin

(cherry picked from commit e936d2b338859c527482d1569c92d1f8f97f4d51)
---
 options/locale/locale_el-GR.ini | 50 +++++++++++++++++++++++
 options/locale/locale_tr-TR.ini | 71 +++++++++++++++++++++++++++++++++
 2 files changed, 121 insertions(+)

diff --git a/options/locale/locale_el-GR.ini b/options/locale/locale_el-GR.ini
index 70df36a4a6..3065990e8f 100644
--- a/options/locale/locale_el-GR.ini
+++ b/options/locale/locale_el-GR.ini
@@ -382,6 +382,7 @@ email_not_associate=Η διεύθυνση ηλεκτρονικού ταχυδρ
 send_reset_mail=Αποστολή Email Ανάκτησης Λογαριασμού
 reset_password=Ανάκτηση Λογαριασμού
 invalid_code=Ο κωδικός επιβεβαίωσης δεν είναι έγκυρος ή έχει λήξει.
+invalid_code_forgot_password=Ο κωδικός επιβεβαίωσης δεν είναι έγκυρος ή έληξε. Πατήστε <a href="%s">εδώ</a> για να ξεκινήσετε νέα συνεδρία.
 invalid_password=Ο κωδικός πρόσβασης σας δεν ταιριάζει με τον κωδικό που χρησιμοποιήθηκε για τη δημιουργία του λογαριασμού.
 reset_password_helper=Ανάκτηση Λογαριασμού
 reset_password_wrong_user=Έχετε συνδεθεί ως %s, αλλά ο σύνδεσμος ανάκτησης λογαριασμού προορίζεται για το %s
@@ -865,10 +866,12 @@ revoke_oauth2_grant_description=Η ανάκληση πρόσβασης για α
 revoke_oauth2_grant_success=Η πρόσβαση ανακλήθηκε επιτυχώς.
 
 twofa_desc=Ο έλεγχος ταυτότητας δύο παραγόντων ενισχύει την ασφάλεια του λογαριασμού σας.
+twofa_recovery_tip=Αν χάσετε τη συσκευή σας, θα είστε σε θέση να χρησιμοποιήσετε ένα κλειδί ανάκτησης μιας χρήσης για να ανακτήσετε την πρόσβαση στο λογαριασμό σας.
 twofa_is_enrolled=Ο λογαριασμός σας είναι <strong>εγγεγραμμένος</strong> σε έλεγχο ταυτότητας δύο παραγόντων.
 twofa_not_enrolled=Ο λογαριασμός σας δεν είναι εγγεγραμμένος σε έλεγχο ταυτότητας δύο παραγόντων.
 twofa_disable=Απενεργοποίηση Ταυτοποίησης Δύο Παραμέτρων
 twofa_scratch_token_regenerate=Αναδημιουργία Διακριτικού Μίας Χρήσης
+twofa_scratch_token_regenerated=Το κλειδί ανάκτησης μιας χρήσης είναι τώρα %s. Αποθηκεύστε το σε ασφαλές μέρος, καθώς δε θα εμφανιστεί ξανά.
 twofa_enroll=Εγγραφή στην ταυτοποίηση δύο παραγόντων
 twofa_disable_note=Μπορείτε να απενεργοποιήσετε την ταυτοποίηση δύο παραγόντων αν χρειαστεί.
 twofa_disable_desc=Η απενεργοποίηση της ταυτοποίησης δύο παραγόντων θα καταστήσει τον λογαριασμό σας λιγότερο ασφαλή. Συνέχεια;
@@ -886,6 +889,8 @@ webauthn_register_key=Προσθήκη Κλειδιού Ασφαλείας
 webauthn_nickname=Ψευδώνυμο
 webauthn_delete_key=Αφαίρεση Κλειδιού Ασφαλείας
 webauthn_delete_key_desc=Αν αφαιρέσετε ένα κλειδί ασφαλείας δεν μπορείτε πλέον να συνδεθείτε με αυτό. Συνέχεια;
+webauthn_key_loss_warning=Αν χάσετε τα κλειδιά ασφαλείας σας, θα χάσετε την πρόσβαση στο λογαριασμό σας.
+webauthn_alternative_tip=Μπορεί να θέλετε να ρυθμίσετε μια πρόσθετη μέθοδο ταυτοποίησης.
 
 manage_account_links=Διαχείριση Συνδεδεμένων Λογαριασμών
 manage_account_links_desc=Αυτοί οι εξωτερικοί λογαριασμοί είναι συνδεδεμένοι στον Forgejo λογαριασμό σας.
@@ -895,6 +900,7 @@ remove_account_link=Αφαίρεση Συνδεδεμένου Λογαριασμ
 remove_account_link_desc=Η κατάργηση ενός συνδεδεμένου λογαριασμού θα ανακαλέσει την πρόσβασή του στο λογαριασμό σας στο Forgejo. Συνέχεια;
 remove_account_link_success=Ο συνδεδεμένος λογαριασμός έχει αφαιρεθεί.
 
+hooks.desc=Προσθήκη webhooks που θα ενεργοποιούνται για <strong>όλα τα αποθετήρια</strong> που σας ανήκουν.
 
 orgs_none=Δεν είστε μέλος σε κάποιο οργανισμό.
 repos_none=Δεν κατέχετε κάποιο αποθετήριο.
@@ -916,9 +922,12 @@ visibility=Ορατότητα χρήστη
 visibility.public=Δημόσια
 visibility.public_tooltip=Ορατό σε όλους
 visibility.limited=Περιορισμένη
+visibility.limited_tooltip=Ορατό μόνο στους ταυτοποιημένους χρήστες
 visibility.private=Ιδιωτική
+visibility.private_tooltip=Ορατό μόνο στα μέλη των οργανισμών που συμμετέχετε
 
 [repo]
+new_repo_helper=Ένα αποθετήριο περιέχει όλα τα αρχεία έργου, συμπεριλαμβανομένου του ιστορικού εκδόσεων. Ήδη φιλοξενείται αλλού; <a href="%s">Μετεγκατάσταση αποθετηρίου.</a>
 owner=Ιδιοκτήτης
 owner_helper=Ορισμένοι οργανισμοί ενδέχεται να μην εμφανίζονται στο αναπτυσσόμενο μενού λόγω του μέγιστου αριθμού αποθετηρίων.
 repo_name=Όνομα αποθετηρίου
@@ -941,6 +950,7 @@ fork_to_different_account=Fork σε διαφορετικό λογαριασμό
 fork_visibility_helper=Η ορατότητα ενός fork αποθετηρίου δεν μπορεί να αλλάξει.
 fork_branch=Κλάδος που θα κλωνοποιηθεί στο fork
 all_branches=Όλοι οι κλάδοι
+fork_no_valid_owners=Αυτό το αποθετήριο δεν μπορεί να γίνει fork επειδή δεν υπάρχουν έγκυροι ιδιοκτήτες.
 use_template=Χρήση αυτού του πρότυπου
 clone_in_vsc=Κλωνοποίηση στο VS Code
 download_zip=Λήψη ZIP
@@ -1005,13 +1015,20 @@ delete_preexisting=Διαγραφή αρχείων που προϋπήρχαν
 delete_preexisting_content=Διαγραφή αρχείων στο %s
 delete_preexisting_success=Διαγράφηκαν τα μη υιοθετημένα αρχεία στο %s
 blame_prior=Προβολή ευθύνης πριν από αυτή την αλλαγή
+blame.ignore_revs=Αγνόηση των αναθεωρήσεων στο <a href="%s">.git-blame-ignore-revs</a>. Πατήστε <a href="%s">εδώ</a> για να το παρακάμψετε και να δείτε την κανονική προβολή ευθυνών.
+blame.ignore_revs.failed=Αποτυχία αγνόησης των αναθεωρήσεων στο <a href="%s">.git-blame-ignore-revs</a>.
 author_search_tooltip=Εμφάνιση το πολύ 30 χρηστών
 
+tree_path_not_found_commit=Η διαδρομή %[1]s δεν υπάρχει στην υποβολή %[2]s
+tree_path_not_found_branch=Η διαδρομή %[1]s δεν υπάρχει στον κλάδο %[2]s
+tree_path_not_found_tag=Η διαδρομή %[1]s δεν υπάρχει στην ετικέτα %[2]s
 
 transfer.accept=Αποδοχή Μεταφοράς
 transfer.accept_desc=`Μεταφορά στο "%s"`
 transfer.reject=Απόρριψη Μεταφοράς
 transfer.reject_desc=`Ακύρωση μεταφοράς σε "%s"`
+transfer.no_permission_to_accept=Δεν έχετε άδεια να αποδεχτείτε αυτή τη μεταφορά.
+transfer.no_permission_to_reject=Δεν έχετε άδεια να απορρίψετε αυτή τη μεταφορά.
 
 desc.private=Ιδιωτικό
 desc.public=Δημόσιο
@@ -1030,6 +1047,8 @@ template.issue_labels=Σήματα Ζητήματος
 template.one_item=Πρέπει να επιλέξετε τουλάχιστον ένα αντικείμενο στο πρότυπο
 template.invalid=Πρέπει να επιλέξετε ένα πρότυπο αποθετήριο
 
+archive.title=Αυτό το αποθετήρειο αρχειοθετήθηκε. Μπορείτε να προβάλετε αρχεία και να τα κλωνοποιήσετε, αλλά δεν μπορείτε να ωθήσετε ή να ανοίξετε ζητήματα ή pull requests.
+archive.title_date=Αυτό το αποθετήριο έχει αρχειοθετηθεί στο %s. Μπορείτε να προβάλετε αρχεία και να κλωνοποιήσετε, αλλά δεν μπορείτε να ωθήσετε ή να ανοίξετε ζητήματα ή pull requests.
 archive.issue.nocomment=Αυτό το αποθετήριο αρχειοθετήθηκε. Δεν μπορείτε να σχολιάσετε σε ζητήματα.
 archive.pull.nocomment=Αυτό το repo αρχειοθετήθηκε. Δεν μπορείτε να σχολιάσετε στα pull requests.
 
@@ -1046,6 +1065,7 @@ migrate_options_lfs=Μεταφορά αρχείων LFS
 migrate_options_lfs_endpoint.label=Άκρο LFS
 migrate_options_lfs_endpoint.description=Η μεταφορά θα προσπαθήσει να χρησιμοποιήσει το Git remote για να <a target="_blank" rel="noopener noreferrer" href="%s">καθορίσει τον διακομιστή LFS</a>. Μπορείτε επίσης να καθορίσετε ένα δικό σας endpoint αν τα δεδομένα LFS του αποθετηρίου αποθηκεύονται κάπου αλλού.
 migrate_options_lfs_endpoint.description.local=Μια διαδρομή στο τοπικό διακομιστή επίσης υποστηρίζεται.
+migrate_options_lfs_endpoint.placeholder=Αν αφεθεί κενό, το άκρο θα προκύψει από το URL του κλώνου
 migrate_items=Στοιχεία Μεταφοράς
 migrate_items_wiki=Wiki
 migrate_items_milestones=Ορόσημα
@@ -1148,6 +1168,7 @@ file_view_rendered=Προβολή Απόδοσης
 file_view_raw=Προβολή Ακατέργαστου
 file_permalink=Permalink
 file_too_large=Το αρχείο είναι πολύ μεγάλο για να εμφανιστεί.
+invisible_runes_header=`Αυτό το αρχείο περιέχει αόρατους χαρακτήρες Unicode `
 invisible_runes_description=`Αυτό το αρχείο περιέχει αόρατους χαρακτήρες Unicode που δεν διακρίνονται από ανθρώπους, αλλά μπορεί να επεξεργάζονται διαφορετικά από έναν υπολογιστή. Αν νομίζετε ότι αυτό είναι σκόπιμο, μπορείτε να αγνοήσετε με ασφάλεια αυτή την προειδοποίηση. Χρησιμοποιήστε το κουμπί Escape για να τους αποκαλύψετε.`
 ambiguous_runes_header=`Αυτό το αρχείο περιέχει ασαφείς χαρακτήρες Unicode `
 ambiguous_runes_description=`Αυτό το αρχείο περιέχει χαρακτήρες Unicode που μπορεί να συγχέονται με άλλους χαρακτήρες. Αν νομίζετε ότι αυτό είναι σκόπιμο, μπορείτε να αγνοήσετε με ασφάλεια αυτή την προειδοποίηση. Χρησιμοποιήστε το κουμπί Escape για να τους αποκαλύψετε.`
@@ -1426,6 +1447,7 @@ issues.filter_sort.moststars=Περισσότερα αστέρια
 issues.filter_sort.feweststars=Λιγότερα αστέρια
 issues.filter_sort.mostforks=Περισσότερα forks
 issues.filter_sort.fewestforks=Λιγότερα forks
+issues.keyword_search_unavailable=Η αναζήτηση μέσω λέξεων κλειδιών δεν είναι διαθέσιμη. Παρακαλώ επικοινωνήστε με το διαχειριστή.
 issues.action_open=Άνοιγμα
 issues.action_close=Κλείσιμο
 issues.action_label=Σήμα
@@ -1716,8 +1738,12 @@ pulls.is_empty=Οι αλλαγές σε αυτόν τον κλάδο είναι
 pulls.required_status_check_failed=Ορισμένοι απαιτούμενοι έλεγχοι δεν ήταν επιτυχείς.
 pulls.required_status_check_missing=Λείπουν ορισμένοι απαιτούμενοι έλεγχοι.
 pulls.required_status_check_administrator=Ως διαχειριστής, μπορείτε ακόμα να συγχωνεύσετε αυτό το pull request.
+pulls.blocked_by_approvals=Το pull request δεν έχει ακόμα αρκετές εγκρίσεις. Δόθηκαν %d από %d εγκρίσεις.
 pulls.blocked_by_rejection=Αυτό το Pull Request έχει αλλαγές που ζητούνται από έναν επίσημο εξεταστή.
 pulls.blocked_by_official_review_requests=Αυτό το Pull Request έχει επίσημες αιτήσεις αξιολόγησης.
+pulls.blocked_by_outdated_branch=Αυτό το pull request έχει αποκλειστεί επειδή είναι παρωχημένο.
+pulls.blocked_by_changed_protected_files_1=Αυτό το pull request έχει αποκλειστεί επειδή αλλάζει ένα προστατευμένο αρχείο:
+pulls.blocked_by_changed_protected_files_n=Αυτό το pull request έχει αποκλειστεί επειδή αλλάζει προστατευμένα αρχεία:
 pulls.can_auto_merge_desc=Αυτό το Pull Request μπορεί να συγχωνευθεί αυτόματα.
 pulls.cannot_auto_merge_desc=Αυτό το pull request δεν μπορεί να συγχωνευθεί αυτόματα λόγω συγκρούσεων.
 pulls.cannot_auto_merge_helper=Χειροκίνητη Συγχώνευση για την επίλυση των συγκρούσεων.
@@ -1833,11 +1859,16 @@ milestones.filter_sort.least_issues=Λιγότερα ζητήματα
 
 signing.will_sign=Αυτή η υποβολή θα υπογραφεί με το κλειδί "%s".
 signing.wont_sign.error=Παρουσιάστηκε σφάλμα κατά τον έλεγχο για το αν η υποβολή μπορεί να υπογραφεί.
+signing.wont_sign.nokey=Δεν υπάρχει διαθέσιμο κλειδί για να υπογραφεί αυτή η υποβολή.
 signing.wont_sign.never=Οι υποβολές δεν υπογράφονται ποτέ.
 signing.wont_sign.always=Οι υποβολές υπογράφονται πάντα.
+signing.wont_sign.pubkey=Η υποβολή δε θα υπογραφεί επειδή δεν υπάρχει δημόσιο κλειδί που να συνδέεται με το λογαριασμό σας.
+signing.wont_sign.twofa=Πρέπει να έχετε ενεργοποιημένη την ταυτοποίηση δύο παραγόντων για να υπογράφεται υποβολές.
 signing.wont_sign.parentsigned=Η υποβολή δε θα υπογραφεί καθώς η γονική υποβολή δεν έχει υπογραφεί.
 signing.wont_sign.basesigned=Η συγχώνευση δε θα υπογραφεί καθώς η βασική υποβολή δεν έχει υπογραφή της βάσης.
 signing.wont_sign.headsigned=Η συγχώνευση δε θα υπογραφεί καθώς δεν έχει υπογραφή η υποβολή της κεφαλής.
+signing.wont_sign.commitssigned=Η συγχώνευση δε θα υπογραφεί καθώς όλες οι σχετικές υποβολές δεν έχουν υπογραφεί.
+signing.wont_sign.approved=Η συγχώνευση δε θα υπογραφεί καθώς το PR δεν έχει εγκριθεί.
 signing.wont_sign.not_signed_in=Δεν είστε συνδεδεμένοι.
 
 ext_wiki=Πρόσβαση στο Εξωτερικό Wiki
@@ -1968,7 +1999,9 @@ settings.mirror_settings.docs.disabled_push_mirror.info=Τα είδωλα ώθη
 settings.mirror_settings.docs.no_new_mirrors=Το αποθετήριο σας αντιγράφει τις αλλαγές προς ή από ένα άλλο αποθετήριο. Λάβετε υπόψη ότι δεν μπορείτε να δημιουργήσετε νέα είδωλα αυτή τη στιγμή.
 settings.mirror_settings.docs.can_still_use=Αν και δεν μπορείτε να τροποποιήσετε τα υπάρχοντα είδωλα ή να δημιουργήσετε νέα, μπορείτε να χρησιμοποιείται ακόμα το υπάρχων είδωλο.
 settings.mirror_settings.docs.pull_mirror_instructions=Για να ορίσετε έναν είδωλο έλξης, παρακαλούμε συμβουλευθείτε:
+settings.mirror_settings.docs.more_information_if_disabled=Μπορείτε να μάθετε περισσότερα για τα είδωλα ώθησης και έλξης εδώ:
 settings.mirror_settings.docs.doc_link_title=Πώς μπορώ να αντιγράψω αποθετήρια;
+settings.mirror_settings.docs.doc_link_pull_section=το κεφάλαιο "Pulling from a remote repository" της τεκμηρίωσης.
 settings.mirror_settings.docs.pulling_remote_title=Έλξη από ένα απομακρυσμένο αποθετήριο
 settings.mirror_settings.mirrored_repository=Είδωλο αποθετηρίου
 settings.mirror_settings.direction=Κατεύθυνση
@@ -1981,6 +2014,8 @@ settings.mirror_settings.push_mirror.add=Προσθήκη Είδωλου Push
 settings.mirror_settings.push_mirror.edit_sync_time=Επεξεργασία διαστήματος συγχρονισμού ειδώλου
 
 settings.sync_mirror=Συγχρονισμός Τώρα
+settings.pull_mirror_sync_in_progress=Έλκονται αλλαγές από το απομακρυσμένο %s αυτή τη στιγμή.
+settings.push_mirror_sync_in_progress=Ώθηση αλλαγών στο απομακρυσμένο %s αυτή τη στιγμή.
 settings.site=Ιστοσελίδα
 settings.update_settings=Ενημέρωση Ρυθμίσεων
 settings.update_mirror_settings=Ενημέρωση Ρυθμίσεων Ειδώλου
@@ -2047,6 +2082,7 @@ settings.transfer.rejected=Η μεταβίβαση του αποθετηρίου
 settings.transfer.success=Η μεταβίβαση του αποθετηρίου ήταν επιτυχής.
 settings.transfer_abort=Ακύρωση μεταβίβασης
 settings.transfer_abort_invalid=Δεν μπορείτε να ακυρώσετε μια ανύπαρκτη μεταβίβαση αποθετηρίου.
+settings.transfer_abort_success=Η μεταφορά αποθετηρίου στο %s ακυρώθηκε με επιτυχία.
 settings.transfer_desc=Μεταβιβάστε αυτό το αποθετήριο σε έναν χρήστη ή σε έναν οργανισμό για τον οποίο έχετε δικαιώματα διαχειριστή.
 settings.transfer_form_title=Εισάγετε το όνομα του αποθετηρίου ως επιβεβαίωση:
 settings.transfer_in_progress=Αυτή τη στιγμή υπάρχει μια εν εξελίξει μεταβίβαση. Παρακαλούμε ακυρώστε την αν θέλετε να μεταβιβάσετε αυτό το αποθετήριο σε άλλο χρήστη.
@@ -2337,6 +2373,7 @@ settings.unarchive.button=Απο-Αρχειοθέτηση αποθετηρίου
 settings.unarchive.header=Απο-Αρχειοθέτηση του αποθετηρίου
 settings.unarchive.text=Η απο-αρχειοθέτηση του αποθετηρίου θα αποκαταστήσει την ικανότητά του να λαμβάνει υποβολές και ωθήσεις, καθώς και νέα ζητήματα και pull-requests.
 settings.unarchive.success=Το αποθετήριο απο-αρχειοθετήθηκε με επιτυχία.
+settings.unarchive.error=Παρουσιάστηκε σφάλμα κατά την προσπάθεια απο-αρχειοθέτησης του αποθετηρίου. Δείτε τις καταγραφές για περισσότερες λεπτομέρειες.
 settings.update_avatar_success=Η εικόνα του αποθετηρίου έχει ενημερωθεί.
 settings.lfs=LFS
 settings.lfs_filelist=Αρχεία LFS σε αυτό το αποθετήριο
@@ -2460,6 +2497,7 @@ release.edit_release=Ενημέρωση Κυκλοφορίας
 release.delete_release=Διαγραφή Κυκλοφορίας
 release.delete_tag=Διαγραφή Ετικέτας
 release.deletion=Διαγραφή Κυκλοφορίας
+release.deletion_desc=Διαγράφοντας μια κυκλοφορία, αυτή αφαιρείται μόνο από το Gitea. Δε θα επηρεάσει την ετικέτα Git, τα περιεχόμενα του αποθετηρίου σας ή το ιστορικό της. Συνέχεια;
 release.deletion_success=Η κυκλοφορία έχει διαγραφεί.
 release.deletion_tag_desc=Θα διαγράψει αυτή την ετικέτα από το αποθετήριο. Τα περιεχόμενα του αποθετηρίου και το ιστορικό παραμένουν αμετάβλητα. Συνέχεια;
 release.deletion_tag_success=Η ετικέτα έχει διαγραφεί.
@@ -2479,6 +2517,7 @@ branch.already_exists=Ήδη υπάρχει ένας κλάδος με το όν
 branch.delete_head=Διαγραφή
 branch.delete=`Διαγραφή του Κλάδου "%s"`
 branch.delete_html=Διαγραφή Κλάδου
+branch.delete_desc=Η διαγραφή ενός κλάδου είναι μόνιμη. Αν και ο διαγραμμένος κλάδος μπορεί να συνεχίσει να υπάρχει για σύντομο χρονικό διάστημα πριν να αφαιρεθεί, ΔΕΝ ΜΠΟΡΕΙ να αναιρεθεί στις περισσότερες περιπτώσεις. Συνέχεια;
 branch.deletion_success=Ο κλάδος "%s" διαγράφηκε.
 branch.deletion_failed=Αποτυχία διαγραφής του κλάδου "%s".
 branch.delete_branch_has_new_commits=Ο κλάδος "%s" δεν μπορεί να διαγραφεί επειδή προστέθηκαν νέες υποβολές μετά τη συγχώνευση.
@@ -2713,6 +2752,7 @@ dashboard.reinit_missing_repos=Επανεκκινήστε όλα τα αποθε
 dashboard.sync_external_users=Συγχρονισμός δεδομένων εξωτερικών χρηστών
 dashboard.cleanup_hook_task_table=Εκκαθάριση πίνακα hook_task
 dashboard.cleanup_packages=Εκκαθάριση ληγμένων πακέτων
+dashboard.cleanup_actions=Οι ενέργειες καθαρισμού καταγραφές και αντικείμενα
 dashboard.server_uptime=Διάρκεια Διακομιστή
 dashboard.current_goroutine=Τρέχουσες Goroutines
 dashboard.current_memory_usage=Τρέχουσα Χρήση Μνήμης
@@ -2823,6 +2863,7 @@ emails.updated=Το email ενημερώθηκε
 emails.not_updated=Αποτυχία ενημέρωσης της ζητούμενης διεύθυνσης email: %v
 emails.duplicate_active=Αυτή η διεύθυνση email είναι ήδη ενεργή σε διαφορετικό χρήστη.
 emails.change_email_header=Ενημέρωση Ιδιοτήτων Email
+emails.change_email_text=Είστε βέβαιοι ότι θέλετε να ενημερώσετε αυτή τη διεύθυνση email;
 
 orgs.org_manage_panel=Διαχείριση Οργανισμού
 orgs.name=Όνομα
@@ -2847,6 +2888,7 @@ packages.package_manage_panel=Διαχείριση Πακέτων
 packages.total_size=Συνολικό Μέγεθος: %s
 packages.unreferenced_size=Μέγεθος Χωρίς Αναφορά: %s
 packages.cleanup=Εκκαθάριση ληγμένων δεδομένων
+packages.cleanup.success=Επιτυχής εκκαθάριση δεδομένων που έχουν λήξει
 packages.owner=Ιδιοκτήτης
 packages.creator=Δημιουργός
 packages.name=Όνομα
@@ -2857,10 +2899,12 @@ packages.size=Μέγεθος
 packages.published=Δημοσιευμένα
 
 defaulthooks=Προεπιλεγμένα Webhooks
+defaulthooks.desc=Τα Webhooks κάνουν αυτόματα αιτήσεις HTTP POST σε ένα διακομιστή όταν ενεργοποιούν ορισμένα γεγονότα στο Gitea. Τα Webhooks που ορίζονται εδώ είναι προκαθορισμένα και θα αντιγραφούν σε όλα τα νέα αποθετήρια. Διαβάστε περισσότερα στον οδηγό <a target="_blank" rel="noopener" href="https://docs.gitea.com/usage/webhooks">webhooks</a>.
 defaulthooks.add_webhook=Προσθήκη Προεπιλεγμένου Webhook
 defaulthooks.update_webhook=Ενημέρωση Προεπιλεγμένου Webhook
 
 systemhooks=Webhooks Συστήματος
+systemhooks.desc=Τα Webhooks κάνουν αυτόματα αιτήσεις HTTP POST σε ένα διακομιστή όταν ενεργοποιούνται ορισμένα γεγονότα στο Gitea. Τα Webhooks που ορίζονται εδώ θα ενεργούν σε όλα τα αποθετήρια του συστήματος, γι 'αυτό παρακαλώ εξετάστε τυχόν επιπτώσεις απόδοσης που μπορεί να έχει. Διαβάστε περισσότερα στον οδηγό <a target="_blank" rel="noopener" href="https://docs.gitea.com/usage/webhooks">webhooks</a>.
 systemhooks.add_webhook=Προσθήκη Webhook Συστήματος
 systemhooks.update_webhook=Ενημέρωση Webhook Συστήματος
 
@@ -2953,6 +2997,7 @@ auths.sspi_default_language=Προεπιλεγμένη γλώσσα χρήστη
 auths.sspi_default_language_helper=Προεπιλεγμένη γλώσσα για τους χρήστες που δημιουργούνται αυτόματα με τη μέθοδο ταυτοποίησης SSPI. Αφήστε κενό αν προτιμάτε η γλώσσα να εντοπιστεί αυτόματα.
 auths.tips=Συμβουλές
 auths.tips.oauth2.general=Ταυτοποίηση OAuth2
+auths.tips.oauth2.general.tip=Κατά την εγγραφή μιας νέας ταυτοποίησης OAuth2, το URL κλήσης/ανακατεύθυνσης πρέπει να είναι:
 auths.tip.oauth2_provider=Πάροχος OAuth2
 auths.tip.bitbucket=Καταχωρήστε ένα νέο καταναλωτή OAuth στο https://bitbucket.org/account/user/<your username>/oauth-consumers/new και προσθέστε το δικαίωμα 'Account' - 'Read'
 auths.tip.nextcloud=`Καταχωρήστε ένα νέο καταναλωτή OAuth στην υπηρεσία σας χρησιμοποιώντας το παρακάτω μενού "Settings -> Security -> OAuth 2.0 client"`
@@ -2964,6 +3009,7 @@ auths.tip.google_plus=Αποκτήστε τα διαπιστευτήρια πε
 auths.tip.openid_connect=Χρησιμοποιήστε το OpenID Connect Discovery URL (<server>/.well known/openid-configuration) για να καθορίσετε τα τελικά σημεία
 auths.tip.twitter=Πηγαίνετε στο https://dev.twitter.com/apps, δημιουργήστε μια εφαρμογή και βεβαιωθείτε ότι η επιλογή “Allow this application to be used to Sign in with Twitter” είναι ενεργοποιημένη
 auths.tip.discord=Καταχωρήστε μια νέα εφαρμογή στο https://discordapp.com/developers/applications/me
+auths.tip.gitea=Καταχωρήστε μια νέα εφαρμογή OAuth2. Μπορείτε να βρείτε τον οδηγό στο https://docs.gitea.com/development/oauth2-provider
 auths.tip.yandex=`Δημιουργήστε μια νέα εφαρμογή στο https://oauth.yandex.com/client/new. Επιλέξτε τα ακόλουθα δικαιώματα από την ενότητα "Yandex.Passport API": "Access to email address", "Access to user avatar" και "Access to username, first name and surname, gender"`
 auths.tip.mastodon=Εισαγάγετε ένα προσαρμομένο URL για την υπηρεσία mastodon με την οποία θέλετε να πιστοποιήσετε (ή να χρησιμοποιήσετε την προεπιλεγμένη)
 auths.edit=Επεξεργασία Πηγής Ταυτοποίησης
@@ -3267,6 +3313,7 @@ desc=Διαχείριση πακέτων μητρώου.
 empty=Δεν υπάρχουν πακέτα ακόμα.
 empty.documentation=Για περισσότερες πληροφορίες σχετικά με το μητρώο πακέτων, ανατρέξτε <a target="_blank" rel="noopener noreferrer" href="%s">στην τεκμηρίωση</a>.
 empty.repo=Μήπως ανεβάσατε ένα πακέτο, αλλά δεν εμφανίζεται εδώ; Πηγαίνετε στις <a href="%[1]s">ρυθμίσεις πακέτων</a> και συνδέστε το σε αυτό το αποθετήριο.
+registry.documentation=Για περισσότερες πληροφορίες σχετικά με το μητρώο %s, ανατρέξτε στη τεκμηρίωση <a target="_blank" rel="noopener noreferrer" href="%s"></a>.
 filter.type=Τύπος
 filter.type.all=Όλα
 filter.no_result=Το φίλτρο δεν παρήγαγε αποτελέσματα.
@@ -3378,9 +3425,11 @@ settings.delete.success=Το πακέτο έχει διαγραφεί.
 settings.delete.error=Αποτυχία διαγραφής του πακέτου.
 owner.settings.cargo.title=Ευρετήριο Μητρώου Cargo
 owner.settings.cargo.initialize=Αρχικοποίηση Ευρετηρίου
+owner.settings.cargo.initialize.description=Απαιτείται ένα ειδικό αποθετήριο ευρετηρίου Git για τη χρήση του μητρώου Cargo. Χρησιμοποιώντας αυτή την επιλογή θα δημιουργηθεί ξανά το αποθετήριο και θα ρυθμιστεί αυτόματα.
 owner.settings.cargo.initialize.error=Αποτυχία αρχικοποίησης ευρετηρίου Cargo: %v
 owner.settings.cargo.initialize.success=Ο ευρετήριο Cargo δημιουργήθηκε με επιτυχία.
 owner.settings.cargo.rebuild=Αναδημιουργία Ευρετηρίου
+owner.settings.cargo.rebuild.description=Η ανοικοδόμηση μπορεί να είναι χρήσιμη εάν ο δείκτης δεν είναι συγχρονισμένος με τα αποθηκευμένα πακέτα Cargo.
 owner.settings.cargo.rebuild.error=Αποτυχία αναδόμησης του ευρετηρίου Cargo: %v
 owner.settings.cargo.rebuild.success=Το ευρετήριο Cargo αναδομήθηκε με επιτυχία.
 owner.settings.cleanuprules.title=Διαχείριση Κανόνων Εκκαθάρισης
@@ -3405,6 +3454,7 @@ owner.settings.cleanuprules.success.update=Ο κανόνας καθαρισμο
 owner.settings.cleanuprules.success.delete=Ο κανόνας καθαρισμού διαγράφηκε.
 owner.settings.chef.title=Μητρώο Chef
 owner.settings.chef.keypair=Δημιουργία ζεύγους κλειδιών
+owner.settings.chef.keypair.description=Ένα ζεύγος κλειδιών είναι απαραίτητο για ταυτοποίηση στο μητρώο Chef. Αν έχετε δημιουργήσει ένα ζεύγος κλειδιών πριν, η δημιουργία ενός νέου ζεύγους κλειδιών θα απορρίψει το παλιό ζεύγος κλειδιών.
 
 [secrets]
 secrets=Μυστικά
diff --git a/options/locale/locale_tr-TR.ini b/options/locale/locale_tr-TR.ini
index 59a71e9e42..094a970f42 100644
--- a/options/locale/locale_tr-TR.ini
+++ b/options/locale/locale_tr-TR.ini
@@ -18,6 +18,7 @@ template=Şablon
 language=Dil
 notifications=Bildirimler
 active_stopwatch=Etkin Zaman Takibi
+tracked_time_summary=Konu listesi süzgeçlerine dayanan takip edilen zamanın özeti
 create_new=Oluştur…
 user_profile_and_more=Profil ve Ayarlar…
 signed_in_as=Giriş yapan:
@@ -91,6 +92,7 @@ remove=Kaldır
 remove_all=Tümünü Kaldır
 remove_label_str=`"%s" öğesini kaldır`
 edit=Düzenle
+view=Görüntüle
 
 enabled=Aktifleştirilmiş
 disabled=Devre Dışı
@@ -98,6 +100,7 @@ locked=Kilitli
 
 copy=Kopyala
 copy_url=URL'yi kopyala
+copy_hash=Hash'i kopyala
 copy_content=İçeriği kopyala
 copy_branch=Dal adını kopyala
 copy_success=Kopyalandı!
@@ -110,6 +113,7 @@ loading=Yükleniyor…
 
 error=Hata
 error404=Ulaşmaya çalıştığınız sayfa <strong>mevcut değil</strong> veya <strong>görüntüleme yetkiniz yok</strong>.
+go_back=Geri Git
 
 never=Asla
 unknown=Bilinmiyor
@@ -181,6 +185,7 @@ network_error=Ağ hatası
 [startpage]
 app_desc=Zahmetsiz, kendi sunucunuzda barındırabileceğiniz Git servisi
 install=Kurulumu kolay
+install_desc=Platformunuz için <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.com/installation/install-from-binary">ikili dosyayı çalıştırın</a>, <a target="_blank" rel="noopener noreferrer" href="https://github.com/go-gitea/gitea/tree/master/docker">Docker</a> ile yükleyin veya <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.com/installation/install-from-package">paket</a> olarak edinin.
 platform=Farklı platformlarda çalışablir
 platform_desc=Forgejo <a target="_blank" rel="noopener noreferrer" href="http://golang.org/">Go</a> ile derleme yapılabilecek her yerde çalışmaktadır: Windows, macOS, Linux, ARM, vb. Hangisini seviyorsanız onu seçin!
 lightweight=Hafif
@@ -357,6 +362,7 @@ disable_register_prompt=Kayıt işlemi devre dışıdır. Lütfen site yönetici
 disable_register_mail=Kayıt için e-posta doğrulama devre dışıdır.
 manual_activation_only=Etkinleştirmeyi tamamlamak için site yöneticinizle bağlantıya geçin.
 remember_me=Bu Aygıtı hatırla
+remember_me.compromised=Oturum açma tokeni artık geçerli değil, bu ele geçirilmiş bir hesaba işaret ediyor olabilir. Lütfen hesabınızda olağandışı faaliyet olup olmadığını denetleyin.
 forgot_password_title=Şifremi unuttum
 forgot_password=Şifrenizi mi unuttunuz?
 sign_up_now=Bir hesaba mı ihtiyacınız var? Hemen kaydolun.
@@ -376,6 +382,7 @@ email_not_associate=Bu e-posta adresi hiçbir hesap ile ilişkilendirilmemiştir
 send_reset_mail=Hesap Kurtarma E-postası Gönder
 reset_password=Hesap Kurtarma
 invalid_code=Doğrulama kodunuz geçersiz veya süresi dolmuş.
+invalid_code_forgot_password=Onay kodunuz hatalı veya süresi geçmiş. Yeni bir oturum başlatmak için <a href="%s">buraya</a> tıklayın.
 invalid_password=Parolanız hesap oluşturulurken kullanılan parolayla eşleşmiyor.
 reset_password_helper=Hesabı Kurtar
 reset_password_wrong_user=%s olarak oturum açmışsınız, ancak hesap kurtarma bağlantısı %s için
@@ -677,6 +684,7 @@ choose_new_avatar=Yeni Avatar Seç
 update_avatar=Profil Resmini Güncelle
 delete_current_avatar=Güncel Avatarı Sil
 uploaded_avatar_not_a_image=Yüklenen dosya bir resim dosyası değil.
+uploaded_avatar_is_too_big=Yüklenen dosyanın boyutu (%d KiB), azami boyutu (%d KiB) aşıyor.
 update_avatar_success=Profil resminiz değiştirildi.
 update_user_avatar_success=Kullanıcının avatarı güncellendi.
 
@@ -858,6 +866,7 @@ revoke_oauth2_grant_description=Bu üçüncü taraf uygulamasına erişimin ipta
 revoke_oauth2_grant_success=Erişim başarıyla kaldırıldı.
 
 twofa_desc=İki faktörlü kimlik doğrulama, hesabınızın güvenliğini artırır.
+twofa_recovery_tip=Aygıtınızı kaybetmeniz durumunda, hesabınıza tekrar erişmek için tek kullanımlık kurtarma anahtarını kullanabileceksiniz.
 twofa_is_enrolled=Hesabınız şu anda iki faktörlü kimlik doğrulaması içinde <strong>kaydedilmiş</strong>.
 twofa_not_enrolled=Hesabınız şu anda iki faktörlü kimlik doğrulaması içinde kaydedilmemiş.
 twofa_disable=İki Aşamalı Doğrulamayı Devre Dışı Bırak
@@ -880,6 +889,8 @@ webauthn_register_key=Güvenlik Anahtarı Ekle
 webauthn_nickname=Takma Ad
 webauthn_delete_key=Güvenlik Anahtarını Kaldır
 webauthn_delete_key_desc=Bir güvenlik anahtarını kaldırırsanız, onunla artık giriş yapamazsınız. Devam edilsin mi?
+webauthn_key_loss_warning=Güvenlik anahtarlarınızı kaybederseniz, hesabınıza erişimi kaybedersiniz.
+webauthn_alternative_tip=Ek bir kimlik doğrulama yöntemi ayarlamak isteyebilirsiniz.
 
 manage_account_links=Bağlı Hesapları Yönet
 manage_account_links_desc=Bu harici hesaplar Forgejo hesabınızla bağlantılı.
@@ -916,6 +927,7 @@ visibility.private=Özel
 visibility.private_tooltip=Sadece katıldığınız organizasyonların üyeleri tarafından görünür
 
 [repo]
+new_repo_helper=Bir depo, sürüm geçmişi dahil tüm proje dosyalarını içerir. Zaten başka bir yerde mi barındırıyorsunuz? <a href="%s">Depoyu taşıyın.</a>
 owner=Sahibi
 owner_helper=Bazı organizasyonlar, en çok depo sayısı sınırı nedeniyle açılır menüde görünmeyebilir.
 repo_name=Depo İsmi
@@ -936,6 +948,8 @@ fork_from=Buradan Çatalla
 already_forked=%s deposunu zaten çatalladınız
 fork_to_different_account=Başka bir hesaba çatalla
 fork_visibility_helper=Çatallanmış bir deponun görünürlüğü değiştirilemez.
+fork_branch=Çatala klonlanacak dal
+all_branches=Tüm dallar
 fork_no_valid_owners=Geçerli bir sahibi olmadığı için bu depo çatallanamaz.
 use_template=Bu şablonu kullan
 clone_in_vsc=VS Code'ta klonla
@@ -965,6 +979,7 @@ trust_model_helper_collaborator_committer=Ortak çalışan+İşleyen: İşleyenl
 trust_model_helper_default=Varsayılan: Bu kurulum için varsayılan güven modelini kullan
 create_repo=Depo Oluştur
 default_branch=Varsayılan Dal
+default_branch_label=varsayılan
 default_branch_helper=Varsayılan dal, değişiklik istekleri ve kod işlemeleri için temel daldır.
 mirror_prune=Buda
 mirror_prune_desc=Kullanılmayan uzak depoları izleyen referansları kaldır
@@ -1000,8 +1015,13 @@ delete_preexisting=Önceden var olan dosyaları sil
 delete_preexisting_content=%s içindeki dosyaları sil
 delete_preexisting_success=%s içindeki kabul edilmeyen dosyalar silindi
 blame_prior=Bu değişiklikten önceki suçu görüntüle
+blame.ignore_revs=<a href="%s">.git-blame-ignore-revs</a> dosyasındaki sürümler yok sayılıyor. Bunun yerine normal sorumlu görüntüsü için <a href="%s">buraya tıklayın</a>.
+blame.ignore_revs.failed=<a href="%s">.git-blame-ignore-revs</a> dosyasındaki sürümler yok sayılamadı.
 author_search_tooltip=En fazla 30 kullanıcı görüntüler
 
+tree_path_not_found_commit=%[1] yolu, %[2]s işlemesinde mevcut değil
+tree_path_not_found_branch=%[1] yolu, %[2]s dalında mevcut değil
+tree_path_not_found_tag=%[1] yolu, %[2]s etiketinde mevcut değil
 
 transfer.accept=Aktarımı Kabul Et
 transfer.accept_desc=`"%s" tarafına aktar`
@@ -1265,6 +1285,7 @@ commits.signed_by_untrusted_user=Güvenilmeyen kullanıcı tarafından imzaland
 commits.signed_by_untrusted_user_unmatched=İşleyici ile eşleşmeyen güvenilmeyen kullanıcı tarafından imzalanmış
 commits.gpg_key_id=GPG Anahtar Kimliği
 commits.ssh_key_fingerprint=SSH Anahtar Parmak İzi
+commits.view_path=Geçmişte bu noktayı görüntüle
 
 commit.operations=İşlemler
 commit.revert=Geri Al
@@ -1475,8 +1496,17 @@ issues.ref_closed_from=`<a href="%[3]s">bu konuyu kapat%[4]s</a> <a id="%[1]s" h
 issues.ref_reopened_from=`<a href="%[3]s">konuyu yeniden aç%[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
 issues.ref_from=`%[1]s'den`
 issues.author=Yazar
+issues.author_helper=Bu kullanıcı yazardır.
 issues.role.owner=Sahibi
+issues.role.owner_helper=Bu kullanıcı bu deponun sahibidir.
 issues.role.member=Üye
+issues.role.member_helper=Bu kullanıcı bu deponun sahibi olan organizasyonun üyesidir.
+issues.role.collaborator=Katkıcı
+issues.role.collaborator_helper=Kullanıcı bu depoya işbirliği için davet edildi.
+issues.role.first_time_contributor=İlk defa katkıcı
+issues.role.first_time_contributor_helper=Bu, bu kullanıcının bu depoya ilk katkısı.
+issues.role.contributor=Katılımcı
+issues.role.contributor_helper=Bu kullanıcı bu depoya daha önce işleme gönderdi.
 issues.re_request_review=İncelemeyi yeniden iste
 issues.is_stale=Bu incelemeden bu yana bu istekte değişiklikler oldu
 issues.remove_request_review=İnceleme isteğini kaldır
@@ -1492,6 +1522,8 @@ issues.label_description=Etiket açıklaması
 issues.label_color=Etiket rengi
 issues.label_exclusive=Özel
 issues.label_archive=Etiketi Arşivle
+issues.label_archived_filter=Arşivlenmiş etiketleri göster
+issues.label_archive_tooltip=Arşivlenmiş etiketler, etiket araması yapılırken varsayılan olarak önerilerin dışında tutuluyor.
 issues.label_exclusive_desc=<code>Kapsam/öğe</code> etiketini, diğer <code>kapsam/</code> etiketleriyle ayrışık olacak şekilde adlandırın.
 issues.label_exclusive_warning=Çakışan kapsamlı etiketler, bir konu veya değişiklik isteği etiketleri düzenlenirken kaldırılacaktır.
 issues.label_count=%d etiket
@@ -1746,6 +1778,7 @@ pulls.rebase_conflict_summary=Hata Mesajı
 pulls.unrelated_histories=Birleştirme Başarısız: Birleştirme başlığı ve tabanı ortak bir geçmişi paylaşmıyor. İpucu: Farklı bir strateji deneyin
 pulls.merge_out_of_date=Birleştirme Başarısız: Birleştirme oluşturulurken, taban güncellendi. İpucu: Tekrar deneyin.
 pulls.head_out_of_date=Birleştirme Başarısız: Birleştirme oluşturulurken, ana güncellendi. İpucu: Tekrar deneyin.
+pulls.has_merged=Başarısız: Değişiklik isteği birleştirildi, yeniden birleştiremez veya hedef dalı değiştiremezsiniz.
 pulls.push_rejected=Birleştirme Başarısız Oldu: Gönderme reddedildi. Bu depo için Git İstemcilerini inceleyin.
 pulls.push_rejected_summary=Tam Red Mesajı
 pulls.push_rejected_no_message=Birleştirme başarısız oldu: Gönderme reddedildi, ancak uzak bir mesaj yoktu.<br>Bu depo için Git İstemcilerini inceleyin
@@ -1757,6 +1790,8 @@ pulls.status_checks_failure=Bazı kontroller başarısız oldu
 pulls.status_checks_error=Bazı kontroller hatalar bildirdi
 pulls.status_checks_requested=Gerekli
 pulls.status_checks_details=Ayrıntılar
+pulls.status_checks_hide_all=Tüm denetlemeleri gizle
+pulls.status_checks_show_all=Tüm denetlemeleri göster
 pulls.update_branch=Dalı birleştirmeyle güncelle
 pulls.update_branch_rebase=Dalı yeniden yapılandırmayla güncelle
 pulls.update_branch_success=Dal güncellemesi başarıyla gerçekleştirildi
@@ -1765,6 +1800,11 @@ pulls.outdated_with_base_branch=Bu dal, temel dal ile güncel değil
 pulls.close=Değişiklik İsteğini Kapat
 pulls.closed_at=`<a id="%[1]s" href="#%[1]s">%[2]s</a> değişiklik isteğini kapattı`
 pulls.reopened_at=`<a id="%[1]s" href="#%[1]s">%[2]s</a> değişiklik isteğini yeniden açtı`
+pulls.cmd_instruction_hint=`<a class="show-instruction">Komut satırı talimatlarını</a> görüntüleyin.`
+pulls.cmd_instruction_checkout_title=Çekme
+pulls.cmd_instruction_checkout_desc=Proje deponuzdan yeni bir dalı çekin ve değişiklikleri test edin.
+pulls.cmd_instruction_merge_title=Birleştir
+pulls.cmd_instruction_merge_desc=Değişiklikleri birleştirin ve Gitea'da güncelleyin.
 pulls.clear_merge_message=Birleştirme iletilerini temizle
 pulls.clear_merge_message_hint=Birleştirme iletisini temizlemek sadece işleme ileti içeriğini kaldırır ama üretilmiş "Co-Authored-By …" gibi git fragmanlarını korur.
 
@@ -1810,6 +1850,8 @@ milestones.edit_success=`"%s" dönüm noktası güncellendi.`
 milestones.deletion=Kilometre Taşını Sil
 milestones.deletion_desc=Bir kilometre taşını silmek, onu ilgili tüm sorunlardan kaldırır. Devam edilsin mi?
 milestones.deletion_success=Kilometre taşı silindi.
+milestones.filter_sort.earliest_due_data=En erken bitiş tarihi
+milestones.filter_sort.latest_due_date=En uzak bitiş tarihi
 milestones.filter_sort.least_complete=En az tamamlama
 milestones.filter_sort.most_complete=En çok tamamlama
 milestones.filter_sort.most_issues=En çok konu
@@ -1972,6 +2014,8 @@ settings.mirror_settings.push_mirror.add=Yansı Gönderimi Ekle
 settings.mirror_settings.push_mirror.edit_sync_time=Yansı eşzamanlama aralığını düzenle
 
 settings.sync_mirror=Şimdi Eşitle
+settings.pull_mirror_sync_in_progress=Şu an %s uzak sunucusundan değişiklikler çekiliyor.
+settings.push_mirror_sync_in_progress=Şu an %s uzak sunucusuna değişiklikler itiliyor.
 settings.site=Web Sitesi
 settings.update_settings=Ayarları Güncelle
 settings.update_mirror_settings=Yansı Ayarları Güncelle
@@ -2105,12 +2149,14 @@ settings.webhook_deletion_desc=Bir web isteğini kaldırmak, ayarlarını ve tes
 settings.webhook_deletion_success=Web isteği silindi.
 settings.webhook.test_delivery=Test Dağıtımı
 settings.webhook.test_delivery_desc=Bu web isteğini sahte bir olayla test edin.
+settings.webhook.test_delivery_desc_disabled=Bu web istemcisini sahte bir olayla denemek için etkinleştirin.
 settings.webhook.request=İstekler
 settings.webhook.response=Cevaplar
 settings.webhook.headers=Başlıklar
 settings.webhook.payload=İçerik
 settings.webhook.body=Gövde
 settings.webhook.replay.description=Bu web kancasını tekrar çalıştır.
+settings.webhook.replay.description_disabled=Bu web istemcisini yeniden oynatmak için etkinleştirin.
 settings.webhook.delivery.success=Teslim kuyruğuna bir olay eklendi. Teslim geçmişinde görünmesi birkaç saniye alabilir.
 settings.githooks_desc=Git İstemcileri Git'in kendisi tarafından desteklenmektedir. Özel işlemler ayarlamak için aşağıdaki istemci dosyalarını düzenleyebilirsiniz.
 settings.githook_edit_desc=İstek aktif değilse örnek içerik sunulacaktır. İçeriği boş bırakmak, isteği devre dışı bırakmayı beraberinde getirecektir.
@@ -2271,6 +2317,7 @@ settings.dismiss_stale_approvals_desc=Değişiklik isteğinin içeriğini deği
 settings.require_signed_commits=İmzalı İşleme Gerekli
 settings.require_signed_commits_desc=Reddetme, onlar imzasızsa veya doğrulanamazsa bu dala gönderir.
 settings.protect_branch_name_pattern=Korunmuş Dal Adı Deseni
+settings.protect_branch_name_pattern_desc=Korunmuş dal isim desenleri. Desen sözdizimi için <a href="https://github.com/gobwas/glob">belgelere</a> bakabilirsiniz. Örnekler: main, release/**
 settings.protect_patterns=Desenler
 settings.protect_protected_file_patterns=Korumalı dosya kalıpları (noktalı virgülle ayrılmış ';'):
 settings.protect_protected_file_patterns_desc=Kullanıcının bu dalda dosya ekleme, düzenleme veya silme hakları olsa bile doğrudan değiştirilmesine izin verilmeyen korumalı dosyalar. Birden çok desen noktalı virgül (';') kullanılarak ayrılabilir. Desen sözdizimi için <a href='https://pkg.go.dev/github.com/gobwas/glob#Compile'>github.com/gobwas/glob</a> belgelerine bakın. Örnekler: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>.
@@ -2307,6 +2354,7 @@ settings.tags.protection.allowed.teams=İzin verilen takımlar
 settings.tags.protection.allowed.noone=Hiç kimse
 settings.tags.protection.create=Etiketi Koru
 settings.tags.protection.none=Korumalı etiket yok.
+settings.tags.protection.pattern.description=Birden çok etiketi eşleştirmek için tek bir ad, glob deseni veya normal ifade kullanabilirsiniz. Daha fazlası için <a target="_blank" rel="noopener" href="https://docs.gitea.com/usage/protected-tags">korumalı etiketler rehberini</a> okuyun.
 settings.bot_token=Bot Jetonu
 settings.chat_id=Sohbet Kimliği
 settings.thread_id=İş Parçacığı ID
@@ -2487,6 +2535,7 @@ branch.default_deletion_failed=`"%s" dalı varsayılan daldır. Silinemez.`
 branch.restore=`"%s" Dalını Geri Yükle`
 branch.download=`"%s" Dalını İndir`
 branch.rename=`"%s" Dalının Adını Değiştir`
+branch.search=Dal Ara
 branch.included_desc=Bu dal varsayılan dalın bir parçasıdır
 branch.included=Dahil
 branch.create_new_branch=Şu daldan dal oluştur:
@@ -2703,6 +2752,7 @@ dashboard.reinit_missing_repos=Kayıtları bulunanlar için tüm eksik Git depol
 dashboard.sync_external_users=Harici kullanıcı verisini senkronize et
 dashboard.cleanup_hook_task_table=Hook_task tablosunu temizleme
 dashboard.cleanup_packages=Süresi dolmuş paketleri temizleme
+dashboard.cleanup_actions=Eylemlerin süresi geçmiş günlük ve yapılarını temizle
 dashboard.server_uptime=Sunucunun Ayakta Kalma Süresi
 dashboard.current_goroutine=Güncel Goroutine'ler
 dashboard.current_memory_usage=Güncel Bellek Kullanımı
@@ -2740,7 +2790,9 @@ dashboard.gc_lfs=LFS üst nesnelerin atıklarını temizle
 dashboard.stop_zombie_tasks=Zombi görevleri durdur
 dashboard.stop_endless_tasks=Daimi görevleri durdur
 dashboard.cancel_abandoned_jobs=Terkedilmiş görevleri iptal et
+dashboard.start_schedule_tasks=Zamanlanmış görevleri başlat
 dashboard.sync_branch.started=Dal Eşzamanlaması başladı
+dashboard.rebuild_issue_indexer=Konu indeksini yeniden oluştur
 
 users.user_manage_panel=Kullanıcı Hesap Yönetimi
 users.new_account=Yeni Kullanıcı Hesabı
@@ -2749,6 +2801,9 @@ users.full_name=Tam İsim
 users.activated=Aktifleştirilmiş
 users.admin=Yönetici
 users.restricted=Kısıtlanmış
+users.reserved=Rezerve
+users.bot=Bot
+users.remote=Uzak
 users.2fa=2FD
 users.repos=Depolar
 users.created=Oluşturuldu
@@ -2795,6 +2850,7 @@ users.list_status_filter.is_prohibit_login=Oturum Açmayı Önle
 users.list_status_filter.not_prohibit_login=Oturum Açmaya İzin Ver
 users.list_status_filter.is_2fa_enabled=2FA Etkin
 users.list_status_filter.not_2fa_enabled=2FA Devre Dışı
+users.details=Kullanıcı Ayrıntıları
 
 emails.email_manage_panel=Kullanıcı E-posta Yönetimi
 emails.primary=Birincil
@@ -2807,6 +2863,7 @@ emails.updated=E-posta güncellendi
 emails.not_updated=İstenen e-posta adresi güncellenemedi: %v
 emails.duplicate_active=Bu e-posta adresi farklı bir kullanıcı için zaten aktif.
 emails.change_email_header=E-posta Özelliklerini Güncelle
+emails.change_email_text=Bu e-posta adresini güncellemek istediğinizden emin misiniz?
 
 orgs.org_manage_panel=Organizasyon Yönetimi
 orgs.name=İsim
@@ -2831,6 +2888,7 @@ packages.package_manage_panel=Paket Yönetimi
 packages.total_size=Toplam Boyut: %s
 packages.unreferenced_size=Referanssız Boyut: %s
 packages.cleanup=Süresi dolmuş veriyi temizle
+packages.cleanup.success=Süresi dolmuş veri başarıyla temizlendi
 packages.owner=Sahibi
 packages.creator=Oluşturan
 packages.name=İsim
@@ -2841,10 +2899,12 @@ packages.size=Boyut
 packages.published=Yayınlandı
 
 defaulthooks=Varsayılan Web İstemcileri
+defaulthooks.desc=Web İstemcileri, belirli Gitea olayları tetiklendiğinde otomatik olarak HTTP POST isteklerini sunucuya yapar. Burada tanımlanan Web İstemcileri varsayılandır ve tüm yeni depolara kopyalanır. <a target="_blank" rel="noopener" href="https://docs.gitea.com/usage/webhooks">web istemcileri kılavuzunda</a> daha fazla bilgi edinin.
 defaulthooks.add_webhook=Varsayılan Web İstemcisi Ekle
 defaulthooks.update_webhook=Varsayılan Web İstemcisini Güncelle
 
 systemhooks=Sistem Web İstemcileri
+systemhooks.desc=Belirli Gitea olayları tetiklendiğinde Web istemcileri otomatik olarak bir sunucuya HTTP POST istekleri yapar. Burada tanımlanan web istemcileri sistemdeki tüm depolar üzerinde çalışır, bu yüzden lütfen bunun olabilecek tüm performans sonuçlarını göz önünde bulundurun. <a target="_blank" rel="noopener" href="https://docs.gitea.com/usage/webhooks">web istemcileri kılavuzunda</a> daha fazla bilgi edinin.
 systemhooks.add_webhook=Sistem Web İstemcisi Ekle
 systemhooks.update_webhook=Sistem Web İstemcisi Güncelle
 
@@ -2949,6 +3009,7 @@ auths.tip.google_plus=OAuth2 istemci kimlik bilgilerini https://console.develope
 auths.tip.openid_connect=Bitiş noktalarını belirlemek için OpenID Connect Discovery URL'sini kullanın (<server>/.well-known/openid-configuration)
 auths.tip.twitter=https://dev.twitter.com/apps adresine gidin, bir uygulama oluşturun ve “Bu uygulamanın Twitter ile oturum açmak için kullanılmasına izin ver” seçeneğinin etkin olduğundan emin olun
 auths.tip.discord=https://discordapp.com/developers/applications/me adresinde yeni bir uygulama kaydedin
+auths.tip.gitea=Yeni bir OAuth2 uygulaması kaydedin. Rehber https://docs.gitea.com/development/oauth2-provider adresinde bulunabilir
 auths.tip.yandex=`https://oauth.yandex.com/client/new adresinde yeni bir uygulama oluşturun. "Yandex.Passport API'sı" bölümünden aşağıdaki izinleri seçin: "E-posta adresine erişim", "Kullanıcı avatarına erişim" ve "Kullanıcı adına, ad ve soyadına, cinsiyete erişim"`
 auths.tip.mastodon=Kimlik doğrulaması yapmak istediğiniz mastodon örneği için özel bir örnek URL girin (veya varsayılan olanı kullanın)
 auths.edit=Kimlik Doğrulama Kaynağı Düzenle
@@ -3128,8 +3189,10 @@ monitor.queue.name=İsim
 monitor.queue.type=Tür
 monitor.queue.exemplar=Örnek Türü
 monitor.queue.numberworkers=Çalışan Sayısı
+monitor.queue.activeworkers=Etkin Çalışanlar
 monitor.queue.maxnumberworkers=En Fazla Çalışan Sayısı
 monitor.queue.numberinqueue=Kuyruktaki Sayı
+monitor.queue.review_add=Çalışanları İncele / Ekle
 monitor.queue.settings.title=Havuz Ayarları
 monitor.queue.settings.desc=Havuzlar, çalışan kuyruğu tıkanmasına bir yanıt olarak dinamik olarak büyürler.
 monitor.queue.settings.maxnumberworkers=En fazla çalışan Sayısı
@@ -3456,23 +3519,31 @@ runners.status.idle=Boşta
 runners.status.active=Etkin
 runners.status.offline=Çevrimdışı
 runners.version=Sürüm
+runners.reset_registration_token=Kayıt tokenini sıfırla
 runners.reset_registration_token_success=Çalıştırıcı kayıt belirteci başarıyla sıfırlandı
 
 runs.all_workflows=Tüm İş Akışları
 runs.commit=İşle
+runs.scheduled=Zamanlanmış
 runs.pushed_by=iten
 runs.invalid_workflow_helper=İş akışı yapılandırma dosyası geçersiz. Lütfen yapılandırma dosyanızı denetleyin: %s
+runs.no_matching_online_runner_helper=Şu etiket ile eşleşen çevrimiçi çalıştırıcı bulunamadı: %s
 runs.actor=Aktör
 runs.status=Durum
 runs.actors_no_select=Tüm aktörler
 runs.status_no_select=Tüm durumlar
 runs.no_results=Eşleşen sonuç yok.
+runs.no_workflows=Henüz hiç bir iş akışı yok.
+runs.no_workflows.quick_start=Gitea İşlem'i nasıl başlatacağınızı bilmiyor musunuz? <a target="_blank" rel="noopener noreferrer" href="%s">Hızlı başlangıç rehberine</a> bakabilirsiniz.
+runs.no_workflows.documentation=Gitea İşlem'i hakkında daha fazla bilgi için, <a target="_blank" rel="noopener noreferrer" href="%s">belgeye</a> bakabilirsiniz.
 runs.no_runs=İş akışı henüz hiç çalıştırılmadı.
+runs.empty_commit_message=(boş işleme iletisi)
 
 workflow.disable=İş Akışını Devre Dışı Bırak
 workflow.disable_success='%s' iş akışı başarıyla devre dışı bırakıldı.
 workflow.enable=İş Akışını Etkinleştir
 workflow.enable_success='%s' iş akışı başarıyla etkinleştirildi.
+workflow.disabled=İş akışı devre dışı.
 
 need_approval_desc=Değişiklik isteği çatalında iş akışı çalıştırmak için onay gerekiyor.
 

From fd3b4afa2b3621ece2d7d1587fd4b017142d75a0 Mon Sep 17 00:00:00 2001
From: yp05327 <576951401@qq.com>
Date: Sat, 17 Feb 2024 14:13:37 +0900
Subject: [PATCH 22/28] Fix broken following organization (#29005)

- following organization is broken from #28908
- add login check for the follow button in organization profile page

(cherry picked from commit 68227996a7a84a240b36c304d04c5c8d82948df8)
---
 routers/web/user/profile.go | 14 ++++++++++++--
 templates/org/home.tmpl     | 16 +++++++++-------
 2 files changed, 21 insertions(+), 9 deletions(-)

diff --git a/routers/web/user/profile.go b/routers/web/user/profile.go
index 7b04948ab0..cc1953debf 100644
--- a/routers/web/user/profile.go
+++ b/routers/web/user/profile.go
@@ -31,6 +31,7 @@ import (
 
 const (
 	tplProfileBigAvatar base.TplName = "shared/user/profile_big_avatar"
+	tplFollowUnfollow   base.TplName = "shared/user/follow_unfollow"
 )
 
 // OwnerProfile render profile page for a user or a organization (aka, repo owner)
@@ -349,6 +350,15 @@ func Action(ctx *context.Context) {
 		return
 	}
 
-	shared_user.PrepareContextForProfileBigAvatar(ctx)
-	ctx.HTML(http.StatusOK, tplProfileBigAvatar)
+	if ctx.ContextUser.IsIndividual() {
+		shared_user.PrepareContextForProfileBigAvatar(ctx)
+		ctx.HTML(http.StatusOK, tplProfileBigAvatar)
+		return
+	} else if ctx.ContextUser.IsOrganization() {
+		ctx.Data["IsFollowing"] = ctx.Doer != nil && user_model.IsFollowing(ctx, ctx.Doer.ID, ctx.ContextUser.ID)
+		ctx.HTML(http.StatusOK, tplFollowUnfollow)
+		return
+	}
+	log.Error("Failed to apply action %q: unsupport context user type: %s", ctx.FormString("action"), ctx.ContextUser.Type)
+	ctx.Error(http.StatusBadRequest, fmt.Sprintf("Action %q failed", ctx.FormString("action")))
 }
diff --git a/templates/org/home.tmpl b/templates/org/home.tmpl
index c2322b75fb..2ca60cd1be 100644
--- a/templates/org/home.tmpl
+++ b/templates/org/home.tmpl
@@ -30,13 +30,15 @@
 				{{svg "octicon-rss" 24}}
 			</a>
 			{{end}}
-			<button class="link-action ui basic button gt-mr-0" data-url="{{.Org.HomeLink}}?action={{if $.IsFollowing}}unfollow{{else}}follow{{end}}">
-				{{if $.IsFollowing}}
-					{{ctx.Locale.Tr "user.unfollow"}}
-				{{else}}
-					{{ctx.Locale.Tr "user.follow"}}
-				{{end}}
-			</button>
+			{{if .IsSigned}}
+				<button class="ui basic button gt-mr-0" hx-post="{{.Org.HomeLink}}?action={{if $.IsFollowing}}unfollow{{else}}follow{{end}}">
+					{{if $.IsFollowing}}
+						{{ctx.Locale.Tr "user.unfollow"}}
+					{{else}}
+						{{ctx.Locale.Tr "user.follow"}}
+					{{end}}
+				</button>
+			{{end}}
 		</div>
 	</div>
 

From c0ced0f0cd7a4050df55fd19f6b881e9747857eb Mon Sep 17 00:00:00 2001
From: Robin Schoonover <robin@cornhooves.org>
Date: Fri, 16 Feb 2024 22:40:13 -0700
Subject: [PATCH 23/28] Fix debian InRelease Acquire-By-Hash newline (#29204)

There is a missing newline when generating the debian apt repo InRelease
file, which results in output like:

```
[...]
Date: Wed, 14 Feb 2024 05:03:01 UTC
Acquire-By-Hash: yesMD5Sum:
 51a518dbddcd569ac3e0cebf330c800a 3018 main-dev/binary-amd64/Packages
[...]
```

It appears this would probably result in apt ignoring the
Acquire-By-Hash setting and not using the by-hash functionality,
although I'm not sure how to confirm it.

(cherry picked from commit 33400a02d4eb35a0656fd6d20fc56801de09b959)
---
 services/packages/debian/repository.go | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/services/packages/debian/repository.go b/services/packages/debian/repository.go
index 86c54e40c8..611faa6ade 100644
--- a/services/packages/debian/repository.go
+++ b/services/packages/debian/repository.go
@@ -342,7 +342,7 @@ func buildReleaseFiles(ctx context.Context, ownerID int64, repoVersion *packages
 	fmt.Fprintf(w, "Components: %s\n", strings.Join(components, " "))
 	fmt.Fprintf(w, "Architectures: %s\n", strings.Join(architectures, " "))
 	fmt.Fprintf(w, "Date: %s\n", time.Now().UTC().Format(time.RFC1123))
-	fmt.Fprint(w, "Acquire-By-Hash: yes")
+	fmt.Fprint(w, "Acquire-By-Hash: yes\n")
 
 	pfds, err := packages_model.GetPackageFileDescriptors(ctx, pfs)
 	if err != nil {

From 99080bd87d99c8df0186007b32bebc654309d74d Mon Sep 17 00:00:00 2001
From: Yarden Shoham <git@yardenshoham.com>
Date: Sat, 17 Feb 2024 15:11:56 +0200
Subject: [PATCH 24/28] Remove jQuery from issue reference context popup attach
 (#29216)

- Switched to plain JavaScript
- Tested the context popup functionality and it works as before

# Demo using JavaScript without jQuery

![action](https://github.com/go-gitea/gitea/assets/20454870/1d2f173e-e626-4f7d-82c8-d1539d38d247)

Signed-off-by: Yarden Shoham <git@yardenshoham.com>
(cherry picked from commit c282d378bd1f2f11ffc884cd6d7c073b7b5745f8)
---
 web_src/js/features/contextpopup.js | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/web_src/js/features/contextpopup.js b/web_src/js/features/contextpopup.js
index 23a620b8a2..51363b810a 100644
--- a/web_src/js/features/contextpopup.js
+++ b/web_src/js/features/contextpopup.js
@@ -1,11 +1,10 @@
-import $ from 'jquery';
 import {createApp} from 'vue';
 import ContextPopup from '../components/ContextPopup.vue';
 import {parseIssueHref} from '../utils.js';
 import {createTippy} from '../modules/tippy.js';
 
 export function initContextPopups() {
-  const refIssues = $('.ref-issue');
+  const refIssues = document.querySelectorAll('.ref-issue');
   attachRefIssueContextPopup(refIssues);
 }
 

From ca852519b54a50f86f84dbc06afe8a4e2efb7990 Mon Sep 17 00:00:00 2001
From: Yarden Shoham <git@yardenshoham.com>
Date: Sat, 17 Feb 2024 15:17:04 +0200
Subject: [PATCH 25/28] Remove jQuery from the webhook editor (#29211)

- Switched to plain JavaScript
- Tested the webhook editing functionality and it works as before

# Demo using JavaScript without jQuery

![action](https://github.com/go-gitea/gitea/assets/20454870/b24c264d-d5e5-4954-8789-e72564a99027)

---------

Signed-off-by: Yarden Shoham <git@yardenshoham.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
(cherry picked from commit 27192bc321161a4e648547bd7b071065a7b18326)
---
 web_src/js/features/comp/WebHookEditor.js | 56 +++++++++++------------
 1 file changed, 27 insertions(+), 29 deletions(-)

diff --git a/web_src/js/features/comp/WebHookEditor.js b/web_src/js/features/comp/WebHookEditor.js
index f4c82898fd..86d21dc815 100644
--- a/web_src/js/features/comp/WebHookEditor.js
+++ b/web_src/js/features/comp/WebHookEditor.js
@@ -1,43 +1,41 @@
-import $ from 'jquery';
+import {POST} from '../../modules/fetch.js';
 import {hideElem, showElem, toggleElem} from '../../utils/dom.js';
 
-const {csrfToken} = window.config;
-
 export function initCompWebHookEditor() {
-  if ($('.new.webhook').length === 0) {
+  if (!document.querySelectorAll('.new.webhook').length) {
     return;
   }
 
-  $('.events.checkbox input').on('change', function () {
-    if ($(this).is(':checked')) {
-      showElem($('.events.fields'));
-    }
-  });
-  $('.non-events.checkbox input').on('change', function () {
-    if ($(this).is(':checked')) {
-      hideElem($('.events.fields'));
-    }
-  });
+  for (const input of document.querySelectorAll('.events.checkbox input')) {
+    input.addEventListener('change', function () {
+      if (this.checked) {
+        showElem('.events.fields');
+      }
+    });
+  }
+
+  for (const input of document.querySelectorAll('.non-events.checkbox input')) {
+    input.addEventListener('change', function () {
+      if (this.checked) {
+        hideElem('.events.fields');
+      }
+    });
+  }
 
   const updateContentType = function () {
-    const visible = $('#http_method').val() === 'POST';
-    toggleElem($('#content_type').parent().parent(), visible);
+    const visible = document.getElementById('http_method').value === 'POST';
+    toggleElem(document.getElementById('content_type').parentNode.parentNode, visible);
   };
   updateContentType();
-  $('#http_method').on('change', () => {
-    updateContentType();
-  });
+
+  document.getElementById('http_method').addEventListener('change', updateContentType);
 
   // Test delivery
-  $('#test-delivery').on('click', function () {
-    const $this = $(this);
-    $this.addClass('loading disabled');
-    $.post($this.data('link'), {
-      _csrf: csrfToken
-    }).done(
-      setTimeout(() => {
-        window.location.href = $this.data('redirect');
-      }, 5000)
-    );
+  document.getElementById('test-delivery')?.addEventListener('click', async function () {
+    this.classList.add('loading', 'disabled');
+    await POST(this.getAttribute('data-link'));
+    setTimeout(() => {
+      window.location.href = this.getAttribute('data-redirect');
+    }, 5000);
   });
 }

From a46fa02d5b5977040fee1f9501c2c45bf0301b76 Mon Sep 17 00:00:00 2001
From: Yarden Shoham <git@yardenshoham.com>
Date: Sat, 17 Feb 2024 15:42:52 +0200
Subject: [PATCH 26/28] Fix missing template for follow button in organization
 (#29215)

Leftover from https://github.com/go-gitea/gitea/pull/29005

# Before

![before](https://github.com/go-gitea/gitea/assets/20454870/24c74278-ccac-4dc6-bf26-713e90c07239)

# After

![after](https://github.com/go-gitea/gitea/assets/20454870/f91d503b-87d4-4c17-a56c-9c0a81fd9082)

---------

Signed-off-by: Yarden Shoham <git@yardenshoham.com>
(cherry picked from commit aa6f88638fb827d5c5ed7506e5fc06dad92beea7)
---
 routers/web/user/profile.go        | 2 +-
 templates/org/follow_unfollow.tmpl | 7 +++++++
 templates/org/home.tmpl            | 8 +-------
 3 files changed, 9 insertions(+), 8 deletions(-)
 create mode 100644 templates/org/follow_unfollow.tmpl

diff --git a/routers/web/user/profile.go b/routers/web/user/profile.go
index cc1953debf..1a9a9cf603 100644
--- a/routers/web/user/profile.go
+++ b/routers/web/user/profile.go
@@ -31,7 +31,7 @@ import (
 
 const (
 	tplProfileBigAvatar base.TplName = "shared/user/profile_big_avatar"
-	tplFollowUnfollow   base.TplName = "shared/user/follow_unfollow"
+	tplFollowUnfollow   base.TplName = "org/follow_unfollow"
 )
 
 // OwnerProfile render profile page for a user or a organization (aka, repo owner)
diff --git a/templates/org/follow_unfollow.tmpl b/templates/org/follow_unfollow.tmpl
new file mode 100644
index 0000000000..b9a3bb77fe
--- /dev/null
+++ b/templates/org/follow_unfollow.tmpl
@@ -0,0 +1,7 @@
+<button class="ui basic button gt-mr-0" hx-post="{{.Org.HomeLink}}?action={{if $.IsFollowing}}unfollow{{else}}follow{{end}}">
+	{{if $.IsFollowing}}
+		{{ctx.Locale.Tr "user.unfollow"}}
+	{{else}}
+		{{ctx.Locale.Tr "user.follow"}}
+	{{end}}
+</button>
diff --git a/templates/org/home.tmpl b/templates/org/home.tmpl
index 2ca60cd1be..fd2326ffd5 100644
--- a/templates/org/home.tmpl
+++ b/templates/org/home.tmpl
@@ -31,13 +31,7 @@
 			</a>
 			{{end}}
 			{{if .IsSigned}}
-				<button class="ui basic button gt-mr-0" hx-post="{{.Org.HomeLink}}?action={{if $.IsFollowing}}unfollow{{else}}follow{{end}}">
-					{{if $.IsFollowing}}
-						{{ctx.Locale.Tr "user.unfollow"}}
-					{{else}}
-						{{ctx.Locale.Tr "user.follow"}}
-					{{end}}
-				</button>
+				{{template "org/follow_unfollow" .}}
 			{{end}}
 		</div>
 	</div>

From f9c931d85881949287eddccc6071265e9980eb2e Mon Sep 17 00:00:00 2001
From: Yarden Shoham <git@yardenshoham.com>
Date: Sat, 17 Feb 2024 16:32:43 +0200
Subject: [PATCH 27/28] Fix label `for` pointing to a `name` instead of `id` in
 webhook settings (#29209)

Here's the spec for the `for` attribute:
https://html.spec.whatwg.org/multipage/forms.html#attr-label-for

Signed-off-by: Yarden Shoham <git@yardenshoham.com>
(cherry picked from commit 0157db84b13203877c098a258abeb387d59f3486)
---
 templates/repo/settings/webhook/settings.tmpl | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/templates/repo/settings/webhook/settings.tmpl b/templates/repo/settings/webhook/settings.tmpl
index addf99d45a..3dfa094cf5 100644
--- a/templates/repo/settings/webhook/settings.tmpl
+++ b/templates/repo/settings/webhook/settings.tmpl
@@ -254,7 +254,7 @@
 <!-- Branch filter -->
 <div class="field">
 	<label for="branch_filter">{{ctx.Locale.Tr "repo.settings.branch_filter"}}</label>
-	<input name="branch_filter" type="text" value="{{or .Webhook.BranchFilter "*"}}">
+	<input id="branch_filter" name="branch_filter" type="text" value="{{or .Webhook.BranchFilter "*"}}">
 	<span class="help">{{ctx.Locale.Tr "repo.settings.branch_filter_desc" | Str2html}}</span>
 </div>
 

From 2685be9f950ad6079cc03429059173db2b27e634 Mon Sep 17 00:00:00 2001
From: Yarden Shoham <git@yardenshoham.com>
Date: Sat, 17 Feb 2024 17:01:25 +0200
Subject: [PATCH 28/28] Fix labels referencing the wrong ID in the user profile
 settings (#29199)

2 instances of `for` with a wrong value and 1 `for` that had a reference
to a `name` instead of `id`.

---------

Signed-off-by: Yarden Shoham <git@yardenshoham.com>
(cherry picked from commit 1d275c1748a75a01c270f5c306c5248808016aba)
---
 templates/user/settings/profile.tmpl | 12 ++++++------
 tests/integration/auth_ldap_test.go  |  2 +-
 2 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/templates/user/settings/profile.tmpl b/templates/user/settings/profile.tmpl
index 1f32aed0e8..d1c68656b6 100644
--- a/templates/user/settings/profile.tmpl
+++ b/templates/user/settings/profile.tmpl
@@ -22,8 +22,8 @@
 					<input id="full_name" name="full_name" value="{{.SignedUser.FullName}}" maxlength="100">
 				</div>
 				<div class="field {{if .Err_Email}}error{{end}}">
-					<label for="email">{{ctx.Locale.Tr "email"}}</label>
-					<p>{{.SignedUser.Email}}</p>
+					<label>{{ctx.Locale.Tr "email"}}</label>
+					<p id="signed-user-email">{{.SignedUser.Email}}</p>
 				</div>
 				<div class="field {{if .Err_Description}}error{{end}}">
 					<label for="description">{{ctx.Locale.Tr "user.user_bio"}}</label>
@@ -42,11 +42,11 @@
 				<!-- private block -->
 
 				<div class="field" id="privacy-user-settings">
-					<label for="security-private"><strong>{{ctx.Locale.Tr "settings.privacy"}}</strong></label>
+					<label><strong>{{ctx.Locale.Tr "settings.privacy"}}</strong></label>
 				</div>
 
 				<div class="inline field {{if .Err_Visibility}}error{{end}}">
-					<span class="inline required field"><label for="visibility">{{ctx.Locale.Tr "settings.visibility"}}</label></span>
+					<span class="inline required field"><label>{{ctx.Locale.Tr "settings.visibility"}}</label></span>
 					<div class="ui selection type dropdown">
 						{{if .SignedUser.Visibility.IsPublic}}<input type="hidden" id="visibility" name="visibility" value="0">{{end}}
 						{{if .SignedUser.Visibility.IsLimited}}<input type="hidden" id="visibility" name="visibility" value="1">{{end}}
@@ -120,8 +120,8 @@
 				</div>
 
 				<div class="inline field gt-pl-4">
-					<label for="avatar">{{ctx.Locale.Tr "settings.choose_new_avatar"}}</label>
-					<input name="avatar" type="file" accept="image/png,image/jpeg,image/gif,image/webp">
+					<label for="new-avatar">{{ctx.Locale.Tr "settings.choose_new_avatar"}}</label>
+					<input id="new-avatar" name="avatar" type="file" accept="image/png,image/jpeg,image/gif,image/webp">
 				</div>
 
 				<div class="field">
diff --git a/tests/integration/auth_ldap_test.go b/tests/integration/auth_ldap_test.go
index 2d69dfcfd7..3a5fdb97a6 100644
--- a/tests/integration/auth_ldap_test.go
+++ b/tests/integration/auth_ldap_test.go
@@ -179,7 +179,7 @@ func TestLDAPUserSignin(t *testing.T) {
 
 	assert.Equal(t, u.UserName, htmlDoc.GetInputValueByName("name"))
 	assert.Equal(t, u.FullName, htmlDoc.GetInputValueByName("full_name"))
-	assert.Equal(t, u.Email, htmlDoc.Find(`label[for="email"]`).Siblings().First().Text())
+	assert.Equal(t, u.Email, htmlDoc.Find("#signed-user-email").Text())
 }
 
 func TestLDAPAuthChange(t *testing.T) {