From 2141bc3298932ddb1652ba2de48c7570e467c665 Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Sun, 17 Mar 2024 20:40:42 +0800
Subject: [PATCH 01/74] Refactor clone-panel styles (#29861)

1. The borders were doubled on the "empty" page, fix it.
2. Remove unnecessary CSS classes like "clone", "compact", etc
3. Use CSS class "clone-panel" instead of ID "clone-panel"
4. Use `tw-flex-1` instead of `gt-f1`
5. Remove unnecessary ID "more-btn"

(cherry picked from commit 673286d8c8a00bf7240a93187d767fb5a5e32a31)
---
 templates/repo/clone_buttons.tmpl |  6 +++---
 templates/repo/empty.tmpl         |  2 +-
 templates/repo/home.tmpl          |  4 ++--
 templates/repo/wiki/revision.tmpl |  2 +-
 templates/repo/wiki/view.tmpl     |  2 +-
 web_src/css/repo.css              | 32 +++++++++----------------------
 web_src/css/repo/wiki.css         |  2 +-
 7 files changed, 18 insertions(+), 32 deletions(-)

diff --git a/templates/repo/clone_buttons.tmpl b/templates/repo/clone_buttons.tmpl
index a664c4bda8..89daba9dc9 100644
--- a/templates/repo/clone_buttons.tmpl
+++ b/templates/repo/clone_buttons.tmpl
@@ -1,15 +1,15 @@
 <!-- there is always at least one button (by context/repo.go) -->
 {{if $.CloneButtonShowHTTPS}}
-	<button class="ui small compact clone button" id="repo-clone-https" data-link="{{$.CloneButtonOriginLink.HTTPS}}">
+	<button class="ui small button" id="repo-clone-https" data-link="{{$.CloneButtonOriginLink.HTTPS}}">
 		HTTPS
 	</button>
 {{end}}
 {{if $.CloneButtonShowSSH}}
-	<button class="ui small compact clone button" id="repo-clone-ssh" data-link="{{$.CloneButtonOriginLink.SSH}}">
+	<button class="ui small button" id="repo-clone-ssh" data-link="{{$.CloneButtonOriginLink.SSH}}">
 		SSH
 	</button>
 {{end}}
 <input id="repo-clone-url" size="20" class="js-clone-url" value="{{$.CloneButtonOriginLink.HTTPS}}" readonly>
-<button class="ui basic small compact icon button" id="clipboard-btn" data-tooltip-content="{{ctx.Locale.Tr "copy_url"}}" data-clipboard-target="#repo-clone-url" aria-label="{{ctx.Locale.Tr "copy_url"}}">
+<button class="ui small icon button" id="clipboard-btn" data-tooltip-content="{{ctx.Locale.Tr "copy_url"}}" data-clipboard-target="#repo-clone-url" aria-label="{{ctx.Locale.Tr "copy_url"}}">
 	{{svg "octicon-copy" 14}}
 </button>
diff --git a/templates/repo/empty.tmpl b/templates/repo/empty.tmpl
index a858a728e9..d3665a9f8b 100644
--- a/templates/repo/empty.tmpl
+++ b/templates/repo/empty.tmpl
@@ -37,7 +37,7 @@
 									</a>
 									{{end}}
 								{{end}}
-								<div class="ui action small input gt-df gt-f1">
+								<div class="clone-panel ui action small input tw-flex-1">
 									{{template "repo/clone_buttons" .}}
 								</div>
 							</div>
diff --git a/templates/repo/home.tmpl b/templates/repo/home.tmpl
index 6b4b227628..2c037fd3d9 100644
--- a/templates/repo/home.tmpl
+++ b/templates/repo/home.tmpl
@@ -132,9 +132,9 @@
 			<div class="gt-df gt-ac">
 				<!-- Only show clone panel in repository home page -->
 				{{if eq $n 0}}
-					<div class="ui action tiny input" id="clone-panel">
+					<div class="clone-panel ui action tiny input">
 						{{template "repo/clone_buttons" .}}
-						<button id="more-btn" class="ui basic small compact jump dropdown icon button" data-tooltip-content="{{ctx.Locale.Tr "repo.more_operations"}}">
+						<button class="ui small jump dropdown icon button" data-tooltip-content="{{ctx.Locale.Tr "repo.more_operations"}}">
 							{{svg "octicon-kebab-horizontal"}}
 							<div class="menu">
 								{{if not $.DisableDownloadSourceArchives}}
diff --git a/templates/repo/wiki/revision.tmpl b/templates/repo/wiki/revision.tmpl
index 647c331d55..182635e011 100644
--- a/templates/repo/wiki/revision.tmpl
+++ b/templates/repo/wiki/revision.tmpl
@@ -15,7 +15,7 @@
 				</div>
 			</div>
 			<div class="ui eight wide column text right">
-				<div class="ui action small input" id="clone-panel">
+				<div class="clone-panel ui action small input">
 					{{template "repo/clone_buttons" .}}
 					{{template "repo/clone_script" .}}
 				</div>
diff --git a/templates/repo/wiki/view.tmpl b/templates/repo/wiki/view.tmpl
index 541b1e9b42..ba94adeab7 100644
--- a/templates/repo/wiki/view.tmpl
+++ b/templates/repo/wiki/view.tmpl
@@ -28,7 +28,7 @@
 					</div>
 				</div>
 			</div>
-			<div class="ui action small input gt-df gt-ac" id="clone-panel">
+			<div class="clone-panel ui action small input">
 				{{template "repo/clone_buttons" .}}
 				{{template "repo/clone_script" .}}
 			</div>
diff --git a/web_src/css/repo.css b/web_src/css/repo.css
index 0eba77dbfe..128748621e 100644
--- a/web_src/css/repo.css
+++ b/web_src/css/repo.css
@@ -143,41 +143,31 @@
   margin-bottom: 12px;
 }
 
-.repository #clone-panel #repo-clone-url {
+.repository .clone-panel #repo-clone-url {
   width: 320px;
   border-radius: 0;
 }
 
-@media (min-width: 768px) and (max-width: 991.98px) {
-  .repository #clone-panel #repo-clone-url {
+@media (max-width: 991.98px) {
+  .repository .clone-panel #repo-clone-url {
     width: 200px;
   }
 }
 
-@media (max-width: 767.98px) {
-  .repository #clone-panel #repo-clone-url {
-    width: 200px;
-  }
+.repository .ui.action.input.clone-panel > button + button,
+.repository .ui.action.input.clone-panel > button + input {
+  margin-left: -1px; /* make the borders overlap to avoid double borders */
 }
 
-.repository #clone-panel #repo-clone-https,
-.repository #clone-panel #repo-clone-ssh {
-  border-right: none;
-}
-
-.repository #clone-panel #more-btn {
-  border-left: none;
-}
-
-.repository #clone-panel button:first-of-type {
+.repository .clone-panel > button:first-of-type {
   border-radius: var(--border-radius) 0 0 var(--border-radius) !important;
 }
 
-.repository #clone-panel button:last-of-type {
+.repository .clone-panel > button:last-of-type {
   border-radius: 0 var(--border-radius) var(--border-radius) 0 !important;
 }
 
-.repository #clone-panel .dropdown .menu {
+.repository .clone-panel .dropdown .menu {
   right: 0 !important;
   left: auto !important;
 }
@@ -1758,10 +1748,6 @@
   font-weight: var(--font-weight-normal);
 }
 
-.repository.quickstart .guide .clone.button:first-child {
-  border-radius: var(--border-radius) 0 0 var(--border-radius);
-}
-
 .repository.quickstart .guide #repo-clone-url {
   border-radius: 0;
   padding: 5px 10px;
diff --git a/web_src/css/repo/wiki.css b/web_src/css/repo/wiki.css
index 1302e9cb5c..bb6f364557 100644
--- a/web_src/css/repo/wiki.css
+++ b/web_src/css/repo/wiki.css
@@ -58,7 +58,7 @@
 }
 
 @media (max-width: 767.98px) {
-  .repository.wiki #clone-panel #repo-clone-url {
+  .repository.wiki .clone-panel #repo-clone-url {
     width: 160px;
   }
   .repository.wiki .wiki-content-main.with-sidebar,

From 003d860aac403e0e1d95ebaecadb751926bb7827 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Sun, 17 Mar 2024 13:50:32 +0100
Subject: [PATCH 02/74] Avoid JS error on issue/pr list when logged out
 (#29854)

When logged out, the checkboxes are not there on the issue/pr lists,
which would cause an error here.

Fixes: https://github.com/go-gitea/gitea/issues/29862

---------

Co-authored-by: delvh <dev.lh@web.de>
(cherry picked from commit 33973ac567d6681bda26d82f26b7294a297c693f)
---
 web_src/js/features/repo-issue-list.js | 1 +
 1 file changed, 1 insertion(+)

diff --git a/web_src/js/features/repo-issue-list.js b/web_src/js/features/repo-issue-list.js
index 48b1555c89..880ecf9489 100644
--- a/web_src/js/features/repo-issue-list.js
+++ b/web_src/js/features/repo-issue-list.js
@@ -9,6 +9,7 @@ import {DELETE, POST} from '../modules/fetch.js';
 
 function initRepoIssueListCheckboxes() {
   const issueSelectAll = document.querySelector('.issue-checkbox-all');
+  if (!issueSelectAll) return; // logged out state
   const issueCheckboxes = document.querySelectorAll('.issue-checkbox');
 
   const syncIssueSelectionState = () => {

From 32380ffa579b1b2be619d3a0e8e521947900a645 Mon Sep 17 00:00:00 2001
From: Nanguan Lin <nanguanlin6@gmail.com>
Date: Sun, 17 Mar 2024 21:24:45 +0800
Subject: [PATCH 03/74] Fix user id column case (#29863)

Sometimes the column name is case-sensitive and it may cause 500.

(cherry picked from commit df05c558da704f0c9c9f11d32bba2a9c1cb2f8a8)
---
 models/user/email_address.go | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/models/user/email_address.go b/models/user/email_address.go
index f2ee5e61b2..45bcc54aa3 100644
--- a/models/user/email_address.go
+++ b/models/user/email_address.go
@@ -443,7 +443,7 @@ func SearchEmails(ctx context.Context, opts *SearchEmailOptions) ([]*SearchEmail
 		cond = cond.And(builder.Eq{"email_address.is_activated": opts.IsActivated.Value()})
 	}
 
-	count, err := db.GetEngine(ctx).Join("INNER", "`user`", "`user`.ID = email_address.uid").
+	count, err := db.GetEngine(ctx).Join("INNER", "`user`", "`user`.id = email_address.uid").
 		Where(cond).Count(new(EmailAddress))
 	if err != nil {
 		return nil, 0, fmt.Errorf("Count: %w", err)
@@ -459,7 +459,7 @@ func SearchEmails(ctx context.Context, opts *SearchEmailOptions) ([]*SearchEmail
 	emails := make([]*SearchEmailResult, 0, opts.PageSize)
 	err = db.GetEngine(ctx).Table("email_address").
 		Select("email_address.*, `user`.name, `user`.full_name").
-		Join("INNER", "`user`", "`user`.ID = email_address.uid").
+		Join("INNER", "`user`", "`user`.id = email_address.uid").
 		Where(cond).
 		OrderBy(orderby).
 		Limit(opts.PageSize, (opts.Page-1)*opts.PageSize).

From 20f839ad00ebd119566f95b42c3e0640bbfcb2b0 Mon Sep 17 00:00:00 2001
From: Denys Konovalov <kontakt@denyskon.de>
Date: Sun, 17 Mar 2024 15:11:28 +0100
Subject: [PATCH 04/74] fix telegram webhook (#29864)

Fix #29837 which is a regression caused by

https://github.com/go-gitea/gitea/pull/29145/files#diff-731445ee00f0f1bf2ff731f4f96ddcf51cdc53fd2faaf406eb3536fc292ea748L48.

The line was probably removed by accident.

---------

Co-authored-by: Giteabot <teabot@gitea.io>
(cherry picked from commit 0285b04f4ca981d7467097dbca3b281011b7798c)
---
 services/webhook/telegram.go      | 4 +++-
 services/webhook/telegram_test.go | 9 +++++++++
 2 files changed, 12 insertions(+), 1 deletion(-)

diff --git a/services/webhook/telegram.go b/services/webhook/telegram.go
index e4a5b5a424..c2b4820032 100644
--- a/services/webhook/telegram.go
+++ b/services/webhook/telegram.go
@@ -181,7 +181,9 @@ func (t telegramConvertor) Package(p *api.PackagePayload) (TelegramPayload, erro
 
 func createTelegramPayload(message string) TelegramPayload {
 	return TelegramPayload{
-		Message: strings.TrimSpace(message),
+		Message:           strings.TrimSpace(message),
+		ParseMode:         "HTML",
+		DisableWebPreview: true,
 	}
 }
 
diff --git a/services/webhook/telegram_test.go b/services/webhook/telegram_test.go
index 27ab96cd09..2fe5161b22 100644
--- a/services/webhook/telegram_test.go
+++ b/services/webhook/telegram_test.go
@@ -18,6 +18,15 @@ import (
 
 func TestTelegramPayload(t *testing.T) {
 	tc := telegramConvertor{}
+
+	t.Run("Correct webhook params", func(t *testing.T) {
+		p := createTelegramPayload("testMsg ")
+
+		assert.Equal(t, "HTML", p.ParseMode)
+		assert.Equal(t, true, p.DisableWebPreview)
+		assert.Equal(t, "testMsg", p.Message)
+	})
+
 	t.Run("Create", func(t *testing.T) {
 		p := createTestPayload()
 

From a2f4ab60e8a9bd92fa6c81da3b97c00ca0a5adc6 Mon Sep 17 00:00:00 2001
From: Nanguan Lin <nanguanlin6@gmail.com>
Date: Mon, 18 Mar 2024 04:28:11 +0800
Subject: [PATCH 05/74] Fix missing code in the user profile (#29865)

fix #29820
deleted by
https://github.com/go-gitea/gitea/pull/29248/files#diff-2b0b591787f16325539485e648a09ab6d3177f47dc129cfe84a35ffe141dfd19L39-L62,
which causing malfunction of follow/unfollow and missing description in
the user profile page.

(cherry picked from commit 5ca65d33906ebbca1e502536ffef18942b541c1d)

Conflicts:
	routers/web/shared/user/header.go
	trivial context conflict because of user blocking code
---
 routers/web/shared/user/header.go | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/routers/web/shared/user/header.go b/routers/web/shared/user/header.go
index f007772d6c..7d0b34cb7d 100644
--- a/routers/web/shared/user/header.go
+++ b/routers/web/shared/user/header.go
@@ -16,6 +16,8 @@ import (
 	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/gitrepo"
 	"code.gitea.io/gitea/modules/log"
+	"code.gitea.io/gitea/modules/markup"
+	"code.gitea.io/gitea/modules/markup/markdown"
 	"code.gitea.io/gitea/modules/optional"
 	"code.gitea.io/gitea/modules/setting"
 	"code.gitea.io/gitea/services/context"
@@ -35,6 +37,7 @@ func PrepareContextForProfileBigAvatar(ctx *context.Context) {
 	prepareContextForCommonProfile(ctx)
 
 	ctx.Data["IsBlocked"] = ctx.Doer != nil && user_model.IsBlocked(ctx, ctx.Doer.ID, ctx.ContextUser.ID)
+	ctx.Data["IsFollowing"] = ctx.Doer != nil && user_model.IsFollowing(ctx, ctx.Doer.ID, ctx.ContextUser.ID)
 	ctx.Data["ShowUserEmail"] = setting.UI.ShowUserEmail && ctx.ContextUser.Email != "" && ctx.IsSigned && !ctx.ContextUser.KeepEmailPrivate
 	if setting.Service.UserLocationMapURL != "" {
 		ctx.Data["ContextUserLocationMapURL"] = setting.Service.UserLocationMapURL + url.QueryEscape(ctx.ContextUser.Location)
@@ -46,6 +49,17 @@ func PrepareContextForProfileBigAvatar(ctx *context.Context) {
 		return
 	}
 	ctx.Data["OpenIDs"] = openIDs
+	if len(ctx.ContextUser.Description) != 0 {
+		content, err := markdown.RenderString(&markup.RenderContext{
+			Metas: map[string]string{"mode": "document"},
+			Ctx:   ctx,
+		}, ctx.ContextUser.Description)
+		if err != nil {
+			ctx.ServerError("RenderString", err)
+			return
+		}
+		ctx.Data["RenderedDescription"] = content
+	}
 
 	showPrivate := ctx.IsSigned && (ctx.Doer.IsAdmin || ctx.Doer.ID == ctx.ContextUser.ID)
 	orgs, err := db.Find[organization.Organization](ctx, organization.FindOrgOptions{

From 6eb576c36d84354c2047232c2682753f333b7794 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Sun, 17 Mar 2024 23:12:36 +0100
Subject: [PATCH 06/74] move some scripts from 'build' to 'tools' directory,
 misc refactors (#29844)

- Move some scripts from `build` to new `tools` dir. Eventually i would
like to move all but let's do it step-by-step.
- Add dir to eslint and move the files into vars.
- Update docs accordingly.
- While updating docs I noticed we were incorrectly having `public/img`
path still in a few places. Replace those with the current
`public/assets/img`.

---------

Co-authored-by: Nanguan Lin <nanguanlin6@gmail.com>
Co-authored-by: Giteabot <teabot@gitea.io>
(cherry picked from commit 095fdd691dd1a7d7748372cc73e7708278c80933)
---
 .dockerignore                                    |  2 +-
 .eslintrc.yaml                                   |  4 ----
 .gitignore                                       |  2 +-
 Makefile                                         | 16 +++++++++-------
 .../content/administration/cmd-embedded.zh-cn.md |  6 +++---
 .../development/hacking-on-gitea.en-us.md        |  2 +-
 .../development/hacking-on-gitea.zh-cn.md        |  2 +-
 {build => tools}/generate-images.js              |  4 ++--
 {build => tools}/generate-svg.js                 |  0
 {build => tools}/watch.sh                        |  0
 10 files changed, 18 insertions(+), 20 deletions(-)
 rename {build => tools}/generate-images.js (95%)
 rename {build => tools}/generate-svg.js (100%)
 rename {build => tools}/watch.sh (100%)

diff --git a/.dockerignore b/.dockerignore
index d1a08977a5..98ef522331 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -62,7 +62,6 @@ cpu.out
 /data
 /indexers
 /log
-/public/img/avatar
 /tests/integration/gitea-integration-*
 /tests/integration/indexers-*
 /tests/e2e/gitea-e2e-*
@@ -77,6 +76,7 @@ cpu.out
 /public/assets/js
 /public/assets/css
 /public/assets/fonts
+/public/assets/img/avatar
 /public/assets/img/webpack
 /vendor
 /web_src/fomantic/node_modules
diff --git a/.eslintrc.yaml b/.eslintrc.yaml
index b62b13cefe..b65fe56cf2 100644
--- a/.eslintrc.yaml
+++ b/.eslintrc.yaml
@@ -42,10 +42,6 @@ overrides:
       worker: true
     rules:
       no-restricted-globals: [2, addEventListener, blur, close, closed, confirm, defaultStatus, defaultstatus, error, event, external, find, focus, frameElement, frames, history, innerHeight, innerWidth, isFinite, isNaN, length, locationbar, menubar, moveBy, moveTo, name, onblur, onerror, onfocus, onload, onresize, onunload, open, opener, opera, outerHeight, outerWidth, pageXOffset, pageYOffset, parent, print, removeEventListener, resizeBy, resizeTo, screen, screenLeft, screenTop, screenX, screenY, scroll, scrollbars, scrollBy, scrollTo, scrollX, scrollY, status, statusbar, stop, toolbar, top]
-  - files: ["build/generate-images.js"]
-    rules:
-      i/no-unresolved: [0]
-      i/no-extraneous-dependencies: [0]
   - files: ["*.config.*"]
     rules:
       i/no-unused-modules: [0]
diff --git a/.gitignore b/.gitignore
index 34c71b6973..b883e079d1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -64,7 +64,7 @@ cpu.out
 /data
 /indexers
 /log
-/public/img/avatar
+/public/assets/img/avatar
 /tests/integration/gitea-integration-*
 /tests/integration/indexers-*
 /tests/e2e/gitea-e2e-*
diff --git a/Makefile b/Makefile
index 6735ffd1a5..4cf8837f72 100644
--- a/Makefile
+++ b/Makefile
@@ -148,6 +148,8 @@ TAR_EXCLUDES := .git data indexers queues log node_modules $(EXECUTABLE) $(FOMAN
 GO_DIRS := build cmd models modules routers services tests
 WEB_DIRS := web_src/js web_src/css
 
+ESLINT_FILES := web_src/js tools *.config.js tests/e2e
+STYLELINT_FILES := web_src/css web_src/js/components/*.vue
 SPELLCHECK_FILES := $(GO_DIRS) $(WEB_DIRS) docs/content templates options/locale/locale_en-US.ini .github
 
 GO_SOURCES := $(wildcard *.go)
@@ -396,19 +398,19 @@ lint-backend-fix: lint-go-fix lint-go-vet lint-editorconfig
 
 .PHONY: lint-js
 lint-js: node_modules
-	npx eslint --color --max-warnings=0 --ext js,vue web_src/js build *.config.js tests/e2e
+	npx eslint --color --max-warnings=0 --ext js,vue $(ESLINT_FILES)
 
 .PHONY: lint-js-fix
 lint-js-fix: node_modules
-	npx eslint --color --max-warnings=0 --ext js,vue web_src/js build *.config.js tests/e2e --fix
+	npx eslint --color --max-warnings=0 --ext js,vue $(ESLINT_FILES) --fix
 
 .PHONY: lint-css
 lint-css: node_modules
-	npx stylelint --color --max-warnings=0 web_src/css web_src/js/components/*.vue
+	npx stylelint --color --max-warnings=0 $(STYLELINT_FILES)
 
 .PHONY: lint-css-fix
 lint-css-fix: node_modules
-	npx stylelint --color --max-warnings=0 web_src/css web_src/js/components/*.vue --fix
+	npx stylelint --color --max-warnings=0 $(STYLELINT_FILES) --fix
 
 .PHONY: lint-swagger
 lint-swagger: node_modules
@@ -468,7 +470,7 @@ lint-yaml: .venv
 
 .PHONY: watch
 watch:
-	@bash build/watch.sh
+	@bash tools/watch.sh
 
 .PHONY: watch-frontend
 watch-frontend: node-check node_modules
@@ -962,7 +964,7 @@ $(WEBPACK_DEST): $(WEBPACK_SOURCES) $(WEBPACK_CONFIGS) package-lock.json
 .PHONY: svg
 svg: node-check | node_modules
 	rm -rf $(SVG_DEST_DIR)
-	node build/generate-svg.js
+	node tools/generate-svg.js
 
 .PHONY: svg-check
 svg-check: svg
@@ -997,7 +999,7 @@ generate-gitignore:
 .PHONY: generate-images
 generate-images: | node_modules
 	npm install --no-save fabric@6.0.0-beta19 imagemin-zopfli@7
-	node build/generate-images.js $(TAGS)
+	node tools/generate-images.js $(TAGS)
 
 .PHONY: generate-manpage
 generate-manpage:
diff --git a/docs/content/administration/cmd-embedded.zh-cn.md b/docs/content/administration/cmd-embedded.zh-cn.md
index 4570bb58a3..a2df1aa2f5 100644
--- a/docs/content/administration/cmd-embedded.zh-cn.md
+++ b/docs/content/administration/cmd-embedded.zh-cn.md
@@ -37,7 +37,7 @@ gitea embedded list [--include-vendored] [patterns...]
 
 - 列出所有模板文件,无论在哪个虚拟目录下:`**.tmpl`
 - 列出所有邮件模板文件:`templates/mail/**.tmpl`
-- 列出 `public/img` 目录下的所有文件:`public/img/**`
+列出 `public/assets/img` 目录下的所有文件:`public/assets/img/**`
 
 不要忘记为模式使用引号,因为空格、`*` 和其他字符可能对命令行解释器有特殊含义。
 
@@ -49,8 +49,8 @@ gitea embedded list [--include-vendored] [patterns...]
 
 ```sh
 $ gitea embedded list '**openid**'
-public/img/auth/openid_connect.svg
-public/img/openid-16x16.png
+public/assets/img/auth/openid_connect.svg
+public/assets/img/openid-16x16.png
 templates/user/auth/finalize_openid.tmpl
 templates/user/auth/signin_openid.tmpl
 templates/user/auth/signup_openid_connect.tmpl
diff --git a/docs/content/development/hacking-on-gitea.en-us.md b/docs/content/development/hacking-on-gitea.en-us.md
index 982dbcf6ea..004e803827 100644
--- a/docs/content/development/hacking-on-gitea.en-us.md
+++ b/docs/content/development/hacking-on-gitea.en-us.md
@@ -214,7 +214,7 @@ REPO_INDEXER_CONN_STR = http://elastic:changeme@localhost:9200
 
 ### Building and adding SVGs
 
-SVG icons are built using the `make svg` target which compiles the icon sources defined in `build/generate-svg.js` into the output directory `public/assets/img/svg`. Custom icons can be added in the `web_src/svg` directory.
+SVG icons are built using the `make svg` target which compiles the icon sources into the output directory `public/assets/img/svg`. Custom icons can be added in the `web_src/svg` directory.
 
 ### Building the Logo
 
diff --git a/docs/content/development/hacking-on-gitea.zh-cn.md b/docs/content/development/hacking-on-gitea.zh-cn.md
index a31e1dc511..7dfea30538 100644
--- a/docs/content/development/hacking-on-gitea.zh-cn.md
+++ b/docs/content/development/hacking-on-gitea.zh-cn.md
@@ -201,7 +201,7 @@ REPO_INDEXER_CONN_STR = http://elastic:changeme@localhost:9200
 
 ### 构建和添加 SVGs
 
-SVG 图标是使用 `make svg` 目标构建的,该目标将 `build/generate-svg.js` 中定义的图标源编译到输出目录 `public/img/svg` 中。可以在 `web_src/svg` 目录中添加自定义图标。
+SVG 图标是使用 `make svg` 命令构建的,该命令将图标资源编译到输出目录 `public/assets/img/svg` 中。可以在 `web_src/svg` 目录中添加自定义图标。
 
 ### 构建 Logo
 
diff --git a/build/generate-images.js b/tools/generate-images.js
similarity index 95%
rename from build/generate-images.js
rename to tools/generate-images.js
index db31d19e2a..cc2855c18e 100755
--- a/build/generate-images.js
+++ b/tools/generate-images.js
@@ -1,7 +1,7 @@
 #!/usr/bin/env node
-import imageminZopfli from 'imagemin-zopfli';
+import imageminZopfli from 'imagemin-zopfli'; // eslint-disable-line i/no-unresolved
+import {loadSVGFromString, Canvas, Rect, util} from 'fabric/node'; // eslint-disable-line i/no-unresolved
 import {optimize} from 'svgo';
-import {loadSVGFromString, Canvas, Rect, util} from 'fabric/node';
 import {readFile, writeFile} from 'node:fs/promises';
 import {argv, exit} from 'node:process';
 
diff --git a/build/generate-svg.js b/tools/generate-svg.js
similarity index 100%
rename from build/generate-svg.js
rename to tools/generate-svg.js
diff --git a/build/watch.sh b/tools/watch.sh
similarity index 100%
rename from build/watch.sh
rename to tools/watch.sh

From 0500d926b203c31f38d519feb0a54025ab7318e7 Mon Sep 17 00:00:00 2001
From: GiteaBot <teabot@gitea.io>
Date: Mon, 18 Mar 2024 00:24:59 +0000
Subject: [PATCH 07/74] [skip ci] Updated licenses and gitignores

(cherry picked from commit a4a766f4a205d507d157f2f17a0e761e00939cb8)
---
 options/license/threeparttable | 3 +++
 1 file changed, 3 insertions(+)
 create mode 100644 options/license/threeparttable

diff --git a/options/license/threeparttable b/options/license/threeparttable
new file mode 100644
index 0000000000..498b728226
--- /dev/null
+++ b/options/license/threeparttable
@@ -0,0 +1,3 @@
+This file may be distributed, modified, and used in other works with just
+one restriction: modified versions must clearly indicate the modification
+(a name change, or a displayed message, or ?).

From 0d3ddec26c1bf779fca60afdfd23fb1a44ad2e44 Mon Sep 17 00:00:00 2001
From: buckybytes <158571971+buckybytes@users.noreply.github.com>
Date: Sun, 17 Mar 2024 21:23:08 -0500
Subject: [PATCH 08/74] Editor error message misleading due to re-used key.
 (#29859)

The error message:

`editor.file_changed_while_editing = The file contents have changed
since you started editing. <a target="_blank" rel="noopener noreferrer"
href="%s">Click here</a> to see them or <strong>Commit Changes
again</strong> to overwrite them.`

Is re-used in inappropriate contexts. The link in the key goes to a 404
when the key is used in a situation where the file contents have not
changed.

Added two new keys to differentiate commit id mismatch and push out of
date conditions.

(cherry picked from commit 16e360099d0a515d429538ec88cff1f3ede23fb4)
---
 options/locale/locale_en-US.ini | 2 ++
 routers/web/repo/editor.go      | 4 ++--
 2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini
index 86975748b5..f18ab87ef6 100644
--- a/options/locale/locale_en-US.ini
+++ b/options/locale/locale_en-US.ini
@@ -1332,6 +1332,8 @@ editor.file_editing_no_longer_exists = The file being edited, "%s", no longer ex
 editor.file_deleting_no_longer_exists = The file being deleted, "%s", no longer exists in this repository.
 editor.file_changed_while_editing = The file contents have changed since you started editing. <a target="_blank" rel="noopener noreferrer" href="%s">Click here</a> to see them or <strong>Commit Changes again</strong> to overwrite them.
 editor.file_already_exists = A file named "%s" already exists in this repository.
+editor.commit_id_not_matching = The Commit ID does not match the ID when you began editing.  Commit into a patch branch and then merge.
+editor.push_out_of_date = The push appears to be out of date.
 editor.commit_empty_file_header = Commit an empty file
 editor.commit_empty_file_text = The file you're about to commit is empty. Proceed?
 editor.no_changes_to_show = There are no changes to show.
diff --git a/routers/web/repo/editor.go b/routers/web/repo/editor.go
index 4f26c0c521..edfae96050 100644
--- a/routers/web/repo/editor.go
+++ b/routers/web/repo/editor.go
@@ -374,9 +374,9 @@ func editFilePost(ctx *context.Context, form forms.EditRepoFileForm, isNewFile b
 				ctx.Error(http.StatusInternalServerError, err.Error())
 			}
 		} else if models.IsErrCommitIDDoesNotMatch(err) {
-			ctx.RenderWithErr(ctx.Tr("repo.editor.file_changed_while_editing", ctx.Repo.RepoLink+"/compare/"+util.PathEscapeSegments(form.LastCommit)+"..."+util.PathEscapeSegments(ctx.Repo.CommitID)), tplEditFile, &form)
+			ctx.RenderWithErr(ctx.Tr("repo.editor.commit_id_not_matching", ctx.Repo.RepoLink+"/compare/"+util.PathEscapeSegments(form.LastCommit)+"..."+util.PathEscapeSegments(ctx.Repo.CommitID)), tplEditFile, &form)
 		} else if git.IsErrPushOutOfDate(err) {
-			ctx.RenderWithErr(ctx.Tr("repo.editor.file_changed_while_editing", ctx.Repo.RepoLink+"/compare/"+util.PathEscapeSegments(form.LastCommit)+"..."+util.PathEscapeSegments(form.NewBranchName)), tplEditFile, &form)
+			ctx.RenderWithErr(ctx.Tr("repo.editor.push_out_of_date", ctx.Repo.RepoLink+"/compare/"+util.PathEscapeSegments(form.LastCommit)+"..."+util.PathEscapeSegments(form.NewBranchName)), tplEditFile, &form)
 		} else if git.IsErrPushRejected(err) {
 			errPushRej := err.(*git.ErrPushRejected)
 			if len(errPushRej.Message) == 0 {

From fbf0b3d661ad81c14546ca25948942f586e2662c Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Mon, 18 Mar 2024 19:05:17 +0800
Subject: [PATCH 09/74] Only do counting when count_only=true for repo
 dashboard (#29884)

Ref: #29878
(cherry picked from commit b251e608c01392c947f84be387f956541bfea25c)
---
 routers/web/repo/repo.go                    | 25 ++++++++++++---------
 web_src/js/components/DashboardRepoList.vue |  2 +-
 2 files changed, 16 insertions(+), 11 deletions(-)

diff --git a/routers/web/repo/repo.go b/routers/web/repo/repo.go
index 07e6c937b0..bda78b0edc 100644
--- a/routers/web/repo/repo.go
+++ b/routers/web/repo/repo.go
@@ -618,26 +618,31 @@ func SearchRepo(ctx *context.Context) {
 		}
 	}
 
-	var err error
+	// To improve performance when only the count is requested
+	if ctx.FormBool("count_only") {
+		if count, err := repo_model.CountRepository(ctx, opts); err != nil {
+			log.Error("CountRepository: %v", err)
+			ctx.JSON(http.StatusInternalServerError, nil) // frontend JS doesn't handle error response (same as below)
+		} else {
+			ctx.SetTotalCountHeader(count)
+			ctx.JSONOK()
+		}
+		return
+	}
+
 	repos, count, err := repo_model.SearchRepository(ctx, opts)
 	if err != nil {
-		ctx.JSON(http.StatusInternalServerError, api.SearchError{
-			OK:    false,
-			Error: err.Error(),
-		})
+		log.Error("SearchRepository: %v", err)
+		ctx.JSON(http.StatusInternalServerError, nil)
 		return
 	}
 
 	ctx.SetTotalCountHeader(count)
 
-	// To improve performance when only the count is requested
-	if ctx.FormBool("count_only") {
-		return
-	}
-
 	latestCommitStatuses, err := commitstatus_service.FindReposLastestCommitStatuses(ctx, repos)
 	if err != nil {
 		log.Error("FindReposLastestCommitStatuses: %v", err)
+		ctx.JSON(http.StatusInternalServerError, nil)
 		return
 	}
 
diff --git a/web_src/js/components/DashboardRepoList.vue b/web_src/js/components/DashboardRepoList.vue
index 825803d939..6c1d031640 100644
--- a/web_src/js/components/DashboardRepoList.vue
+++ b/web_src/js/components/DashboardRepoList.vue
@@ -235,7 +235,7 @@ const sfc = {
         if (!this.reposTotalCount) {
           const totalCountSearchURL = `${this.subUrl}/repo/search?count_only=1&uid=${this.uid}&team_id=${this.teamId}&q=&page=1&mode=`;
           response = await GET(totalCountSearchURL);
-          this.reposTotalCount = response.headers.get('X-Total-Count');
+          this.reposTotalCount = response.headers.get('X-Total-Count') ?? '?';
         }
 
         response = await GET(searchedURL);

From 4711d3311ba493e52786323eebb5ac88c343f955 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Mon, 18 Mar 2024 15:47:05 +0100
Subject: [PATCH 10/74] Migrate border and margin classes to Tailwind (#29828)

Used all existing css vars, other migrations are 1:1.

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
(cherry picked from commit 34290a00c4501ffeba26db267be71ab68e3ec97f)

Conflicts:
	templates/repo/issue/filter_list.tmpl
	web_src/js/components/DashboardRepoList.vue
	trivial context conflicts
---
 tailwind.config.js                           | 11 ++++++++++
 templates/devtest/flex-list.tmpl             |  2 +-
 templates/devtest/gitea-ui.tmpl              |  4 ++--
 templates/org/header.tmpl                    |  2 +-
 templates/repo/issue/filter_list.tmpl        |  4 ++--
 templates/repo/issue/labels/label_list.tmpl  |  2 +-
 templates/repo/pulls/tab_menu.tmpl           |  2 +-
 templates/repo/settings/options.tmpl         |  6 +++---
 templates/status/500.tmpl                    |  3 ++-
 templates/user/dashboard/navbar.tmpl         |  2 +-
 web_src/css/helpers.css                      | 22 --------------------
 web_src/js/components/DashboardRepoList.vue  |  9 ++++----
 web_src/js/components/DiffCommitSelector.vue | 14 ++++++++-----
 web_src/js/components/RepoCodeFrequency.vue  |  2 +-
 web_src/js/components/RepoContributors.vue   |  2 +-
 web_src/js/components/RepoRecentCommits.vue  |  2 +-
 web_src/js/features/repo-diff-commit.js      |  2 +-
 17 files changed, 43 insertions(+), 48 deletions(-)

diff --git a/tailwind.config.js b/tailwind.config.js
index d783268bd7..e2e8f23656 100644
--- a/tailwind.config.js
+++ b/tailwind.config.js
@@ -55,5 +55,16 @@ export default {
       current: 'currentcolor',
       transparent: 'transparent',
     },
+    borderRadius: {
+      'none': '0',
+      'sm': '2px',
+      'DEFAULT': 'var(--border-radius)', // 4px
+      'md': 'var(--border-radius-medium)', // 6px
+      'lg': '8px',
+      'xl': '12px',
+      '2xl': '16px',
+      '3xl': '24px',
+      'full': 'var(--border-radius-circle)', // 50%
+    },
   },
 };
diff --git a/templates/devtest/flex-list.tmpl b/templates/devtest/flex-list.tmpl
index c8584c110b..0c7b27cd84 100644
--- a/templates/devtest/flex-list.tmpl
+++ b/templates/devtest/flex-list.tmpl
@@ -104,7 +104,7 @@
 		</div>
 
 		<h1>If parent provides the padding/margin space:</h1>
-		<div class="gt-border-secondary gt-py-4">
+		<div class="tw-border tw-border-secondary gt-py-4">
 			<div class="flex-list flex-space-fitted">
 				<div class="flex-item">item 1 (no padding top)</div>
 				<div class="flex-item">item 2 (no padding bottom)</div>
diff --git a/templates/devtest/gitea-ui.tmpl b/templates/devtest/gitea-ui.tmpl
index 0b1f982ee4..9ef0aa675a 100644
--- a/templates/devtest/gitea-ui.tmpl
+++ b/templates/devtest/gitea-ui.tmpl
@@ -95,8 +95,8 @@
 
 	<div>
 		<h1>Loading</h1>
-		<div class="is-loading small-loading-icon gt-border-secondary gt-py-2"><span>loading ...</span></div>
-		<div class="is-loading gt-border-secondary gt-py-4">
+		<div class="is-loading small-loading-icon tw-border tw-border-secondary gt-py-2"><span>loading ...</span></div>
+		<div class="is-loading tw-border tw-border-secondary gt-py-4">
 			<p>loading ...</p>
 			<p>loading ...</p>
 			<p>loading ...</p>
diff --git a/templates/org/header.tmpl b/templates/org/header.tmpl
index 943557b1ca..1a55101c2e 100644
--- a/templates/org/header.tmpl
+++ b/templates/org/header.tmpl
@@ -7,7 +7,7 @@
 				{{if .Org.Visibility.IsLimited}}<span class="ui large basic horizontal label">{{ctx.Locale.Tr "org.settings.visibility.limited_shortname"}}</span>{{end}}
 				{{if .Org.Visibility.IsPrivate}}<span class="ui large basic horizontal label">{{ctx.Locale.Tr "org.settings.visibility.private_shortname"}}</span>{{end}}
 			</span>
-			<span class="gt-df gt-ac gt-gap-2 gt-ml-auto gt-font-16 tw-whitespace-nowrap">
+			<span class="gt-df gt-ac gt-gap-2 tw-ml-auto gt-font-16 tw-whitespace-nowrap">
 				{{if .EnableFeed}}
 					<a class="ui basic label button gt-mr-0" href="{{.Org.HomeLink}}.rss" data-tooltip-content="{{ctx.Locale.Tr "rss_feed"}}">
 						{{svg "octicon-rss" 24}}
diff --git a/templates/repo/issue/filter_list.tmpl b/templates/repo/issue/filter_list.tmpl
index f9f635f7cd..c95ce6b2b8 100644
--- a/templates/repo/issue/filter_list.tmpl
+++ b/templates/repo/issue/filter_list.tmpl
@@ -43,7 +43,7 @@
 					{{end}}
 				{{end}}
 				{{RenderLabel $.Context .}}
-				<p class="gt-ml-auto">{{template "repo/issue/labels/label_archived" .}}</p>
+				<p class="tw-ml-auto">{{template "repo/issue/labels/label_archived" .}}</p>
 			</a>
 		{{end}}
 	</div>
@@ -108,7 +108,7 @@
 			</div>
 			{{range .OpenProjects}}
 				<a rel="nofollow" class="{{if $.ProjectID}}{{if eq $.ProjectID .ID}}active selected{{end}}{{end}} item gt-df" href="{{$.Link}}?type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{$.SelectLabels}}&milestone={{$.MilestoneID}}&project={{.ID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">
-					{{svg .IconName 18 "gt-mr-3 gt-shrink-0"}}<span class="gt-ellipsis">{{.Title}}</span>
+					{{svg .IconName 18 "gt-mr-3 tw-shrink-0"}}<span class="gt-ellipsis">{{.Title}}</span>
 				</a>
 			{{end}}
 		{{end}}
diff --git a/templates/repo/issue/labels/label_list.tmpl b/templates/repo/issue/labels/label_list.tmpl
index 9b0061b60e..284c0d3bf6 100644
--- a/templates/repo/issue/labels/label_list.tmpl
+++ b/templates/repo/issue/labels/label_list.tmpl
@@ -44,7 +44,7 @@
 			</div>
 			<div class="label-operation gt-df">
 				{{template "repo/issue/labels/label_archived" .}}
-				<div class="gt-df gt-ml-auto">
+				<div class="gt-df tw-ml-auto">
 					{{if and (not $.PageIsOrgSettingsLabels) (not $.Repository.IsArchived) (or $.CanWriteIssues $.CanWritePulls)}}
 						<a class="edit-label-button" href="#" data-id="{{.ID}}" data-title="{{.Name}}" {{if .Exclusive}}data-exclusive{{end}} {{if gt .ArchivedUnix 0}}data-is-archived{{end}} data-num-issues="{{.NumIssues}}" data-description="{{.Description}}" data-color={{.Color}}>{{svg "octicon-pencil"}} {{ctx.Locale.Tr "repo.issues.label_edit"}}</a>
 						<a class="delete-button" href="#" data-url="{{$.Link}}/delete" data-id="{{.ID}}">{{svg "octicon-trash"}} {{ctx.Locale.Tr "repo.issues.label_delete"}}</a>
diff --git a/templates/repo/pulls/tab_menu.tmpl b/templates/repo/pulls/tab_menu.tmpl
index 10bdfdb3de..340b1bb397 100644
--- a/templates/repo/pulls/tab_menu.tmpl
+++ b/templates/repo/pulls/tab_menu.tmpl
@@ -15,7 +15,7 @@
 			{{ctx.Locale.Tr "repo.pulls.tab_files"}}
 			<span class="ui small label">{{if .NumFiles}}{{.NumFiles}}{{else}}-{{end}}</span>
 		</a>
-		<span class="item gt-ml-auto gt-pr-0 gt-font-bold gt-df gt-ac gt-gap-3">
+		<span class="item tw-ml-auto gt-pr-0 gt-font-bold gt-df gt-ac gt-gap-3">
 			<span><span class="text green">{{if .Diff.TotalAddition}}+{{.Diff.TotalAddition}}{{end}}</span> <span class="text red">{{if .Diff.TotalDeletion}}-{{.Diff.TotalDeletion}}{{end}}</span></span>
 			<span class="diff-stats-bar">
 				<div class="diff-stats-add-bar" style="width: {{Eval 100 "*" .Diff.TotalAddition "/" "(" .Diff.TotalAddition "+" .Diff.TotalDeletion "+" 0.0 ")"}}%"></div>
diff --git a/templates/repo/settings/options.tmpl b/templates/repo/settings/options.tmpl
index 99d75b8a84..a2947701a0 100644
--- a/templates/repo/settings/options.tmpl
+++ b/templates/repo/settings/options.tmpl
@@ -116,13 +116,12 @@
 								<th></th>
 							</tr>
 						</thead>
-						{{end}}
 						{{if $modifyBrokenPullMirror}}
 							{{/* even if a repo is a pull mirror (IsMirror=true), the PullMirror might still be nil if the mirror migration is broken */}}
 							<tbody>
 								<tr>
 									<td colspan="4">
-										<div class="text red gt-py-4 gt-border-secondary-bottom">{{ctx.Locale.Tr "repo.settings.mirror_settings.direction.pull"}}: {{ctx.Locale.Tr "error.occurred"}}</div>
+										<div class="text red gt-py-4">{{ctx.Locale.Tr "repo.settings.mirror_settings.direction.pull"}}: {{ctx.Locale.Tr "error.occurred"}}</div>
 									</td>
 								</tr>
 							</tbody>
@@ -201,8 +200,9 @@
 								</td>
 							</tr>
 						</tbody>
+						{{end}}{{/* end if: $modifyBrokenPullMirror / $isWorkingPullMirror */}}
 					</table>
-					{{end}}{{/* end if: IsMirror */}}
+					{{end}}{{/* end if .Repository.IsMirror */}}
 
 					<table class="ui table">
 						<thead>
diff --git a/templates/status/500.tmpl b/templates/status/500.tmpl
index 98e3044db2..043ca7523b 100644
--- a/templates/status/500.tmpl
+++ b/templates/status/500.tmpl
@@ -16,7 +16,7 @@
 </head>
 <body>
 	<div class="full height">
-		<nav class="ui secondary menu gt-border-secondary-bottom">
+		<nav class="ui secondary menu">
 			<div class="ui container gt-df">
 				<div class="item gt-f1">
 					<a href="{{AppSubUrl}}/" aria-label="{{ctx.Locale.Tr "home"}}">
@@ -28,6 +28,7 @@
 				</div>
 			</div>
 		</nav>
+		<div class="divider gt-my-0"></div>
 		<div role="main" class="page-content status-page-500">
 			<div class="ui container" >
 				<style> .ui.message.flash-message { text-align: left; } </style>
diff --git a/templates/user/dashboard/navbar.tmpl b/templates/user/dashboard/navbar.tmpl
index af07897e2c..3e9442d6fc 100644
--- a/templates/user/dashboard/navbar.tmpl
+++ b/templates/user/dashboard/navbar.tmpl
@@ -78,7 +78,7 @@
 
 	{{if .ContextUser.IsOrganization}}
 		<div class="right menu">
-			<a class="{{if .PageIsNews}}active {{end}}item gt-ml-auto" href="{{.ContextUser.DashboardLink}}{{if .Team}}/{{PathEscape .Team.Name}}{{end}}">
+			<a class="{{if .PageIsNews}}active {{end}}item tw-ml-auto" href="{{.ContextUser.DashboardLink}}{{if .Team}}/{{PathEscape .Team.Name}}{{end}}">
 				{{svg "octicon-rss"}}&nbsp;{{ctx.Locale.Tr "activities"}}
 			</a>
 			{{if not .UnitIssuesGlobalDisabled}}
diff --git a/web_src/css/helpers.css b/web_src/css/helpers.css
index 860722823a..6fc84d743c 100644
--- a/web_src/css/helpers.css
+++ b/web_src/css/helpers.css
@@ -52,18 +52,6 @@ Gitea's private styles use `g-` prefix.
 .gt-font-semibold { font-weight: var(--font-weight-semibold) !important; }
 .gt-font-bold { font-weight: var(--font-weight-bold) !important; }
 
-.gt-rounded { border-radius: var(--border-radius) !important; }
-.gt-rounded-top { border-radius: var(--border-radius) var(--border-radius) 0 0 !important; }
-.gt-rounded-bottom { border-radius: 0 0 var(--border-radius) var(--border-radius) !important; }
-.gt-rounded-left { border-radius: var(--border-radius) 0 0 var(--border-radius) !important; }
-.gt-rounded-right { border-radius: 0 var(--border-radius) var(--border-radius) 0 !important; }
-
-.gt-border-secondary { border: 1px solid var(--color-secondary) !important; }
-.gt-border-secondary-top { border-top: 1px solid var(--color-secondary) !important; }
-.gt-border-secondary-bottom { border-bottom: 1px solid var(--color-secondary) !important; }
-.gt-border-secondary-left { border-left: 1px solid var(--color-secondary) !important; }
-.gt-border-secondary-right { border-right: 1px solid var(--color-secondary) !important; }
-
 .interact-fg { color: inherit !important; }
 .interact-fg:hover { color: var(--color-primary) !important; }
 .interact-fg:active { color: var(--color-primary-active) !important; }
@@ -121,14 +109,6 @@ Gitea's private styles use `g-` prefix.
 .gt-my-4 { margin-top: 1rem !important; margin-bottom: 1rem !important; }
 .gt-my-5 { margin-top: 2rem !important; margin-bottom: 2rem !important; }
 
-.gt-m-auto  { margin: auto !important; }
-.gt-mx-auto { margin-left: auto !important; margin-right: auto !important; }
-.gt-my-auto { margin-top: auto !important; margin-bottom: auto !important; }
-.gt-mt-auto { margin-top: auto !important; }
-.gt-mr-auto { margin-right: auto !important; }
-.gt-mb-auto { margin-bottom: auto !important; }
-.gt-ml-auto { margin-left: auto !important; }
-
 .gt-p-0 { padding: 0 !important; }
 .gt-p-1 { padding: .125rem !important; }
 .gt-p-2 { padding: .25rem !important; }
@@ -199,8 +179,6 @@ Gitea's private styles use `g-` prefix.
 .gt-gap-y-4 { row-gap: 1rem !important; }
 .gt-gap-y-5 { row-gap: 2rem !important; }
 
-.gt-shrink-0 { flex-shrink: 0 !important; }
-
 .gt-font-12 { font-size: 12px !important }
 .gt-font-13 { font-size: 13px !important }
 .gt-font-14 { font-size: 14px !important }
diff --git a/web_src/js/components/DashboardRepoList.vue b/web_src/js/components/DashboardRepoList.vue
index 6c1d031640..3b3402f458 100644
--- a/web_src/js/components/DashboardRepoList.vue
+++ b/web_src/js/components/DashboardRepoList.vue
@@ -405,7 +405,7 @@ export default sfc; // activate the IDE's Vue plugin
           </div>
         </overflow-menu>
       </div>
-      <div v-if="repos.length" class="ui attached table segment gt-rounded-bottom">
+      <div v-if="repos.length" class="ui attached table segment tw-rounded-b">
         <ul class="repo-owner-name-list">
           <li class="gt-df gt-ac gt-py-3" v-for="repo, index in repos" :class="{'active': index === activeIndex}" :key="repo.id">
             <a class="repo-list-link muted" :href="repo.link">
@@ -421,8 +421,9 @@ export default sfc; // activate the IDE's Vue plugin
             </a>
           </li>
         </ul>
-        <div v-if="showMoreReposLink" class="center gt-py-3 gt-border-secondary-top">
-          <div class="ui borderless pagination menu narrow">
+        <div v-if="showMoreReposLink" class="tw-text-center">
+          <div class="divider gt-my-0"/>
+          <div class="ui borderless pagination menu narrow gt-my-3">
             <a
               class="item navigation gt-py-2" :class="{'disabled': page === 1}"
               @click="changePage(1)" :title="textFirstPage"
@@ -453,7 +454,7 @@ export default sfc; // activate the IDE's Vue plugin
       </div>
     </div>
     <div v-if="!isOrganization" v-show="tab === 'organizations'" class="ui tab active list dashboard-orgs">
-      <div v-if="organizations.length" class="ui attached table segment gt-rounded">
+      <div v-if="organizations.length" class="ui attached table segment tw-rounded-b">
         <ul class="repo-owner-name-list">
           <li class="gt-df gt-ac gt-py-3" v-for="org in organizations" :key="org.name">
             <a class="repo-list-link muted" :href="subUrl + '/' + encodeURIComponent(org.name)">
diff --git a/web_src/js/components/DiffCommitSelector.vue b/web_src/js/components/DiffCommitSelector.vue
index 780ba22f0c..b465127aca 100644
--- a/web_src/js/components/DiffCommitSelector.vue
+++ b/web_src/js/components/DiffCommitSelector.vue
@@ -204,7 +204,7 @@ export default {
     </button>
     <div class="menu left transition" id="diff-commit-selector-menu" :class="{visible: menuVisible}" v-show="menuVisible" v-cloak :aria-expanded="menuVisible ? 'true': 'false'">
       <div class="loading-indicator is-loading" v-if="isLoading"/>
-      <div v-if="!isLoading" class="vertical item gt-df gt-fc gt-gap-2" id="diff-commit-list-show-all" role="menuitem" @keydown.enter="showAllChanges()" @click="showAllChanges()">
+      <div v-if="!isLoading" class="vertical item" id="diff-commit-list-show-all" role="menuitem" @keydown.enter="showAllChanges()" @click="showAllChanges()">
         <div class="gt-ellipsis">
           {{ locale.show_all_commits }}
         </div>
@@ -215,7 +215,7 @@ export default {
       <!-- only show the show changes since last review if there is a review AND we are commits ahead of the last review -->
       <div
         v-if="lastReviewCommitSha != null" role="menuitem"
-        class="vertical item gt-df gt-fc gt-gap-2 gt-border-secondary-top"
+        class="vertical item"
         :class="{disabled: commitsSinceLastReview === 0}"
         @keydown.enter="changesSinceLastReviewClick()"
         @click="changesSinceLastReviewClick()"
@@ -227,10 +227,10 @@ export default {
           {{ commitsSinceLastReview }} commits
         </div>
       </div>
-      <span v-if="!isLoading" class="info gt-border-secondary-top text light-2">{{ locale.select_commit_hold_shift_for_range }}</span>
+      <span v-if="!isLoading" class="info text light-2">{{ locale.select_commit_hold_shift_for_range }}</span>
       <template v-for="commit in commits" :key="commit.id">
         <div
-          class="vertical item gt-df gt-gap-2 gt-border-secondary-top" role="menuitem"
+          class="vertical item" role="menuitem"
           :class="{selection: commit.selected, hovered: commit.hovered}"
           @keydown.enter.exact="commitClicked(commit.id)"
           @keydown.enter.shift.exact="commitClickedShift(commit)"
@@ -285,10 +285,14 @@ export default {
     width: 350px;
   }
 
-  #diff-commit-selector-menu .item {
+  #diff-commit-selector-menu .item,
+  #diff-commit-selector-menu .info {
+    display: flex !important;
     flex-direction: row;
     line-height: 1.4;
     padding: 7px 14px !important;
+    border-top: 1px solid var(--color-secondary) !important;
+    gap: 0.25em;
   }
 
   #diff-commit-selector-menu .item:focus {
diff --git a/web_src/js/components/RepoCodeFrequency.vue b/web_src/js/components/RepoCodeFrequency.vue
index ad607a041a..c55bbff9cd 100644
--- a/web_src/js/components/RepoCodeFrequency.vue
+++ b/web_src/js/components/RepoCodeFrequency.vue
@@ -148,7 +148,7 @@ export default {
       {{ isLoading ? locale.loadingTitle : errorText ? locale.loadingTitleFailed: `Code frequency over the history of ${repoLink.slice(1)}` }}
     </div>
     <div class="gt-df ui segment main-graph">
-      <div v-if="isLoading || errorText !== ''" class="gt-tc gt-m-auto">
+      <div v-if="isLoading || errorText !== ''" class="gt-tc tw-m-auto">
         <div v-if="isLoading">
           <SvgIcon name="octicon-sync" class="gt-mr-3 job-status-rotate"/>
           {{ locale.loadingInfo }}
diff --git a/web_src/js/components/RepoContributors.vue b/web_src/js/components/RepoContributors.vue
index 22c247ae32..6093c762cb 100644
--- a/web_src/js/components/RepoContributors.vue
+++ b/web_src/js/components/RepoContributors.vue
@@ -353,7 +353,7 @@ export default {
       </div>
     </div>
     <div class="gt-df ui segment main-graph">
-      <div v-if="isLoading || errorText !== ''" class="gt-tc gt-m-auto">
+      <div v-if="isLoading || errorText !== ''" class="gt-tc tw-m-auto">
         <div v-if="isLoading">
           <SvgIcon name="octicon-sync" class="gt-mr-3 job-status-rotate"/>
           {{ locale.loadingInfo }}
diff --git a/web_src/js/components/RepoRecentCommits.vue b/web_src/js/components/RepoRecentCommits.vue
index 77697cd413..c1fd40f506 100644
--- a/web_src/js/components/RepoRecentCommits.vue
+++ b/web_src/js/components/RepoRecentCommits.vue
@@ -125,7 +125,7 @@ export default {
       {{ isLoading ? locale.loadingTitle : errorText ? locale.loadingTitleFailed: "Number of commits in the past year" }}
     </div>
     <div class="gt-df ui segment main-graph">
-      <div v-if="isLoading || errorText !== ''" class="gt-tc gt-m-auto">
+      <div v-if="isLoading || errorText !== ''" class="gt-tc tw-m-auto">
         <div v-if="isLoading">
           <SvgIcon name="octicon-sync" class="gt-mr-3 job-status-rotate"/>
           {{ locale.loadingInfo }}
diff --git a/web_src/js/features/repo-diff-commit.js b/web_src/js/features/repo-diff-commit.js
index 3d4f0f677a..f0466f9320 100644
--- a/web_src/js/features/repo-diff-commit.js
+++ b/web_src/js/features/repo-diff-commit.js
@@ -39,7 +39,7 @@ function addLink(parent, href, text, tooltip) {
   link.href = href;
   link.textContent = text;
   if (tooltip) {
-    link.classList.add('gt-border-secondary', 'gt-rounded');
+    link.classList.add('tw-border', 'tw-border-secondary', 'tw-rounded');
     link.setAttribute('data-tooltip-content', tooltip);
   }
   parent.append(link);

From 40c9fa43cda52c868c5986ec1b0c32b78f551d5d Mon Sep 17 00:00:00 2001
From: Nanguan Lin <nanguanlin6@gmail.com>
Date: Tue, 19 Mar 2024 05:14:51 +0800
Subject: [PATCH 11/74] Remove unused error in graceful manager (#29871)

As title.

(cherry picked from commit 1f0d31ce8fdfc8c32f84e4e0801c2d04b727bbd8)
---
 modules/graceful/manager_unix.go |  4 ++--
 modules/graceful/net_unix.go     | 12 ++----------
 2 files changed, 4 insertions(+), 12 deletions(-)

diff --git a/modules/graceful/manager_unix.go b/modules/graceful/manager_unix.go
index f49c42650c..d03fff9b5b 100644
--- a/modules/graceful/manager_unix.go
+++ b/modules/graceful/manager_unix.go
@@ -59,8 +59,8 @@ func (g *Manager) start() {
 	go func() {
 		defer func() {
 			close(startupDone)
-			// Close the unused listeners and ignore the error here there's not much we can do with it, they're logged in the CloseProvidedListeners function
-			_ = CloseProvidedListeners()
+			// Close the unused listeners
+			closeProvidedListeners()
 		}()
 		// Wait for all servers to be created
 		g.createServerCond.L.Lock()
diff --git a/modules/graceful/net_unix.go b/modules/graceful/net_unix.go
index 4f8c036a69..796e00507c 100644
--- a/modules/graceful/net_unix.go
+++ b/modules/graceful/net_unix.go
@@ -129,25 +129,17 @@ func getProvidedFDs() (savedErr error) {
 	return savedErr
 }
 
-// CloseProvidedListeners closes all unused provided listeners.
-func CloseProvidedListeners() error {
+// closeProvidedListeners closes all unused provided listeners.
+func closeProvidedListeners() {
 	mutex.Lock()
 	defer mutex.Unlock()
-	var returnableError error
 	for _, l := range providedListeners {
 		err := l.Close()
 		if err != nil {
 			log.Error("Error in closing unused provided listener: %v", err)
-			if returnableError != nil {
-				returnableError = fmt.Errorf("%v & %w", returnableError, err)
-			} else {
-				returnableError = err
-			}
 		}
 	}
 	providedListeners = []net.Listener{}
-
-	return returnableError
 }
 
 // DefaultGetListener obtains a listener for the stream-oriented local network address:

From 664052fb0b603f4d57ecde5e5740c9137f89005c Mon Sep 17 00:00:00 2001
From: coldWater <forsaken628@gmail.com>
Date: Tue, 19 Mar 2024 10:20:36 +0800
Subject: [PATCH 12/74] Fix missing error check of bufio.Scanner (#29882)

maybe more

(cherry picked from commit 0e183d81fc5283f9d2047472de580e4f04a046c1)
---
 models/asymkey/ssh_key_authorized_keys.go       | 4 ++++
 models/asymkey/ssh_key_authorized_principals.go | 4 ++++
 modules/git/commit.go                           | 5 +++++
 modules/git/repo_stats.go                       | 4 ++++
 modules/markup/csv/csv.go                       | 5 +++++
 routers/web/repo/compare.go                     | 4 ++++
 services/doctor/authorizedkeys.go               | 4 ++++
 7 files changed, 30 insertions(+)

diff --git a/models/asymkey/ssh_key_authorized_keys.go b/models/asymkey/ssh_key_authorized_keys.go
index 2b15450c98..8c3e0aa81d 100644
--- a/models/asymkey/ssh_key_authorized_keys.go
+++ b/models/asymkey/ssh_key_authorized_keys.go
@@ -211,6 +211,10 @@ func RegeneratePublicKeys(ctx context.Context, t io.StringWriter) error {
 				return err
 			}
 		}
+		err = scanner.Err()
+		if err != nil {
+			return fmt.Errorf("scan: %w", err)
+		}
 		f.Close()
 	}
 	return nil
diff --git a/models/asymkey/ssh_key_authorized_principals.go b/models/asymkey/ssh_key_authorized_principals.go
index f3017c3089..9e6cc3685b 100644
--- a/models/asymkey/ssh_key_authorized_principals.go
+++ b/models/asymkey/ssh_key_authorized_principals.go
@@ -133,6 +133,10 @@ func regeneratePrincipalKeys(ctx context.Context, t io.StringWriter) error {
 				return err
 			}
 		}
+		err = scanner.Err()
+		if err != nil {
+			return fmt.Errorf("scan: %w", err)
+		}
 		f.Close()
 	}
 	return nil
diff --git a/modules/git/commit.go b/modules/git/commit.go
index c30f1e35e5..47237d2d58 100644
--- a/modules/git/commit.go
+++ b/modules/git/commit.go
@@ -9,6 +9,7 @@ import (
 	"bytes"
 	"context"
 	"errors"
+	"fmt"
 	"io"
 	"os/exec"
 	"strconv"
@@ -390,6 +391,10 @@ func (c *Commit) GetSubModules() (*ObjectCache, error) {
 			}
 		}
 	}
+	err = scanner.Err()
+	if err != nil {
+		return nil, fmt.Errorf("scan: %w", err)
+	}
 
 	return c.submoduleCache, nil
 }
diff --git a/modules/git/repo_stats.go b/modules/git/repo_stats.go
index 41f94e24f9..ce82946873 100644
--- a/modules/git/repo_stats.go
+++ b/modules/git/repo_stats.go
@@ -124,6 +124,10 @@ func (repo *Repository) GetCodeActivityStats(fromTime time.Time, branch string)
 					}
 				}
 			}
+			err = scanner.Err()
+			if err != nil {
+				return fmt.Errorf("scan: %w", err)
+			}
 			a := make([]*CodeActivityAuthor, 0, len(authors))
 			for _, v := range authors {
 				a = append(a, v)
diff --git a/modules/markup/csv/csv.go b/modules/markup/csv/csv.go
index 570c4f4704..50bb918442 100644
--- a/modules/markup/csv/csv.go
+++ b/modules/markup/csv/csv.go
@@ -6,6 +6,7 @@ package markup
 import (
 	"bufio"
 	"bytes"
+	"fmt"
 	"html"
 	"io"
 	"regexp"
@@ -123,6 +124,10 @@ func (Renderer) fallbackRender(input io.Reader, tmpBlock *bufio.Writer) error {
 			return err
 		}
 	}
+	err = scan.Err()
+	if err != nil {
+		return fmt.Errorf("scan: %w", err)
+	}
 
 	_, err = tmpBlock.WriteString("</pre>")
 	if err != nil {
diff --git a/routers/web/repo/compare.go b/routers/web/repo/compare.go
index b0570f97c3..bf42b77b66 100644
--- a/routers/web/repo/compare.go
+++ b/routers/web/repo/compare.go
@@ -980,5 +980,9 @@ func getExcerptLines(commit *git.Commit, filePath string, idxLeft, idxRight, chu
 		}
 		diffLines = append(diffLines, diffLine)
 	}
+	err = scanner.Err()
+	if err != nil {
+		return nil, fmt.Errorf("scan: %w", err)
+	}
 	return diffLines, nil
 }
diff --git a/services/doctor/authorizedkeys.go b/services/doctor/authorizedkeys.go
index 050a4e7974..43740af3db 100644
--- a/services/doctor/authorizedkeys.go
+++ b/services/doctor/authorizedkeys.go
@@ -50,6 +50,10 @@ func checkAuthorizedKeys(ctx context.Context, logger log.Logger, autofix bool) e
 		}
 		linesInAuthorizedKeys.Add(line)
 	}
+	err = scanner.Err()
+	if err != nil {
+		return fmt.Errorf("scan: %w", err)
+	}
 	f.Close()
 
 	// now we regenerate and check if there are any lines missing

From 0da787f23737d252e6c80aa1a1f665e09dba0ea9 Mon Sep 17 00:00:00 2001
From: Zettat123 <zettat123@gmail.com>
Date: Tue, 19 Mar 2024 12:46:40 +0800
Subject: [PATCH 13/74] Fix milestoneID filter bug in issue list (#29897)

Fix #29717

(cherry picked from commit 656d8e2267dbdbb595704d507a780533038bb7ed)
---
 routers/web/repo/issue.go | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go
index e4193e9b79..39b33e15b3 100644
--- a/routers/web/repo/issue.go
+++ b/routers/web/repo/issue.go
@@ -451,13 +451,13 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption opt
 	linkStr := "%s?q=%s&type=%s&sort=%s&state=%s&labels=%s&milestone=%d&project=%d&assignee=%d&poster=%d&archived=%t"
 	ctx.Data["AllStatesLink"] = fmt.Sprintf(linkStr, ctx.Link,
 		url.QueryEscape(keyword), url.QueryEscape(viewType), url.QueryEscape(sortType), "all", url.QueryEscape(selectLabels),
-		mentionedID, projectID, assigneeID, posterID, archived)
+		milestoneID, projectID, assigneeID, posterID, archived)
 	ctx.Data["OpenLink"] = fmt.Sprintf(linkStr, ctx.Link,
 		url.QueryEscape(keyword), url.QueryEscape(viewType), url.QueryEscape(sortType), "open", url.QueryEscape(selectLabels),
-		mentionedID, projectID, assigneeID, posterID, archived)
+		milestoneID, projectID, assigneeID, posterID, archived)
 	ctx.Data["ClosedLink"] = fmt.Sprintf(linkStr, ctx.Link,
 		url.QueryEscape(keyword), url.QueryEscape(viewType), url.QueryEscape(sortType), "closed", url.QueryEscape(selectLabels),
-		mentionedID, projectID, assigneeID, posterID, archived)
+		milestoneID, projectID, assigneeID, posterID, archived)
 	ctx.Data["SelLabelIDs"] = labelIDs
 	ctx.Data["SelectLabels"] = selectLabels
 	ctx.Data["ViewType"] = viewType

From e8e8d14b5321a6faf679098a3cd9ec992070089b Mon Sep 17 00:00:00 2001
From: yp05327 <576951401@qq.com>
Date: Tue, 19 Mar 2024 16:23:40 +0900
Subject: [PATCH 14/74] Fix invalid link of the commit status when ref is tag
 (#29752)

Fix #29731
Caused by #24634
Also remove fixme.

ps: we can not fix the existed runs, as wrong refs are all recorded in
DB, and we can not know whether they are branch or tag:

![image](https://github.com/go-gitea/gitea/assets/18380374/cb7cf266-f73f-419a-be1a-4689fdd1952a)

(cherry picked from commit 98217b034076157547cf688cc10f47cd3275c872)

Conflicts:
	tests/integration/actions_trigger_test.go
	there is a need for more imports because the exist tests
	are done differently, using CreateDeclarativeRepo
---
 services/actions/notifier.go              |  16 ++-
 tests/integration/actions_trigger_test.go | 121 ++++++++++++++++++++++
 2 files changed, 134 insertions(+), 3 deletions(-)

diff --git a/services/actions/notifier.go b/services/actions/notifier.go
index aa88d4e0d8..eec5f814da 100644
--- a/services/actions/notifier.go
+++ b/services/actions/notifier.go
@@ -515,6 +515,12 @@ func (*actionsNotifier) MergePullRequest(ctx context.Context, doer *user_model.U
 }
 
 func (n *actionsNotifier) PushCommits(ctx context.Context, pusher *user_model.User, repo *repo_model.Repository, opts *repository.PushUpdateOptions, commits *repository.PushCommits) {
+	commitID, _ := git.NewIDFromString(opts.NewCommitID)
+	if commitID.IsZero() {
+		log.Trace("new commitID is empty")
+		return
+	}
+
 	ctx = withMethod(ctx, "PushCommits")
 
 	apiPusher := convert.ToUser(ctx, pusher, nil)
@@ -547,9 +553,9 @@ func (n *actionsNotifier) CreateRef(ctx context.Context, pusher *user_model.User
 	apiRepo := convert.ToRepo(ctx, repo, access_model.Permission{AccessMode: perm_model.AccessModeNone})
 
 	newNotifyInput(repo, pusher, webhook_module.HookEventCreate).
-		WithRef(refFullName.ShortName()). // FIXME: should we use a full ref name
+		WithRef(refFullName.String()).
 		WithPayload(&api.CreatePayload{
-			Ref:     refFullName.ShortName(),
+			Ref:     refFullName.String(),
 			Sha:     refID,
 			RefType: refFullName.RefType(),
 			Repo:    apiRepo,
@@ -566,7 +572,7 @@ func (n *actionsNotifier) DeleteRef(ctx context.Context, pusher *user_model.User
 
 	newNotifyInput(repo, pusher, webhook_module.HookEventDelete).
 		WithPayload(&api.DeletePayload{
-			Ref:        refFullName.ShortName(),
+			Ref:        refFullName.String(),
 			RefType:    refFullName.RefType(),
 			PusherType: api.PusherTypeUser,
 			Repo:       apiRepo,
@@ -623,6 +629,10 @@ func (n *actionsNotifier) UpdateRelease(ctx context.Context, doer *user_model.Us
 }
 
 func (n *actionsNotifier) DeleteRelease(ctx context.Context, doer *user_model.User, rel *repo_model.Release) {
+	if rel.IsTag {
+		// has sent same action in `PushCommits`, so skip it.
+		return
+	}
 	ctx = withMethod(ctx, "DeleteRelease")
 	notifyRelease(ctx, doer, rel, api.HookReleaseDeleted)
 }
diff --git a/tests/integration/actions_trigger_test.go b/tests/integration/actions_trigger_test.go
index 0137964a5c..9359dc09b4 100644
--- a/tests/integration/actions_trigger_test.go
+++ b/tests/integration/actions_trigger_test.go
@@ -11,17 +11,22 @@ import (
 	"time"
 
 	actions_model "code.gitea.io/gitea/models/actions"
+	"code.gitea.io/gitea/models/db"
+	git_model "code.gitea.io/gitea/models/git"
 	issues_model "code.gitea.io/gitea/models/issues"
+	repo_model "code.gitea.io/gitea/models/repo"
 	unit_model "code.gitea.io/gitea/models/unit"
 	"code.gitea.io/gitea/models/unittest"
 	user_model "code.gitea.io/gitea/models/user"
 	actions_module "code.gitea.io/gitea/modules/actions"
 	"code.gitea.io/gitea/modules/git"
+	"code.gitea.io/gitea/modules/gitrepo"
 	"code.gitea.io/gitea/modules/setting"
 	"code.gitea.io/gitea/modules/test"
 	webhook_module "code.gitea.io/gitea/modules/webhook"
 	actions_service "code.gitea.io/gitea/services/actions"
 	pull_service "code.gitea.io/gitea/services/pull"
+	release_service "code.gitea.io/gitea/services/release"
 	repo_service "code.gitea.io/gitea/services/repository"
 	files_service "code.gitea.io/gitea/services/repository/files"
 
@@ -275,3 +280,119 @@ func TestSkipCI(t *testing.T) {
 		assert.Equal(t, 1, unittest.GetCount(t, &actions_model.ActionRun{RepoID: repo.ID}))
 	})
 }
+
+func TestCreateDeleteRefEvent(t *testing.T) {
+	onGiteaRun(t, func(t *testing.T, u *url.URL) {
+		user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
+
+		// create the repo
+		repo, err := repo_service.CreateRepository(db.DefaultContext, user2, user2, repo_service.CreateRepoOptions{
+			Name:          "create-delete-ref-event",
+			Description:   "test create delete ref ci event",
+			AutoInit:      true,
+			Gitignores:    "Go",
+			License:       "MIT",
+			Readme:        "Default",
+			DefaultBranch: "main",
+			IsPrivate:     false,
+		})
+		assert.NoError(t, err)
+		assert.NotEmpty(t, repo)
+
+		// enable actions
+		err = repo_service.UpdateRepositoryUnits(db.DefaultContext, repo, []repo_model.RepoUnit{{
+			RepoID: repo.ID,
+			Type:   unit_model.TypeActions,
+		}}, nil)
+		assert.NoError(t, err)
+
+		// add workflow file to the repo
+		addWorkflowToBaseResp, err := files_service.ChangeRepoFiles(git.DefaultContext, repo, user2, &files_service.ChangeRepoFilesOptions{
+			Files: []*files_service.ChangeRepoFile{
+				{
+					Operation:     "create",
+					TreePath:      ".gitea/workflows/createdelete.yml",
+					ContentReader: strings.NewReader("name: test\non:\n  [create,delete]\njobs:\n  test:\n    runs-on: ubuntu-latest\n    steps:\n      - run: echo helloworld\n"),
+				},
+			},
+			Message:   "add workflow",
+			OldBranch: "main",
+			NewBranch: "main",
+			Author: &files_service.IdentityOptions{
+				Name:  user2.Name,
+				Email: user2.Email,
+			},
+			Committer: &files_service.IdentityOptions{
+				Name:  user2.Name,
+				Email: user2.Email,
+			},
+			Dates: &files_service.CommitDateOptions{
+				Author:    time.Now(),
+				Committer: time.Now(),
+			},
+		})
+		assert.NoError(t, err)
+		assert.NotEmpty(t, addWorkflowToBaseResp)
+
+		// Get the commit ID of the default branch
+		gitRepo, err := gitrepo.OpenRepository(git.DefaultContext, repo)
+		assert.NoError(t, err)
+		defer gitRepo.Close()
+		branch, err := git_model.GetBranch(db.DefaultContext, repo.ID, repo.DefaultBranch)
+		assert.NoError(t, err)
+
+		// create a branch
+		err = repo_service.CreateNewBranchFromCommit(db.DefaultContext, user2, repo, gitRepo, branch.CommitID, "test-create-branch")
+		assert.NoError(t, err)
+		run := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRun{
+			Title:      "add workflow",
+			RepoID:     repo.ID,
+			Event:      "create",
+			Ref:        "refs/heads/test-create-branch",
+			WorkflowID: "createdelete.yml",
+			CommitSHA:  branch.CommitID,
+		})
+		assert.NotNil(t, run)
+
+		// create a tag
+		err = release_service.CreateNewTag(db.DefaultContext, user2, repo, branch.CommitID, "test-create-tag", "test create tag event")
+		assert.NoError(t, err)
+		run = unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRun{
+			Title:      "add workflow",
+			RepoID:     repo.ID,
+			Event:      "create",
+			Ref:        "refs/tags/test-create-tag",
+			WorkflowID: "createdelete.yml",
+			CommitSHA:  branch.CommitID,
+		})
+		assert.NotNil(t, run)
+
+		// delete the branch
+		err = repo_service.DeleteBranch(db.DefaultContext, user2, repo, gitRepo, "test-create-branch")
+		assert.NoError(t, err)
+		run = unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRun{
+			Title:      "add workflow",
+			RepoID:     repo.ID,
+			Event:      "delete",
+			Ref:        "main",
+			WorkflowID: "createdelete.yml",
+			CommitSHA:  branch.CommitID,
+		})
+		assert.NotNil(t, run)
+
+		// delete the tag
+		tag, err := repo_model.GetRelease(db.DefaultContext, repo.ID, "test-create-tag")
+		assert.NoError(t, err)
+		err = release_service.DeleteReleaseByID(db.DefaultContext, repo, tag, user2, true)
+		assert.NoError(t, err)
+		run = unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRun{
+			Title:      "add workflow",
+			RepoID:     repo.ID,
+			Event:      "delete",
+			Ref:        "main",
+			WorkflowID: "createdelete.yml",
+			CommitSHA:  branch.CommitID,
+		})
+		assert.NotNil(t, run)
+	})
+}

From 272dffb0d6052c1911480b9bc0cee2cb4e254b17 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Tue, 19 Mar 2024 11:36:54 +0100
Subject: [PATCH 15/74] Fix border on focus in dashboard repo search (#29893)

Before:

<img width="449" alt="Screenshot 2024-03-18 at 22 35 10"
src="https://github.com/go-gitea/gitea/assets/115237/f2893870-e7a3-4e34-b0cf-4610735c9b36">

After:

<img width="453" alt="image"
src="https://github.com/go-gitea/gitea/assets/115237/36a9f800-28a4-40fc-b6d2-a2e717ddba01">

(cherry picked from commit 5a8559ec47a271e45bf5836eaf5e9040a0f1d6bf)
---
 web_src/css/base.css | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/web_src/css/base.css b/web_src/css/base.css
index 5a9c938520..b4f8be607b 100644
--- a/web_src/css/base.css
+++ b/web_src/css/base.css
@@ -294,7 +294,9 @@ ol.ui.list li,
 .ui.action.input:not([class*="left action"]) > input:focus + .ui.dropdown.selection,
 .ui.action.input:not([class*="left action"]) > input:focus + .ui.dropdown.selection:hover,
 .ui.action.input:not([class*="left action"]) > input:focus + .button,
-.ui.action.input:not([class*="left action"]) > input:focus + .button:hover {
+.ui.action.input:not([class*="left action"]) > input:focus + .button:hover,
+.ui.action.input:not([class*="left action"]) > input:focus + .icon + .button,
+.ui.action.input:not([class*="left action"]) > input:focus + .icon + .button:hover {
   border-left-color: var(--color-primary);
 }
 .ui.action.input:not([class*="left action"]) > input:focus {
@@ -1972,6 +1974,10 @@ table th[data-sortt-desc] .svg {
   justify-content: center;
 }
 
+.ui.icon.input > i.icon {
+  transition: none;
+}
+
 .flex-items-block > .item,
 .flex-text-block {
   display: flex;

From 8c3d7c049a3540dfccf81a79e33a52b5f3172488 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Tue, 19 Mar 2024 17:28:46 +0100
Subject: [PATCH 16/74] Forbid jQuery `.css` and refactor all usage (#29852)

Tested all functionality. There is a [pre-existing
bug](https://github.com/go-gitea/gitea/issues/29853) when moving a
project panels which is not caused by this refactoring.

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
(cherry picked from commit fa100618c4b644346bf5666f92d33dce0747d0a2)
---
 .eslintrc.yaml                          |   4 +-
 web_src/js/features/colorpicker.js      |   8 +-
 web_src/js/features/comp/ColorPicker.js |  16 ++--
 web_src/js/features/comp/LabelEdit.js   |   6 +-
 web_src/js/features/imagediff.js        | 108 ++++++++++++------------
 web_src/js/features/repo-legacy.js      |   2 +-
 web_src/js/features/repo-projects.js    |   7 +-
 7 files changed, 78 insertions(+), 73 deletions(-)

diff --git a/.eslintrc.yaml b/.eslintrc.yaml
index b65fe56cf2..72039a6013 100644
--- a/.eslintrc.yaml
+++ b/.eslintrc.yaml
@@ -286,7 +286,7 @@ rules:
   jquery/no-class: [0]
   jquery/no-clone: [2]
   jquery/no-closest: [0]
-  jquery/no-css: [0]
+  jquery/no-css: [2]
   jquery/no-data: [0]
   jquery/no-deferred: [2]
   jquery/no-delegate: [2]
@@ -409,7 +409,7 @@ rules:
   no-jquery/no-constructor-attributes: [2]
   no-jquery/no-contains: [2]
   no-jquery/no-context-prop: [2]
-  no-jquery/no-css: [0]
+  no-jquery/no-css: [2]
   no-jquery/no-data: [0]
   no-jquery/no-deferred: [2]
   no-jquery/no-delegate: [2]
diff --git a/web_src/js/features/colorpicker.js b/web_src/js/features/colorpicker.js
index a5fdb3f5a6..df0353376d 100644
--- a/web_src/js/features/colorpicker.js
+++ b/web_src/js/features/colorpicker.js
@@ -1,10 +1,12 @@
-export async function createColorPicker($els) {
-  if (!$els || !$els.length) return;
+import $ from 'jquery';
+
+export async function createColorPicker(els) {
+  if (!els.length) return;
 
   await Promise.all([
     import(/* webpackChunkName: "minicolors" */'@claviska/jquery-minicolors'),
     import(/* webpackChunkName: "minicolors" */'@claviska/jquery-minicolors/jquery.minicolors.css'),
   ]);
 
-  $els.minicolors();
+  return $(els).minicolors();
 }
diff --git a/web_src/js/features/comp/ColorPicker.js b/web_src/js/features/comp/ColorPicker.js
index 5665b7a24a..d7e7038803 100644
--- a/web_src/js/features/comp/ColorPicker.js
+++ b/web_src/js/features/comp/ColorPicker.js
@@ -2,11 +2,15 @@ import $ from 'jquery';
 import {createColorPicker} from '../colorpicker.js';
 
 export function initCompColorPicker() {
-  createColorPicker($('.color-picker'));
+  (async () => {
+    await createColorPicker(document.querySelectorAll('.color-picker'));
 
-  $('.precolors .color').on('click', function () {
-    const color_hex = $(this).data('color-hex');
-    $('.color-picker').val(color_hex);
-    $('.minicolors-swatch-color').css('background-color', color_hex);
-  });
+    for (const el of document.querySelectorAll('.precolors .color')) {
+      el.addEventListener('click', (e) => {
+        const color = e.target.getAttribute('data-color-hex');
+        const parent = e.target.closest('.color.picker');
+        $(parent.querySelector('.color-picker')).minicolors('value', color);
+      });
+    }
+  })();
 }
diff --git a/web_src/js/features/comp/LabelEdit.js b/web_src/js/features/comp/LabelEdit.js
index 26800ae05c..44fc9d9b6b 100644
--- a/web_src/js/features/comp/LabelEdit.js
+++ b/web_src/js/features/comp/LabelEdit.js
@@ -43,7 +43,6 @@ export function initCompLabelEdit(selector) {
 
   // Edit label
   $('.edit-label-button').on('click', function () {
-    $('.edit-label .color-picker').minicolors('value', $(this).data('color'));
     $('#label-modal-id').val($(this).data('id'));
 
     const $nameInput = $('.edit-label .label-name-input');
@@ -60,9 +59,8 @@ export function initCompLabelEdit(selector) {
       (!this.hasAttribute('data-exclusive') || !isExclusiveScopeName($nameInput.val())));
     updateExclusiveLabelEdit('.edit-label');
 
-    $('.edit-label .label-desc-input').val($(this).data('description'));
-    $('.edit-label .color-picker').val($(this).data('color'));
-    $('.edit-label .minicolors-swatch-color').css('background-color', $(this).data('color'));
+    $('.edit-label .label-desc-input').val(this.getAttribute('data-description'));
+    $('.edit-label .color-picker').minicolors('value', this.getAttribute('data-color'));
 
     $('.edit-label.modal').modal({
       onApprove() {
diff --git a/web_src/js/features/imagediff.js b/web_src/js/features/imagediff.js
index 80b7e83385..293e1f809a 100644
--- a/web_src/js/features/imagediff.js
+++ b/web_src/js/features/imagediff.js
@@ -133,24 +133,25 @@ export function initImageDiff() {
         $container.find('.bounds-info-before .bounds-info-height').text(`${sizes.image2[0].naturalHeight}px`).addClass(heightChanged ? 'red' : '');
       }
 
-      sizes.image1.css({
-        width: sizes.size1.width * factor,
-        height: sizes.size1.height * factor
-      });
-      sizes.image1.parent().css({
-        margin: `10px auto`,
-        width: sizes.size1.width * factor + 2,
-        height: sizes.size1.height * factor + 2
-      });
-      sizes.image2.css({
-        width: sizes.size2.width * factor,
-        height: sizes.size2.height * factor
-      });
-      sizes.image2.parent().css({
-        margin: `10px auto`,
-        width: sizes.size2.width * factor + 2,
-        height: sizes.size2.height * factor + 2
-      });
+      const image1 = sizes.image1[0];
+      if (image1) {
+        const container = image1.parentNode;
+        image1.style.width = `${sizes.size1.width * factor}px`;
+        image1.style.height = `${sizes.size1.height * factor}px`;
+        container.style.margin = '10px auto';
+        container.style.width = `${sizes.size1.width * factor + 2}px`;
+        container.style.height = `${sizes.size1.height * factor + 2}px`;
+      }
+
+      const image2 = sizes.image2[0];
+      if (image2) {
+        const container = image2.parentNode;
+        image2.style.width = `${sizes.size2.width * factor}px`;
+        image2.style.height = `${sizes.size2.height * factor}px`;
+        container.style.margin = '10px auto';
+        container.style.width = `${sizes.size2.width * factor + 2}px`;
+        container.style.height = `${sizes.size2.height * factor + 2}px`;
+      }
     }
 
     function initSwipe(sizes) {
@@ -159,36 +160,39 @@ export function initImageDiff() {
         factor = (diffContainerWidth - 12) / sizes.max.width;
       }
 
-      sizes.image1.css({
-        width: sizes.size1.width * factor,
-        height: sizes.size1.height * factor
-      });
-      sizes.image1.parent().css({
-        margin: `0px ${sizes.ratio[0] * factor}px`,
-        width: sizes.size1.width * factor + 2,
-        height: sizes.size1.height * factor + 2
-      });
-      sizes.image1.parent().parent().css({
-        padding: `${sizes.ratio[1] * factor}px 0 0 0`,
-        width: sizes.max.width * factor + 2
-      });
-      sizes.image2.css({
-        width: sizes.size2.width * factor,
-        height: sizes.size2.height * factor
-      });
-      sizes.image2.parent().css({
-        margin: `${sizes.ratio[3] * factor}px ${sizes.ratio[2] * factor}px`,
-        width: sizes.size2.width * factor + 2,
-        height: sizes.size2.height * factor + 2
-      });
-      sizes.image2.parent().parent().css({
-        width: sizes.max.width * factor + 2,
-        height: sizes.max.height * factor + 2
-      });
-      $container.find('.diff-swipe').css({
-        width: sizes.max.width * factor + 2,
-        height: sizes.max.height * factor + 30 /* extra height for inner "position: absolute" elements */,
-      });
+      const image1 = sizes.image1[0];
+      if (image1) {
+        const container = image1.parentNode;
+        const swipeFrame = container.parentNode;
+        image1.style.width = `${sizes.size1.width * factor}px`;
+        image1.style.height = `${sizes.size1.height * factor}px`;
+        container.style.margin = `0px ${sizes.ratio[0] * factor}px`;
+        container.style.width = `${sizes.size1.width * factor + 2}px`;
+        container.style.height = `${sizes.size1.height * factor + 2}px`;
+        swipeFrame.style.padding = `${sizes.ratio[1] * factor}px 0 0 0`;
+        swipeFrame.style.width = `${sizes.max.width * factor + 2}px`;
+      }
+
+      const image2 = sizes.image2[0];
+      if (image2) {
+        const container = image2.parentNode;
+        const swipeFrame = container.parentNode;
+        image2.style.width = `${sizes.size2.width * factor}px`;
+        image2.style.height = `${sizes.size2.height * factor}px`;
+        container.style.margin = `${sizes.ratio[3] * factor}px ${sizes.ratio[2] * factor}px`;
+        container.style.width = `${sizes.size2.width * factor + 2}px`;
+        container.style.height = `${sizes.size2.height * factor + 2}px`;
+        swipeFrame.style.width = `${sizes.max.width * factor + 2}px`;
+        swipeFrame.style.height = `${sizes.max.height * factor + 2}px`;
+      }
+
+      // extra height for inner "position: absolute" elements
+      const swipe = $container.find('.diff-swipe')[0];
+      if (swipe) {
+        swipe.style.width = `${sizes.max.width * factor + 2}px`;
+        swipe.style.height = `${sizes.max.height * factor + 30}px`;
+      }
+
       $container.find('.swipe-bar').on('mousedown', function(e) {
         e.preventDefault();
 
@@ -200,13 +204,9 @@ export function initImageDiff() {
           e2.preventDefault();
 
           const value = Math.max(0, Math.min(e2.clientX - $swipeFrame.offset().left, width));
+          $swipeBar[0].style.left = `${value}px`;
+          $container.find('.swipe-container')[0].style.width = `${$swipeFrame.width() - value}px`;
 
-          $swipeBar.css({
-            left: value
-          });
-          $container.find('.swipe-container').css({
-            width: $swipeFrame.width() - value
-          });
           $(document).on('mouseup.diff-swipe', () => {
             $(document).off('.diff-swipe');
           });
diff --git a/web_src/js/features/repo-legacy.js b/web_src/js/features/repo-legacy.js
index 6155b3a5da..11bb758187 100644
--- a/web_src/js/features/repo-legacy.js
+++ b/web_src/js/features/repo-legacy.js
@@ -389,7 +389,7 @@ async function onEditContent(event) {
               dz.emit('complete', attachment);
               dz.files.push(attachment);
               fileUuidDict[attachment.uuid] = {submitted: true};
-              $dropzone.find(`img[src='${imgSrc}']`).css('max-width', '100%');
+              $dropzone.find(`img[src='${imgSrc}']`)[0].style.maxWidth = '100%';
               const $input = $(`<input id="${attachment.uuid}" name="files" type="hidden">`).val(attachment.uuid);
               $dropzone.find('.files').append($input);
             }
diff --git a/web_src/js/features/repo-projects.js b/web_src/js/features/repo-projects.js
index 1f86711ab1..e95d875ec4 100644
--- a/web_src/js/features/repo-projects.js
+++ b/web_src/js/features/repo-projects.js
@@ -72,7 +72,7 @@ async function initRepoProjectSortable() {
             await PUT($(column).data('url'), {
               data: {
                 sorting: i,
-                color: rgbToHex($(column).css('backgroundColor')),
+                color: rgbToHex(window.getComputedStyle($(column)[0]).backgroundColor),
               },
             });
           } catch (error) {
@@ -111,8 +111,9 @@ export function initRepoProject() {
     const $projectColorInput = $(this).find('#new_project_column_color');
     const $boardColumn = $(this).closest('.project-column');
 
-    if ($boardColumn.css('backgroundColor')) {
-      setLabelColor($projectHeader, rgbToHex($boardColumn.css('backgroundColor')));
+    const bgColor = $boardColumn[0].style.backgroundColor;
+    if (bgColor) {
+      setLabelColor($projectHeader, rgbToHex(bgColor));
     }
 
     $(this).find('.edit-project-column-button').on('click', async function (e) {

From 7512b9f7d4992124e96c4cb3b345495ab82a71f2 Mon Sep 17 00:00:00 2001
From: Yarden Shoham <git@yardenshoham.com>
Date: Wed, 20 Mar 2024 01:08:42 +0200
Subject: [PATCH 17/74] Switch to the maintained vitest extension (#29914)

https://marketplace.visualstudio.com/items?itemName=zixuanchen.vitest-explorer
was moved to
https://marketplace.visualstudio.com/items?itemName=vitest.explorer

Signed-off-by: Yarden Shoham <git@yardenshoham.com>
(cherry picked from commit 8bf4173e31a4018fb277c871df7d8d31c98dba0b)
---
 .devcontainer/devcontainer.json | 4 ++--
 .gitpod.yml                     | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
index 8563aafd04..1b1b66c7dd 100644
--- a/.devcontainer/devcontainer.json
+++ b/.devcontainer/devcontainer.json
@@ -4,7 +4,7 @@
   "features": {
     // installs nodejs into container
     "ghcr.io/devcontainers/features/node:1": {
-      "version":"20"
+      "version": "20"
     },
     "ghcr.io/devcontainers/features/git-lfs:1.1.0": {},
     "ghcr.io/devcontainers-contrib/features/poetry:2": {},
@@ -24,7 +24,7 @@
         "DavidAnson.vscode-markdownlint",
         "Vue.volar",
         "ms-azuretools.vscode-docker",
-        "zixuanchen.vitest-explorer",
+        "vitest.explorer",
         "qwtel.sqlite-viewer",
         "GitHub.vscode-pull-request-github"
       ]
diff --git a/.gitpod.yml b/.gitpod.yml
index ed2f57f4bf..f573d55a76 100644
--- a/.gitpod.yml
+++ b/.gitpod.yml
@@ -42,7 +42,7 @@ vscode:
     - DavidAnson.vscode-markdownlint
     - Vue.volar
     - ms-azuretools.vscode-docker
-    - zixuanchen.vitest-explorer
+    - vitest.explorer
     - qwtel.sqlite-viewer
     - GitHub.vscode-pull-request-github
 

From 5ed8c86aab2cdf14816d69714db7d1458f6c3d4c Mon Sep 17 00:00:00 2001
From: Yarden Shoham <git@yardenshoham.com>
Date: Wed, 20 Mar 2024 01:39:36 +0200
Subject: [PATCH 18/74] Remove jQuery `.attr` from the issue author dropdown
 (#29915)

- Switched from jQuery `.attr` to plain javascript `.getAttribute`
- Tested the issue author dropdown functionality and it works as before

Signed-off-by: Yarden Shoham <git@yardenshoham.com>
(cherry picked from commit 55a8f4510af5ef67e484d45dd3789c29f120d58e)
---
 web_src/js/features/repo-issue-list.js | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/web_src/js/features/repo-issue-list.js b/web_src/js/features/repo-issue-list.js
index 880ecf9489..48658fd723 100644
--- a/web_src/js/features/repo-issue-list.js
+++ b/web_src/js/features/repo-issue-list.js
@@ -93,9 +93,9 @@ function initRepoIssueListAuthorDropdown() {
   const $searchDropdown = $('.user-remote-search');
   if (!$searchDropdown.length) return;
 
-  let searchUrl = $searchDropdown.attr('data-search-url');
-  const actionJumpUrl = $searchDropdown.attr('data-action-jump-url');
-  const selectedUserId = $searchDropdown.attr('data-selected-user-id');
+  let searchUrl = $searchDropdown[0].getAttribute('data-search-url');
+  const actionJumpUrl = $searchDropdown[0].getAttribute('data-action-jump-url');
+  const selectedUserId = $searchDropdown[0].getAttribute('data-selected-user-id');
   if (!searchUrl.includes('?')) searchUrl += '?';
 
   $searchDropdown.dropdown('setting', {

From f9546aa0f20c7056f51764242679303e64390d86 Mon Sep 17 00:00:00 2001
From: Yarden Shoham <git@yardenshoham.com>
Date: Wed, 20 Mar 2024 01:44:21 +0200
Subject: [PATCH 19/74] Remove jQuery `.attr` from the quick pull request
 button text (#29916)

- Switched from jQuery `.attr` to plain javascript `.getAttribute`
- Tested the quick pull request button text change functionality and it
works as before

Signed-off-by: Yarden Shoham <git@yardenshoham.com>
(cherry picked from commit 4cfda0241993eabb50ed937dab2ffa7deff3d36e)
---
 web_src/js/features/repo-editor.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/web_src/js/features/repo-editor.js b/web_src/js/features/repo-editor.js
index ba00573c07..1ab0a57865 100644
--- a/web_src/js/features/repo-editor.js
+++ b/web_src/js/features/repo-editor.js
@@ -72,7 +72,7 @@ export function initRepoEditor() {
       hideElem($('.quick-pull-branch-name'));
       document.querySelector('.quick-pull-branch-name input').required = false;
     }
-    $('#commit-button').text($(this).attr('button_text'));
+    $('#commit-button').text(this.getAttribute('button_text'));
   });
 
   const joinTreePath = ($fileNameEl) => {

From 32cef22bfdf3886404d234b0d9ef2397ebee71ba Mon Sep 17 00:00:00 2001
From: Yarden Shoham <git@yardenshoham.com>
Date: Wed, 20 Mar 2024 01:49:15 +0200
Subject: [PATCH 20/74] Remove jQuery `.attr` from the image diff (#29917)

- Switched from jQuery `.attr` to plain javascript `.setAttribute`
- Tested the image diff functionality and it works as before

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

diff --git a/web_src/js/features/imagediff.js b/web_src/js/features/imagediff.js
index 293e1f809a..2bac13b0bf 100644
--- a/web_src/js/features/imagediff.js
+++ b/web_src/js/features/imagediff.js
@@ -70,7 +70,7 @@ export function initImageDiff() {
 
   $('.image-diff:not([data-image-diff-loaded])').each(async function() {
     const $container = $(this);
-    $container.attr('data-image-diff-loaded', 'true');
+    this.setAttribute('data-image-diff-loaded', 'true');
 
     // the container may be hidden by "viewed" checkbox, so use the parent's width for reference
     const diffContainerWidth = Math.max($container.closest('.diff-file-box').width() - 300, 100);

From 872287c3804ee796b34ac5d8c61a22e4b31fce35 Mon Sep 17 00:00:00 2001
From: Yarden Shoham <git@yardenshoham.com>
Date: Wed, 20 Mar 2024 02:04:24 +0200
Subject: [PATCH 21/74] Remove jQuery `.attr` from the archive download and
 compare page branch selector (#29918)

- Switched from jQuery `.attr` to plain javascript `.getAttribute`
- Tested the archive download and compare page branch selector
functionality and it works as before

Signed-off-by: Yarden Shoham <git@yardenshoham.com>
(cherry picked from commit dd043854ee8963057daa7b835fc700731ae5fb9e)
---
 web_src/js/features/repo-common.js | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/web_src/js/features/repo-common.js b/web_src/js/features/repo-common.js
index a8221bbea8..9e11ffe197 100644
--- a/web_src/js/features/repo-common.js
+++ b/web_src/js/features/repo-common.js
@@ -34,7 +34,7 @@ async function getArchive($target, url, first) {
 export function initRepoArchiveLinks() {
   $('.archive-link').on('click', function (event) {
     event.preventDefault();
-    const url = $(this).attr('href');
+    const url = this.getAttribute('href');
     if (!url) return;
     getArchive($(event.target), url, true);
   });
@@ -80,10 +80,10 @@ export function initRepoCommonFilterSearchDropdown(selector) {
     fullTextSearch: 'exact',
     selectOnKeydown: false,
     onChange(_text, _value, $choice) {
-      if ($choice.attr('data-url')) {
-        window.location.href = $choice.attr('data-url');
+      if ($choice[0].getAttribute('data-url')) {
+        window.location.href = $choice[0].getAttribute('data-url');
       }
     },
-    message: {noResults: $dropdown.attr('data-no-results')},
+    message: {noResults: $dropdown[0].getAttribute('data-no-results')},
   });
 }

From e3585005193a189e62112773fd31f0dea1846ba1 Mon Sep 17 00:00:00 2001
From: Yarden Shoham <git@yardenshoham.com>
Date: Wed, 20 Mar 2024 02:09:52 +0200
Subject: [PATCH 22/74] Remove jQuery `.attr` from the user search box (#29919)

- Switched from jQuery `.attr` to plain javascript `.getAttribute`
- Tested the user search box and it works as before

Signed-off-by: Yarden Shoham <git@yardenshoham.com>
(cherry picked from commit adc61c5d71651acc316bbc3a7fee6f2c0c60b06e)
---
 web_src/js/features/comp/SearchUserBox.js | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/web_src/js/features/comp/SearchUserBox.js b/web_src/js/features/comp/SearchUserBox.js
index 992d4ef020..541052c174 100644
--- a/web_src/js/features/comp/SearchUserBox.js
+++ b/web_src/js/features/comp/SearchUserBox.js
@@ -5,9 +5,12 @@ const {appSubUrl} = window.config;
 const looksLikeEmailAddressCheck = /^\S+@\S+$/;
 
 export function initCompSearchUserBox() {
-  const $searchUserBox = $('#search-user-box');
-  const allowEmailInput = $searchUserBox.attr('data-allow-email') === 'true';
-  const allowEmailDescription = $searchUserBox.attr('data-allow-email-description');
+  const searchUserBox = document.getElementById('search-user-box');
+  if (!searchUserBox) return;
+
+  const $searchUserBox = $(searchUserBox);
+  const allowEmailInput = searchUserBox.getAttribute('data-allow-email') === 'true';
+  const allowEmailDescription = searchUserBox.getAttribute('data-allow-email-description') ?? undefined;
   $searchUserBox.search({
     minCharacters: 2,
     apiSettings: {

From 18e95af24f762cc1ad0ec23f619721422fe8e68a Mon Sep 17 00:00:00 2001
From: Jason Song <i@wolfogre.com>
Date: Wed, 20 Mar 2024 09:45:27 +0800
Subject: [PATCH 23/74] Restore deleted branches when syncing (#29898)

Regression of #29493. If a branch has been deleted, repushing it won't
restore it.

Lunny may have noticed that, but I didn't delve into the comment then
overlooked it:
https://github.com/go-gitea/gitea/pull/29493#discussion_r1509046867

The additional comments added are to explain the issue I found during
testing, which are unrelated to the fixes.

(cherry picked from commit f371f84fa3456c2a71470632b6458d81e4892a54)
---
 routers/private/hook_post_receive.go |  4 ++++
 tests/integration/git_push_test.go   | 17 +++++++++++++++++
 2 files changed, 21 insertions(+)

diff --git a/routers/private/hook_post_receive.go b/routers/private/hook_post_receive.go
index f5527cb15b..fff47caa88 100644
--- a/routers/private/hook_post_receive.go
+++ b/routers/private/hook_post_receive.go
@@ -75,6 +75,10 @@ func HookPostReceive(ctx *gitea_context.PrivateContext) {
 			updates = append(updates, option)
 			if repo.IsEmpty && (refFullName.BranchName() == "master" || refFullName.BranchName() == "main") {
 				// put the master/main branch first
+				// FIXME: It doesn't always work, since the master/main branch may not be the first batch of updates.
+				//        If the user pushes many branches at once, the Git hook will call the internal API in batches, rather than all at once.
+				//        See https://github.com/go-gitea/gitea/blob/cb52b17f92e2d2293f7c003649743464492bca48/cmd/hook.go#L27
+				//        If the user executes `git push origin --all` and pushes more than 30 branches, the master/main may not be the default branch.
 				copy(updates[1:], updates)
 				updates[0] = option
 			}
diff --git a/tests/integration/git_push_test.go b/tests/integration/git_push_test.go
index cb2910b175..0a35724807 100644
--- a/tests/integration/git_push_test.go
+++ b/tests/integration/git_push_test.go
@@ -69,6 +69,23 @@ func testGitPush(t *testing.T, u *url.URL) {
 			return pushed, deleted
 		})
 	})
+
+	t.Run("Push to deleted branch", func(t *testing.T) {
+		runTestGitPush(t, u, func(t *testing.T, gitPath string) (pushed, deleted []string) {
+			doGitPushTestRepository(gitPath, "origin", "master")(t) // make sure master is the default branch instead of a branch we are going to delete
+			pushed = append(pushed, "master")
+
+			doGitCreateBranch(gitPath, "branch-1")(t)
+			doGitPushTestRepository(gitPath, "origin", "branch-1")(t)
+			pushed = append(pushed, "branch-1")
+
+			// delete and restore
+			doGitPushTestRepository(gitPath, "origin", "--delete", "branch-1")(t)
+			doGitPushTestRepository(gitPath, "origin", "branch-1")(t)
+
+			return pushed, deleted
+		})
+	})
 }
 
 func runTestGitPush(t *testing.T, u *url.URL, gitOperation func(t *testing.T, gitPath string) (pushed, deleted []string)) {

From 06db43f85892da2146b6479c83cd2a1d4d8d0921 Mon Sep 17 00:00:00 2001
From: Jason Song <i@wolfogre.com>
Date: Wed, 20 Mar 2024 12:59:01 +0800
Subject: [PATCH 24/74] Show Actions post step when it's running (#29926)

The post step was always waiting, even if all steps were done. Then,
once the task was done, the post step became success immediately.

Before:

<img width="915" alt="xnip_240320_120228"
src="https://github.com/go-gitea/gitea/assets/9418365/00347430-f998-4c43-917a-bf6dd6d0e333">

After:

<img width="905" alt="xnip_240320_120443"
src="https://github.com/go-gitea/gitea/assets/9418365/a419b111-17c2-4029-a022-c761cc419091">

(cherry picked from commit 35cfd98e121005f90f6d0d55d9a69e37d7990010)
---
 modules/actions/task_state.go      | 14 ++++++++++--
 modules/actions/task_state_test.go | 34 ++++++++++++++++++++++++++++++
 2 files changed, 46 insertions(+), 2 deletions(-)

diff --git a/modules/actions/task_state.go b/modules/actions/task_state.go
index fe925bbb5d..31a74be3fd 100644
--- a/modules/actions/task_state.go
+++ b/modules/actions/task_state.go
@@ -41,6 +41,12 @@ func FullSteps(task *actions_model.ActionTask) []*actions_model.ActionTaskStep {
 	}
 	logIndex += preStep.LogLength
 
+	// lastHasRunStep is the last step that has run.
+	// For example,
+	// 1. preStep(Success) -> step1(Success) -> step2(Running) -> step3(Waiting) -> postStep(Waiting): lastHasRunStep is step1.
+	// 2. preStep(Success) -> step1(Success) -> step2(Success) -> step3(Success) -> postStep(Success): lastHasRunStep is step3.
+	// 3. preStep(Success) -> step1(Success) -> step2(Failure) -> step3 -> postStep(Waiting): lastHasRunStep is step2.
+	// So its Stopped is the Started of postStep when there are no more steps to run.
 	var lastHasRunStep *actions_model.ActionTaskStep
 	for _, step := range task.Steps {
 		if step.Status.HasRun() {
@@ -56,11 +62,15 @@ func FullSteps(task *actions_model.ActionTask) []*actions_model.ActionTaskStep {
 		Name:   postStepName,
 		Status: actions_model.StatusWaiting,
 	}
-	if task.Status.IsDone() {
+	// If the lastHasRunStep is the last step, or it has failed, postStep has started.
+	if lastHasRunStep.Status.IsFailure() || lastHasRunStep == task.Steps[len(task.Steps)-1] {
 		postStep.LogIndex = logIndex
 		postStep.LogLength = task.LogLength - postStep.LogIndex
-		postStep.Status = task.Status
 		postStep.Started = lastHasRunStep.Stopped
+		postStep.Status = actions_model.StatusRunning
+	}
+	if task.Status.IsDone() {
+		postStep.Status = task.Status
 		postStep.Stopped = task.Stopped
 	}
 	ret := make([]*actions_model.ActionTaskStep, 0, len(task.Steps)+2)
diff --git a/modules/actions/task_state_test.go b/modules/actions/task_state_test.go
index 3a599fbcbd..28213d781b 100644
--- a/modules/actions/task_state_test.go
+++ b/modules/actions/task_state_test.go
@@ -103,6 +103,40 @@ func TestFullSteps(t *testing.T) {
 				{Name: postStepName, Status: actions_model.StatusSuccess, LogIndex: 100, LogLength: 0, Started: 10100, Stopped: 10100},
 			},
 		},
+		{
+			name: "all steps finished but task is running",
+			task: &actions_model.ActionTask{
+				Steps: []*actions_model.ActionTaskStep{
+					{Status: actions_model.StatusSuccess, LogIndex: 10, LogLength: 80, Started: 10010, Stopped: 10090},
+				},
+				Status:    actions_model.StatusRunning,
+				Started:   10000,
+				Stopped:   0,
+				LogLength: 100,
+			},
+			want: []*actions_model.ActionTaskStep{
+				{Name: preStepName, Status: actions_model.StatusSuccess, LogIndex: 0, LogLength: 10, Started: 10000, Stopped: 10010},
+				{Status: actions_model.StatusSuccess, LogIndex: 10, LogLength: 80, Started: 10010, Stopped: 10090},
+				{Name: postStepName, Status: actions_model.StatusRunning, LogIndex: 90, LogLength: 10, Started: 10090, Stopped: 0},
+			},
+		},
+		{
+			name: "skipped task",
+			task: &actions_model.ActionTask{
+				Steps: []*actions_model.ActionTaskStep{
+					{Status: actions_model.StatusSkipped, LogIndex: 0, LogLength: 0, Started: 0, Stopped: 0},
+				},
+				Status:    actions_model.StatusSkipped,
+				Started:   0,
+				Stopped:   0,
+				LogLength: 0,
+			},
+			want: []*actions_model.ActionTaskStep{
+				{Name: preStepName, Status: actions_model.StatusSkipped, LogIndex: 0, LogLength: 0, Started: 0, Stopped: 0},
+				{Status: actions_model.StatusSkipped, LogIndex: 0, LogLength: 0, Started: 0, Stopped: 0},
+				{Name: postStepName, Status: actions_model.StatusSkipped, LogIndex: 0, LogLength: 0, Started: 0, Stopped: 0},
+			},
+		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {

From c758c5612e91e6f8367c5c7751ab122f9266317d Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Wed, 20 Mar 2024 13:56:42 +0800
Subject: [PATCH 25/74] Remove unnecessary ".Link" usages (#29909)

In HTML, `?key=val` already means "use the current link with new query parameters"

(cherry picked from commit 4c476fa41dc29dc24afda0925023ae3d0b9707cd)

Conflicts:
	templates/repo/issue/filter_list.tmpl
	templates/shared/issuelist.tmpl
	trivial context conflict because the lines in Forgejo have rel=nofollow
---
 templates/admin/emails/list.tmpl              |  8 +--
 templates/admin/org/list.tmpl                 | 12 ++--
 templates/explore/repo_list.tmpl              |  2 +-
 templates/explore/search.tmpl                 |  8 +--
 templates/projects/list.tmpl                  | 10 ++--
 templates/repo/actions/list.tmpl              | 12 ++--
 templates/repo/diff/box.tmpl                  |  6 +-
 templates/repo/issue/filter_list.tmpl         | 58 +++++++++----------
 templates/repo/issue/labels/label_list.tmpl   |  8 +--
 .../repo/issue/milestone/filter_list.tmpl     | 12 ++--
 templates/repo/view_file.tmpl                 |  2 +-
 templates/shared/issuelist.tmpl               |  2 +-
 templates/shared/search/code/results.tmpl     |  2 +-
 templates/user/dashboard/issues.tmpl          | 16 ++---
 templates/user/dashboard/milestones.tmpl      | 14 ++---
 .../notification_subscriptions.tmpl           | 28 ++++-----
 templates/user/settings/keys_gpg.tmpl         |  2 +-
 templates/user/settings/keys_ssh.tmpl         |  2 +-
 tests/integration/explore_user_test.go        | 12 ++--
 19 files changed, 108 insertions(+), 108 deletions(-)

diff --git a/templates/admin/emails/list.tmpl b/templates/admin/emails/list.tmpl
index 1e552fba88..660df55999 100644
--- a/templates/admin/emails/list.tmpl
+++ b/templates/admin/emails/list.tmpl
@@ -15,10 +15,10 @@
 					</span>
 					{{svg "octicon-triangle-down" 14 "dropdown icon"}}
 					<div class="menu">
-						<a class="{{if or (eq .SortType "email") (not .SortType)}}active {{end}}item" href="{{$.Link}}?sort=email&q={{$.Keyword}}">{{ctx.Locale.Tr "admin.emails.filter_sort.email"}}</a>
-						<a class="{{if eq .SortType "reverseemail"}}active {{end}}item" href="{{$.Link}}?sort=reverseemail&q={{$.Keyword}}">{{ctx.Locale.Tr "admin.emails.filter_sort.email_reverse"}}</a>
-						<a class="{{if eq .SortType "username"}}active {{end}}item" href="{{$.Link}}?sort=username&q={{$.Keyword}}">{{ctx.Locale.Tr "admin.emails.filter_sort.name"}}</a>
-						<a class="{{if eq .SortType "reverseusername"}}active {{end}}item" href="{{$.Link}}?sort=reverseusername&q={{$.Keyword}}">{{ctx.Locale.Tr "admin.emails.filter_sort.name_reverse"}}</a>
+						<a class="{{if or (eq .SortType "email") (not .SortType)}}active {{end}}item" href="?sort=email&q={{$.Keyword}}">{{ctx.Locale.Tr "admin.emails.filter_sort.email"}}</a>
+						<a class="{{if eq .SortType "reverseemail"}}active {{end}}item" href="?sort=reverseemail&q={{$.Keyword}}">{{ctx.Locale.Tr "admin.emails.filter_sort.email_reverse"}}</a>
+						<a class="{{if eq .SortType "username"}}active {{end}}item" href="?sort=username&q={{$.Keyword}}">{{ctx.Locale.Tr "admin.emails.filter_sort.name"}}</a>
+						<a class="{{if eq .SortType "reverseusername"}}active {{end}}item" href="?sort=reverseusername&q={{$.Keyword}}">{{ctx.Locale.Tr "admin.emails.filter_sort.name_reverse"}}</a>
 					</div>
 				</div>
 			</div>
diff --git a/templates/admin/org/list.tmpl b/templates/admin/org/list.tmpl
index efb0a8847e..4609d1b8b4 100644
--- a/templates/admin/org/list.tmpl
+++ b/templates/admin/org/list.tmpl
@@ -18,12 +18,12 @@
 					</span>
 					{{svg "octicon-triangle-down" 14 "dropdown icon"}}
 					<div class="menu">
-						<a class="{{if or (eq .SortType "oldest") (not .SortType)}}active {{end}}item" href="{{$.Link}}?sort=oldest&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.issues.filter_sort.oldest"}}</a>
-						<a class="{{if eq .SortType "newest"}}active {{end}}item" href="{{$.Link}}?sort=newest&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.issues.filter_sort.latest"}}</a>
-						<a class="{{if eq .SortType "alphabetically"}}active {{end}}item" href="{{$.Link}}?sort=alphabetically&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.issues.label.filter_sort.alphabetically"}}</a>
-						<a class="{{if eq .SortType "reversealphabetically"}}active {{end}}item" href="{{$.Link}}?sort=reversealphabetically&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.issues.label.filter_sort.reverse_alphabetically"}}</a>
-						<a class="{{if eq .SortType "recentupdate"}}active {{end}}item" href="{{$.Link}}?sort=recentupdate&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.issues.filter_sort.recentupdate"}}</a>
-						<a class="{{if eq .SortType "leastupdate"}}active {{end}}item" href="{{$.Link}}?sort=leastupdate&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.issues.filter_sort.leastupdate"}}</a>
+						<a class="{{if or (eq .SortType "oldest") (not .SortType)}}active {{end}}item" href="?sort=oldest&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.issues.filter_sort.oldest"}}</a>
+						<a class="{{if eq .SortType "newest"}}active {{end}}item" href="?sort=newest&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.issues.filter_sort.latest"}}</a>
+						<a class="{{if eq .SortType "alphabetically"}}active {{end}}item" href="?sort=alphabetically&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.issues.label.filter_sort.alphabetically"}}</a>
+						<a class="{{if eq .SortType "reversealphabetically"}}active {{end}}item" href="?sort=reversealphabetically&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.issues.label.filter_sort.reverse_alphabetically"}}</a>
+						<a class="{{if eq .SortType "recentupdate"}}active {{end}}item" href="?sort=recentupdate&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.issues.filter_sort.recentupdate"}}</a>
+						<a class="{{if eq .SortType "leastupdate"}}active {{end}}item" href="?sort=leastupdate&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.issues.filter_sort.leastupdate"}}</a>
 					</div>
 				</div>
 			</div>
diff --git a/templates/explore/repo_list.tmpl b/templates/explore/repo_list.tmpl
index 2bda50671f..505387988b 100644
--- a/templates/explore/repo_list.tmpl
+++ b/templates/explore/repo_list.tmpl
@@ -32,7 +32,7 @@
 					</div>
 					<div class="flex-item-trailing">
 						{{if .PrimaryLanguage}}
-							<a class="muted" href="{{$.Link}}?q={{$.Keyword}}&sort={{$.SortType}}&language={{.PrimaryLanguage.Language}}{{if $.TabName}}&tab={{$.TabName}}{{end}}">
+							<a class="muted" href="?q={{$.Keyword}}&sort={{$.SortType}}&language={{.PrimaryLanguage.Language}}{{if $.TabName}}&tab={{$.TabName}}{{end}}">
 								<span class="flex-text-inline"><i class="color-icon gt-mr-3" style="background-color: {{.PrimaryLanguage.Color}}"></i>{{.PrimaryLanguage.Language}}</span>
 							</a>
 						{{end}}
diff --git a/templates/explore/search.tmpl b/templates/explore/search.tmpl
index 54d995989a..c1d114125e 100644
--- a/templates/explore/search.tmpl
+++ b/templates/explore/search.tmpl
@@ -13,10 +13,10 @@
 		</span>
 		{{svg "octicon-triangle-down" 14 "dropdown icon"}}
 		<div class="menu">
-			<a class="{{if eq .SortType "newest"}}active {{end}}item" href="{{$.Link}}?sort=newest&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.issues.filter_sort.latest"}}</a>
-			<a class="{{if eq .SortType "oldest"}}active {{end}}item" href="{{$.Link}}?sort=oldest&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.issues.filter_sort.oldest"}}</a>
-			<a class="{{if eq .SortType "alphabetically"}}active {{end}}item" href="{{$.Link}}?sort=alphabetically&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.issues.label.filter_sort.alphabetically"}}</a>
-			<a class="{{if eq .SortType "reversealphabetically"}}active {{end}}item" href="{{$.Link}}?sort=reversealphabetically&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.issues.label.filter_sort.reverse_alphabetically"}}</a>
+			<a class="{{if eq .SortType "newest"}}active {{end}}item" href="?sort=newest&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.issues.filter_sort.latest"}}</a>
+			<a class="{{if eq .SortType "oldest"}}active {{end}}item" href="?sort=oldest&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.issues.filter_sort.oldest"}}</a>
+			<a class="{{if eq .SortType "alphabetically"}}active {{end}}item" href="?sort=alphabetically&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.issues.label.filter_sort.alphabetically"}}</a>
+			<a class="{{if eq .SortType "reversealphabetically"}}active {{end}}item" href="?sort=reversealphabetically&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.issues.label.filter_sort.reverse_alphabetically"}}</a>
 		</div>
 	</div>
 </div>
diff --git a/templates/projects/list.tmpl b/templates/projects/list.tmpl
index 414c9dca2e..d87e7e0663 100644
--- a/templates/projects/list.tmpl
+++ b/templates/projects/list.tmpl
@@ -1,11 +1,11 @@
 {{if and $.CanWriteProjects (not $.Repository.IsArchived)}}
 	<div class="gt-df gt-sb gt-mb-4">
 		<div class="small-menu-items ui compact tiny menu list-header-toggle">
-			<a class="item{{if not .IsShowClosed}} active{{end}}" href="{{$.Link}}?state=open&q={{$.Keyword}}">
+			<a class="item{{if not .IsShowClosed}} active{{end}}" href="?state=open&q={{$.Keyword}}">
 				{{svg "octicon-project-symlink" 16 "gt-mr-3"}}
 				{{ctx.Locale.PrettyNumber .OpenCount}}&nbsp;{{ctx.Locale.Tr "repo.issues.open_title"}}
 			</a>
-			<a class="item{{if .IsShowClosed}} active{{end}}" href="{{$.Link}}?state=closed&q={{$.Keyword}}">
+			<a class="item{{if .IsShowClosed}} active{{end}}" href="?state=closed&q={{$.Keyword}}">
 				{{svg "octicon-check" 16 "gt-mr-3"}}
 				{{ctx.Locale.PrettyNumber .ClosedCount}}&nbsp;{{ctx.Locale.Tr "repo.issues.closed_title"}}
 			</a>
@@ -31,9 +31,9 @@
 		</span>
 		{{svg "octicon-triangle-down" 14 "dropdown icon"}}
 		<div class="menu">
-			<a class="{{if eq .SortType "oldest"}}active {{end}}item" href="{{$.Link}}?q={{$.Keyword}}&sort=oldest&state={{$.State}}">{{ctx.Locale.Tr "repo.issues.filter_sort.oldest"}}</a>
-			<a class="{{if eq .SortType "recentupdate"}}active {{end}}item" href="{{$.Link}}?q={{$.Keyword}}&sort=recentupdate&state={{$.State}}">{{ctx.Locale.Tr "repo.issues.filter_sort.recentupdate"}}</a>
-			<a class="{{if eq .SortType "leastupdate"}}active {{end}}item" href="{{$.Link}}?q={{$.Keyword}}&sort=leastupdate&state={{$.State}}">{{ctx.Locale.Tr "repo.issues.filter_sort.leastupdate"}}</a>
+			<a class="{{if eq .SortType "oldest"}}active {{end}}item" href="?q={{$.Keyword}}&sort=oldest&state={{$.State}}">{{ctx.Locale.Tr "repo.issues.filter_sort.oldest"}}</a>
+			<a class="{{if eq .SortType "recentupdate"}}active {{end}}item" href="?q={{$.Keyword}}&sort=recentupdate&state={{$.State}}">{{ctx.Locale.Tr "repo.issues.filter_sort.recentupdate"}}</a>
+			<a class="{{if eq .SortType "leastupdate"}}active {{end}}item" href="?q={{$.Keyword}}&sort=leastupdate&state={{$.State}}">{{ctx.Locale.Tr "repo.issues.filter_sort.leastupdate"}}</a>
 		</div>
 	</div>
 </div>
diff --git a/templates/repo/actions/list.tmpl b/templates/repo/actions/list.tmpl
index 62d30305b3..55c0494566 100644
--- a/templates/repo/actions/list.tmpl
+++ b/templates/repo/actions/list.tmpl
@@ -8,9 +8,9 @@
 		<div class="ui stackable grid">
 			<div class="four wide column">
 				<div class="ui fluid vertical menu">
-					<a class="item{{if not $.CurWorkflow}} active{{end}}" href="{{$.Link}}?actor={{$.CurActor}}&status={{$.CurStatus}}">{{ctx.Locale.Tr "actions.runs.all_workflows"}}</a>
+					<a class="item{{if not $.CurWorkflow}} active{{end}}" href="?actor={{$.CurActor}}&status={{$.CurStatus}}">{{ctx.Locale.Tr "actions.runs.all_workflows"}}</a>
 					{{range .workflows}}
-						<a class="item{{if eq .Entry.Name $.CurWorkflow}} active{{end}}" href="{{$.Link}}?workflow={{.Entry.Name}}&actor={{$.CurActor}}&status={{$.CurStatus}}">{{.Entry.Name}}
+						<a class="item{{if eq .Entry.Name $.CurWorkflow}} active{{end}}" href="?workflow={{.Entry.Name}}&actor={{$.CurActor}}&status={{$.CurStatus}}">{{.Entry.Name}}
 							{{if .ErrMsg}}
 								<span data-tooltip-content="{{.ErrMsg}}">
 									{{svg "octicon-alert" 16 "text red"}}
@@ -35,11 +35,11 @@
 								<i class="icon">{{svg "octicon-search"}}</i>
 								<input type="text" placeholder="{{ctx.Locale.Tr "actions.runs.actor"}}">
 							</div>
-							<a class="item{{if not $.CurActor}} active{{end}}" href="{{$.Link}}?workflow={{$.CurWorkflow}}&status={{$.CurStatus}}&actor=0">
+							<a class="item{{if not $.CurActor}} active{{end}}" href="?workflow={{$.CurWorkflow}}&status={{$.CurStatus}}&actor=0">
 								{{ctx.Locale.Tr "actions.runs.actors_no_select"}}
 							</a>
 							{{range .Actors}}
-								<a class="item{{if eq .ID $.CurActor}} active{{end}}" href="{{$.Link}}?workflow={{$.CurWorkflow}}&actor={{.ID}}&status={{$.CurStatus}}">
+								<a class="item{{if eq .ID $.CurActor}} active{{end}}" href="?workflow={{$.CurWorkflow}}&actor={{.ID}}&status={{$.CurStatus}}">
 									{{ctx.AvatarUtils.Avatar . 20}} {{.GetDisplayName}}
 								</a>
 							{{end}}
@@ -54,11 +54,11 @@
 								<i class="icon">{{svg "octicon-search"}}</i>
 								<input type="text" placeholder="{{ctx.Locale.Tr "actions.runs.status"}}">
 							</div>
-							<a class="item{{if not $.CurStatus}} active{{end}}" href="{{$.Link}}?workflow={{$.CurWorkflow}}&actor={{$.CurActor}}&status=0">
+							<a class="item{{if not $.CurStatus}} active{{end}}" href="?workflow={{$.CurWorkflow}}&actor={{$.CurActor}}&status=0">
 								{{ctx.Locale.Tr "actions.runs.status_no_select"}}
 							</a>
 							{{range .StatusInfoList}}
-								<a class="item{{if eq .Status $.CurStatus}} active{{end}}" href="{{$.Link}}?workflow={{$.CurWorkflow}}&actor={{$.CurActor}}&status={{.Status}}">
+								<a class="item{{if eq .Status $.CurStatus}} active{{end}}" href="?workflow={{$.CurWorkflow}}&actor={{$.CurActor}}&status={{.Status}}">
 									{{.DisplayedStatus}}
 								</a>
 							{{end}}
diff --git a/templates/repo/diff/box.tmpl b/templates/repo/diff/box.tmpl
index 886004ea65..3daac1c2ce 100644
--- a/templates/repo/diff/box.tmpl
+++ b/templates/repo/diff/box.tmpl
@@ -68,7 +68,7 @@
 				binaryFileMessage: "{{ctx.Locale.Tr "repo.diff.bin"}}",
 				showMoreMessage: "{{ctx.Locale.Tr "repo.diff.show_more"}}",
 				statisticsMessage: "{{ctx.Locale.Tr "repo.diff.stats_desc_file"}}",
-				linkLoadMore: "{{$.Link}}?skip-to={{.Diff.End}}&file-only=true",
+				linkLoadMore: "?skip-to={{.Diff.End}}&file-only=true",
 			};
 
 			// for first time loading, the diffFileInfo is a plain object
@@ -187,7 +187,7 @@
 												{{ctx.Locale.Tr "repo.diff.file_suppressed_line_too_long"}}
 											{{else}}
 												{{ctx.Locale.Tr "repo.diff.file_suppressed"}}
-												<a class="ui basic tiny button diff-load-button" data-href="{{$.Link}}?file-only=true&files={{$file.Name}}&files={{$file.OldName}}">{{ctx.Locale.Tr "repo.diff.load"}}</a>
+												<a class="ui basic tiny button diff-load-button" data-href="?file-only=true&files={{$file.Name}}&files={{$file.OldName}}">{{ctx.Locale.Tr "repo.diff.load"}}</a>
 											{{end}}
 										{{else}}
 											{{ctx.Locale.Tr "repo.diff.bin_not_shown"}}
@@ -223,7 +223,7 @@
 					<div class="diff-file-box diff-box file-content gt-mt-3" id="diff-incomplete">
 						<h4 class="ui top attached normal header gt-df gt-ac gt-sb">
 							{{ctx.Locale.Tr "repo.diff.too_many_files"}}
-							<a class="ui basic tiny button" id="diff-show-more-files" data-href="{{$.Link}}?skip-to={{.Diff.End}}&file-only=true">{{ctx.Locale.Tr "repo.diff.show_more"}}</a>
+							<a class="ui basic tiny button" id="diff-show-more-files" data-href="?skip-to={{.Diff.End}}&file-only=true">{{ctx.Locale.Tr "repo.diff.show_more"}}</a>
 						</h4>
 					</div>
 				{{end}}
diff --git a/templates/repo/issue/filter_list.tmpl b/templates/repo/issue/filter_list.tmpl
index c95ce6b2b8..0349737c0d 100644
--- a/templates/repo/issue/filter_list.tmpl
+++ b/templates/repo/issue/filter_list.tmpl
@@ -23,8 +23,8 @@
 		</div>
 		<span class="info">{{ctx.Locale.Tr "repo.issues.filter_label_exclude"}}</span>
 		<div class="divider"></div>
-		<a rel="nofollow" class="{{if .AllLabels}}active selected {{end}}item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_label_no_select"}}</a>
-		<a rel="nofollow" class="{{if .NoLabel}}active selected {{end}}item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels=0&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_label_select_no_label"}}</a>
+		<a rel="nofollow" class="{{if .AllLabels}}active selected {{end}}item" href="?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_label_no_select"}}</a>
+		<a rel="nofollow" class="{{if .NoLabel}}active selected {{end}}item" href="?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels=0&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_label_select_no_label"}}</a>
 		{{$previousExclusiveScope := "_no_scope"}}
 		{{range .Labels}}
 			{{$exclusiveScope := .ExclusiveScope}}
@@ -32,7 +32,7 @@
 				<div class="divider"></div>
 			{{end}}
 			{{$previousExclusiveScope = $exclusiveScope}}
-			<a rel="nofollow" class="item label-filter-item gt-df gt-ac" {{if .IsArchived}}data-is-archived{{end}} href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{.QueryString}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}" data-label-id="{{.ID}}">
+			<a rel="nofollow" class="item label-filter-item gt-df gt-ac" {{if .IsArchived}}data-is-archived{{end}} href="?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{.QueryString}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}" data-label-id="{{.ID}}">
 				{{if .IsExcluded}}
 					{{svg "octicon-circle-slash"}}
 				{{else if .IsSelected}}
@@ -62,13 +62,13 @@
 			<input type="text" placeholder="{{ctx.Locale.Tr "repo.issues.filter_milestone"}}">
 		</div>
 		<div class="divider"></div>
-		<a rel="nofollow" class="{{if not $.MilestoneID}}active selected {{end}}item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone=0&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_milestone_all"}}</a>
-		<a rel="nofollow" class="{{if $.MilestoneID}}{{if eq $.MilestoneID -1}}active selected {{end}}{{end}}item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone=-1&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_milestone_none"}}</a>
+		<a rel="nofollow" class="{{if not $.MilestoneID}}active selected {{end}}item" href="?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone=0&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_milestone_all"}}</a>
+		<a rel="nofollow" class="{{if $.MilestoneID}}{{if eq $.MilestoneID -1}}active selected {{end}}{{end}}item" href="?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone=-1&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_milestone_none"}}</a>
 		{{if .OpenMilestones}}
 			<div class="divider"></div>
 			<div class="header">{{ctx.Locale.Tr "repo.issues.filter_milestone_open"}}</div>
 			{{range .OpenMilestones}}
-			<a rel="nofollow" class="{{if $.MilestoneID}}{{if eq $.MilestoneID .ID}}active selected {{end}}{{end}}item" href="{{$.Link}}?type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{$.SelectLabels}}&milestone={{.ID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">
+			<a rel="nofollow" class="{{if $.MilestoneID}}{{if eq $.MilestoneID .ID}}active selected {{end}}{{end}}item" href="?type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{$.SelectLabels}}&milestone={{.ID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">
 				{{svg "octicon-milestone" 16 "mr-2"}}
 				{{.Name}}
 			</a>
@@ -78,7 +78,7 @@
 			<div class="divider"></div>
 			<div class="header">{{ctx.Locale.Tr "repo.issues.filter_milestone_closed"}}</div>
 			{{range .ClosedMilestones}}
-			<a rel="nofollow" class="{{if $.MilestoneID}}{{if eq $.MilestoneID .ID}}active selected {{end}}{{end}}item" href="{{$.Link}}?type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{$.SelectLabels}}&milestone={{.ID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">
+			<a rel="nofollow" class="{{if $.MilestoneID}}{{if eq $.MilestoneID .ID}}active selected {{end}}{{end}}item" href="?type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{$.SelectLabels}}&milestone={{.ID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">
 				{{svg "octicon-milestone" 16 "mr-2"}}
 				{{.Name}}
 			</a>
@@ -99,15 +99,15 @@
 			<i class="icon">{{svg "octicon-search" 16}}</i>
 			<input type="text" placeholder="{{ctx.Locale.Tr "repo.issues.filter_project"}}">
 		</div>
-		<a rel="nofollow" class="{{if not .ProjectID}}active selected {{end}}item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_project_all"}}</a>
-		<a rel="nofollow" class="{{if eq .ProjectID -1}}active selected {{end}}item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&project=-1&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_project_none"}}</a>
+		<a rel="nofollow" class="{{if not .ProjectID}}active selected {{end}}item" href="?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_project_all"}}</a>
+		<a rel="nofollow" class="{{if eq .ProjectID -1}}active selected {{end}}item" href="?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&project=-1&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_project_none"}}</a>
 		{{if .OpenProjects}}
 			<div class="divider"></div>
 			<div class="header">
 				{{ctx.Locale.Tr "repo.issues.new.open_projects"}}
 			</div>
 			{{range .OpenProjects}}
-				<a rel="nofollow" class="{{if $.ProjectID}}{{if eq $.ProjectID .ID}}active selected{{end}}{{end}} item gt-df" href="{{$.Link}}?type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{$.SelectLabels}}&milestone={{$.MilestoneID}}&project={{.ID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">
+				<a rel="nofollow" class="{{if $.ProjectID}}{{if eq $.ProjectID .ID}}active selected{{end}}{{end}} item gt-df" href="?type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{$.SelectLabels}}&milestone={{$.MilestoneID}}&project={{.ID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">
 					{{svg .IconName 18 "gt-mr-3 tw-shrink-0"}}<span class="gt-ellipsis">{{.Title}}</span>
 				</a>
 			{{end}}
@@ -118,7 +118,7 @@
 				{{ctx.Locale.Tr "repo.issues.new.closed_projects"}}
 			</div>
 			{{range .ClosedProjects}}
-				<a rel="nofollow" class="{{if $.ProjectID}}{{if eq $.ProjectID .ID}}active selected{{end}}{{end}} item" href="{{$.Link}}?type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{$.SelectLabels}}&milestone={{$.MilestoneID}}&project={{.ID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">
+				<a rel="nofollow" class="{{if $.ProjectID}}{{if eq $.ProjectID .ID}}active selected{{end}}{{end}} item" href="?type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{$.SelectLabels}}&milestone={{$.MilestoneID}}&project={{.ID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">
 					{{svg .IconName 18 "gt-mr-3"}}{{.Title}}
 				</a>
 			{{end}}
@@ -130,7 +130,7 @@
 <div class="ui dropdown jump item user-remote-search" data-tooltip-content="{{ctx.Locale.Tr "repo.author_search_tooltip"}}"
 	data-search-url="{{if .Milestone}}{{$.RepoLink}}/issues/posters{{else}}{{$.Link}}/posters{{end}}"
 	data-selected-user-id="{{$.PosterID}}"
-	data-action-jump-url="{{$.Link}}?type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{$.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={user_id}{{if $.ShowArchivedLabels}}&archived=true{{end}}"
+	data-action-jump-url="?type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{$.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={user_id}{{if $.ShowArchivedLabels}}&archived=true{{end}}"
 >
 	<span class="text">
 		{{ctx.Locale.Tr "repo.issues.filter_poster"}}
@@ -156,11 +156,11 @@
 			<i class="icon">{{svg "octicon-search" 16}}</i>
 			<input type="text" placeholder="{{ctx.Locale.Tr "repo.issues.filter_assignee"}}">
 		</div>
-		<a rel="nofollow" class="{{if not .AssigneeID}}active selected {{end}}item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_assginee_no_select"}}</a>
-		<a rel="nofollow" class="{{if eq .AssigneeID -1}}active selected {{end}}item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee=-1&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_assginee_no_assignee"}}</a>
+		<a rel="nofollow" class="{{if not .AssigneeID}}active selected {{end}}item" href="?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_assginee_no_select"}}</a>
+		<a rel="nofollow" class="{{if eq .AssigneeID -1}}active selected {{end}}item" href="?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee=-1&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_assginee_no_assignee"}}</a>
 		<div class="divider"></div>
 		{{range .Assignees}}
-			<a rel="nofollow" class="{{if eq $.AssigneeID .ID}}active selected{{end}} item gt-df" href="{{$.Link}}?type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{$.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{.ID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">
+			<a rel="nofollow" class="{{if eq $.AssigneeID .ID}}active selected{{end}} item gt-df" href="?type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{$.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{.ID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">
 				{{ctx.AvatarUtils.Avatar . 20}}{{template "repo/search_name" .}}
 			</a>
 		{{end}}
@@ -175,14 +175,14 @@
 		</span>
 		{{svg "octicon-triangle-down" 14 "dropdown icon"}}
 		<div class="menu">
-			<a rel="nofollow" class="{{if eq .ViewType "all"}}active {{end}}item" href="{{$.Link}}?q={{$.Keyword}}&type=all&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_type.all_issues"}}</a>
-			<a rel="nofollow" class="{{if eq .ViewType "assigned"}}active {{end}}item" href="{{$.Link}}?q={{$.Keyword}}&type=assigned&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_type.assigned_to_you"}}</a>
-			<a rel="nofollow" class="{{if eq .ViewType "created_by"}}active {{end}}item" href="{{$.Link}}?q={{$.Keyword}}&type=created_by&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_type.created_by_you"}}</a>
+			<a rel="nofollow" class="{{if eq .ViewType "all"}}active {{end}}item" href="?q={{$.Keyword}}&type=all&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_type.all_issues"}}</a>
+			<a rel="nofollow" class="{{if eq .ViewType "assigned"}}active {{end}}item" href="?q={{$.Keyword}}&type=assigned&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_type.assigned_to_you"}}</a>
+			<a rel="nofollow" class="{{if eq .ViewType "created_by"}}active {{end}}item" href="?q={{$.Keyword}}&type=created_by&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_type.created_by_you"}}</a>
 			{{if .PageIsPullList}}
-				<a rel="nofollow" class="{{if eq .ViewType "review_requested"}}active {{end}}item" href="{{$.Link}}?q={{$.Keyword}}&type=review_requested&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_type.review_requested"}}</a>
-				<a rel="nofollow" class="{{if eq .ViewType "reviewed_by"}}active {{end}}item" href="{{$.Link}}?q={{$.Keyword}}&type=reviewed_by&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_type.reviewed_by_you"}}</a>
+				<a rel="nofollow" class="{{if eq .ViewType "review_requested"}}active {{end}}item" href="?q={{$.Keyword}}&type=review_requested&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_type.review_requested"}}</a>
+				<a rel="nofollow" class="{{if eq .ViewType "reviewed_by"}}active {{end}}item" href="?q={{$.Keyword}}&type=reviewed_by&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_type.reviewed_by_you"}}</a>
 			{{end}}
-			<a rel="nofollow" class="{{if eq .ViewType "mentioned"}}active {{end}}item" href="{{$.Link}}?q={{$.Keyword}}&type=mentioned&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_type.mentioning_you"}}</a>
+			<a rel="nofollow" class="{{if eq .ViewType "mentioned"}}active {{end}}item" href="?q={{$.Keyword}}&type=mentioned&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_type.mentioning_you"}}</a>
 		</div>
 	</div>
 {{end}}
@@ -194,13 +194,13 @@
 	</span>
 	{{svg "octicon-triangle-down" 14 "dropdown icon"}}
 	<div class="menu">
-		<a rel="nofollow" class="{{if or (eq .SortType "latest") (not .SortType)}}active {{end}}item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort=latest&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_sort.latest"}}</a>
-		<a rel="nofollow" class="{{if eq .SortType "oldest"}}active {{end}}item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort=oldest&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_sort.oldest"}}</a>
-		<a rel="nofollow" class="{{if eq .SortType "recentupdate"}}active {{end}}item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort=recentupdate&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_sort.recentupdate"}}</a>
-		<a rel="nofollow" class="{{if eq .SortType "leastupdate"}}active {{end}}item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort=leastupdate&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_sort.leastupdate"}}</a>
-		<a rel="nofollow" class="{{if eq .SortType "mostcomment"}}active {{end}}item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort=mostcomment&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_sort.mostcomment"}}</a>
-		<a rel="nofollow" class="{{if eq .SortType "leastcomment"}}active {{end}}item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort=leastcomment&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_sort.leastcomment"}}</a>
-		<a rel="nofollow" class="{{if eq .SortType "nearduedate"}}active {{end}}item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort=nearduedate&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_sort.nearduedate"}}</a>
-		<a rel="nofollow" class="{{if eq .SortType "farduedate"}}active {{end}}item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort=farduedate&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_sort.farduedate"}}</a>
+		<a rel="nofollow" class="{{if or (eq .SortType "latest") (not .SortType)}}active {{end}}item" href="?q={{$.Keyword}}&type={{$.ViewType}}&sort=latest&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_sort.latest"}}</a>
+		<a rel="nofollow" class="{{if eq .SortType "oldest"}}active {{end}}item" href="?q={{$.Keyword}}&type={{$.ViewType}}&sort=oldest&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_sort.oldest"}}</a>
+		<a rel="nofollow" class="{{if eq .SortType "recentupdate"}}active {{end}}item" href="?q={{$.Keyword}}&type={{$.ViewType}}&sort=recentupdate&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_sort.recentupdate"}}</a>
+		<a rel="nofollow" class="{{if eq .SortType "leastupdate"}}active {{end}}item" href="?q={{$.Keyword}}&type={{$.ViewType}}&sort=leastupdate&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_sort.leastupdate"}}</a>
+		<a rel="nofollow" class="{{if eq .SortType "mostcomment"}}active {{end}}item" href="?q={{$.Keyword}}&type={{$.ViewType}}&sort=mostcomment&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_sort.mostcomment"}}</a>
+		<a rel="nofollow" class="{{if eq .SortType "leastcomment"}}active {{end}}item" href="?q={{$.Keyword}}&type={{$.ViewType}}&sort=leastcomment&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_sort.leastcomment"}}</a>
+		<a rel="nofollow" class="{{if eq .SortType "nearduedate"}}active {{end}}item" href="?q={{$.Keyword}}&type={{$.ViewType}}&sort=nearduedate&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_sort.nearduedate"}}</a>
+		<a rel="nofollow" class="{{if eq .SortType "farduedate"}}active {{end}}item" href="?q={{$.Keyword}}&type={{$.ViewType}}&sort=farduedate&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_sort.farduedate"}}</a>
 	</div>
 </div>
diff --git a/templates/repo/issue/labels/label_list.tmpl b/templates/repo/issue/labels/label_list.tmpl
index 284c0d3bf6..fbfc885880 100644
--- a/templates/repo/issue/labels/label_list.tmpl
+++ b/templates/repo/issue/labels/label_list.tmpl
@@ -9,10 +9,10 @@
 				</span>
 				{{svg "octicon-triangle-down" 14 "dropdown icon"}}
 				<div class="left menu">
-					<a class="{{if or (eq .SortType "alphabetically") (not .SortType)}}active {{end}}item" href="{{$.Link}}?sort=alphabetically&state={{$.State}}">{{ctx.Locale.Tr "repo.issues.label.filter_sort.alphabetically"}}</a>
-					<a class="{{if eq .SortType "reversealphabetically"}}active {{end}}item" href="{{$.Link}}?sort=reversealphabetically&state={{$.State}}">{{ctx.Locale.Tr "repo.issues.label.filter_sort.reverse_alphabetically"}}</a>
-					<a class="{{if eq .SortType "leastissues"}}active {{end}}item" href="{{$.Link}}?sort=leastissues&state={{$.State}}">{{ctx.Locale.Tr "repo.milestones.filter_sort.least_issues"}}</a>
-					<a class="{{if eq .SortType "mostissues"}}active {{end}}item" href="{{$.Link}}?sort=mostissues&state={{$.State}}">{{ctx.Locale.Tr "repo.milestones.filter_sort.most_issues"}}</a>
+					<a class="{{if or (eq .SortType "alphabetically") (not .SortType)}}active {{end}}item" href="?sort=alphabetically&state={{$.State}}">{{ctx.Locale.Tr "repo.issues.label.filter_sort.alphabetically"}}</a>
+					<a class="{{if eq .SortType "reversealphabetically"}}active {{end}}item" href="?sort=reversealphabetically&state={{$.State}}">{{ctx.Locale.Tr "repo.issues.label.filter_sort.reverse_alphabetically"}}</a>
+					<a class="{{if eq .SortType "leastissues"}}active {{end}}item" href="?sort=leastissues&state={{$.State}}">{{ctx.Locale.Tr "repo.milestones.filter_sort.least_issues"}}</a>
+					<a class="{{if eq .SortType "mostissues"}}active {{end}}item" href="?sort=mostissues&state={{$.State}}">{{ctx.Locale.Tr "repo.milestones.filter_sort.most_issues"}}</a>
 				</div>
 			</div>
 		</div>
diff --git a/templates/repo/issue/milestone/filter_list.tmpl b/templates/repo/issue/milestone/filter_list.tmpl
index 0eea42d6ee..45f9866a16 100644
--- a/templates/repo/issue/milestone/filter_list.tmpl
+++ b/templates/repo/issue/milestone/filter_list.tmpl
@@ -5,11 +5,11 @@
 	</span>
 	{{svg "octicon-triangle-down" 14 "dropdown icon"}}
 	<div class="menu">
-		<a class="{{if or (eq .SortType "closestduedate") (not .SortType)}}active {{end}}item" href="{{$.Link}}?sort=closestduedate&state={{$.State}}&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.milestones.filter_sort.earliest_due_data"}}</a>
-		<a class="{{if eq .SortType "furthestduedate"}}active {{end}}item" href="{{$.Link}}?sort=furthestduedate&state={{$.State}}&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.milestones.filter_sort.latest_due_date"}}</a>
-		<a class="{{if eq .SortType "leastcomplete"}}active {{end}}item" href="{{$.Link}}?sort=leastcomplete&state={{$.State}}&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.milestones.filter_sort.least_complete"}}</a>
-		<a class="{{if eq .SortType "mostcomplete"}}active {{end}}item" href="{{$.Link}}?sort=mostcomplete&state={{$.State}}&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.milestones.filter_sort.most_complete"}}</a>
-		<a class="{{if eq .SortType "mostissues"}}active {{end}}item" href="{{$.Link}}?sort=mostissues&state={{$.State}}&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.milestones.filter_sort.most_issues"}}</a>
-		<a class="{{if eq .SortType "leastissues"}}active {{end}}item" href="{{$.Link}}?sort=leastissues&state={{$.State}}&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.milestones.filter_sort.least_issues"}}</a>
+		<a class="{{if or (eq .SortType "closestduedate") (not .SortType)}}active {{end}}item" href="?sort=closestduedate&state={{$.State}}&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.milestones.filter_sort.earliest_due_data"}}</a>
+		<a class="{{if eq .SortType "furthestduedate"}}active {{end}}item" href="?sort=furthestduedate&state={{$.State}}&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.milestones.filter_sort.latest_due_date"}}</a>
+		<a class="{{if eq .SortType "leastcomplete"}}active {{end}}item" href="?sort=leastcomplete&state={{$.State}}&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.milestones.filter_sort.least_complete"}}</a>
+		<a class="{{if eq .SortType "mostcomplete"}}active {{end}}item" href="?sort=mostcomplete&state={{$.State}}&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.milestones.filter_sort.most_complete"}}</a>
+		<a class="{{if eq .SortType "mostissues"}}active {{end}}item" href="?sort=mostissues&state={{$.State}}&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.milestones.filter_sort.most_issues"}}</a>
+		<a class="{{if eq .SortType "leastissues"}}active {{end}}item" href="?sort=leastissues&state={{$.State}}&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.milestones.filter_sort.least_issues"}}</a>
 	</div>
 </div>
diff --git a/templates/repo/view_file.tmpl b/templates/repo/view_file.tmpl
index 851e67db63..9d116c7b46 100644
--- a/templates/repo/view_file.tmpl
+++ b/templates/repo/view_file.tmpl
@@ -37,7 +37,7 @@
 		<div class="file-header-right file-actions gt-df gt-ac gt-fw">
 			{{if .HasSourceRenderedToggle}}
 				<div class="ui compact icon buttons">
-					<a href="{{$.Link}}?display=source" class="ui mini basic button {{if .IsDisplayingSource}}active{{end}}" data-tooltip-content="{{ctx.Locale.Tr "repo.file_view_source"}}">{{svg "octicon-code" 15}}</a>
+					<a href="?display=source" class="ui mini basic button {{if .IsDisplayingSource}}active{{end}}" data-tooltip-content="{{ctx.Locale.Tr "repo.file_view_source"}}">{{svg "octicon-code" 15}}</a>
 					<a href="{{$.Link}}" class="ui mini basic button {{if .IsDisplayingRendered}}active{{end}}" data-tooltip-content="{{ctx.Locale.Tr "repo.file_view_rendered"}}">{{svg "octicon-file" 15}}</a>
 				</div>
 			{{end}}
diff --git a/templates/shared/issuelist.tmpl b/templates/shared/issuelist.tmpl
index 2969efa6be..639221abb9 100644
--- a/templates/shared/issuelist.tmpl
+++ b/templates/shared/issuelist.tmpl
@@ -21,7 +21,7 @@
 						{{end}}
 						<span class="labels-list gt-ml-2">
 							{{range .Labels}}
-								<a href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&state={{$.State}}&labels={{.ID}}{{if ne $.listType "milestone"}}&milestone={{$.MilestoneID}}{{end}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{RenderLabel $.Context .}}</a>
+								<a href="?q={{$.Keyword}}&type={{$.ViewType}}&state={{$.State}}&labels={{.ID}}{{if ne $.listType "milestone"}}&milestone={{$.MilestoneID}}{{end}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{RenderLabel $.Context .}}</a>
 							{{end}}
 						</span>
 					</div>
diff --git a/templates/shared/search/code/results.tmpl b/templates/shared/search/code/results.tmpl
index 02d1252770..11d7fbbd6b 100644
--- a/templates/shared/search/code/results.tmpl
+++ b/templates/shared/search/code/results.tmpl
@@ -1,7 +1,7 @@
 <div class="flex-text-block gt-fw">
 	{{range $term := .SearchResultLanguages}}
 	<a class="ui {{if eq $.Language $term.Language}}primary{{end}} basic label gt-m-0"
-		href="{{$.Link}}?q={{$.Keyword}}{{if ne $.Language $term.Language}}&l={{$term.Language}}{{end}}&fuzzy={{$.IsFuzzy}}">
+		href="?q={{$.Keyword}}{{if ne $.Language $term.Language}}&l={{$term.Language}}{{end}}&fuzzy={{$.IsFuzzy}}">
 		<i class="color-icon gt-mr-3" style="background-color: {{$term.Color}}"></i>
 		{{$term.Language}}
 		<div class="detail">{{$term.Count}}</div>
diff --git a/templates/user/dashboard/issues.tmpl b/templates/user/dashboard/issues.tmpl
index 0fbf9a7361..c9972f9426 100644
--- a/templates/user/dashboard/issues.tmpl
+++ b/templates/user/dashboard/issues.tmpl
@@ -62,14 +62,14 @@
 							{{svg "octicon-triangle-down" 14 "dropdown icon"}}
 						</span>
 						<div class="menu">
-							<a class="{{if eq .SortType "recentupdate"}}active {{end}}item" href="{{$.Link}}?type={{$.ViewType}}&sort=recentupdate&state={{$.State}}&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.issues.filter_sort.recentupdate"}}</a>
-							<a class="{{if eq .SortType "leastupdate"}}active {{end}}item" href="{{$.Link}}?type={{$.ViewType}}&sort=leastupdate&state={{$.State}}&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.issues.filter_sort.leastupdate"}}</a>
-							<a class="{{if or (eq .SortType "latest") (not .SortType)}}active {{end}}item" href="{{$.Link}}?type={{$.ViewType}}&sort=latest&state={{$.State}}&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.issues.filter_sort.latest"}}</a>
-							<a class="{{if eq .SortType "oldest"}}active {{end}}item" href="{{$.Link}}?type={{$.ViewType}}&sort=oldest&state={{$.State}}&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.issues.filter_sort.oldest"}}</a>
-							<a class="{{if eq .SortType "mostcomment"}}active {{end}}item" href="{{$.Link}}?type={{$.ViewType}}&sort=mostcomment&state={{$.State}}&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.issues.filter_sort.mostcomment"}}</a>
-							<a class="{{if eq .SortType "leastcomment"}}active {{end}}item" href="{{$.Link}}?type={{$.ViewType}}&sort=leastcomment&state={{$.State}}&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.issues.filter_sort.leastcomment"}}</a>
-							<a class="{{if eq .SortType "nearduedate"}}active {{end}}item" href="{{$.Link}}?type={{$.ViewType}}&sort=nearduedate&state={{$.State}}&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.issues.filter_sort.nearduedate"}}</a>
-							<a class="{{if eq .SortType "farduedate"}}active {{end}}item" href="{{$.Link}}?type={{$.ViewType}}&sort=farduedate&state={{$.State}}&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.issues.filter_sort.farduedate"}}</a>
+							<a class="{{if eq .SortType "recentupdate"}}active {{end}}item" href="?type={{$.ViewType}}&sort=recentupdate&state={{$.State}}&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.issues.filter_sort.recentupdate"}}</a>
+							<a class="{{if eq .SortType "leastupdate"}}active {{end}}item" href="?type={{$.ViewType}}&sort=leastupdate&state={{$.State}}&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.issues.filter_sort.leastupdate"}}</a>
+							<a class="{{if or (eq .SortType "latest") (not .SortType)}}active {{end}}item" href="?type={{$.ViewType}}&sort=latest&state={{$.State}}&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.issues.filter_sort.latest"}}</a>
+							<a class="{{if eq .SortType "oldest"}}active {{end}}item" href="?type={{$.ViewType}}&sort=oldest&state={{$.State}}&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.issues.filter_sort.oldest"}}</a>
+							<a class="{{if eq .SortType "mostcomment"}}active {{end}}item" href="?type={{$.ViewType}}&sort=mostcomment&state={{$.State}}&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.issues.filter_sort.mostcomment"}}</a>
+							<a class="{{if eq .SortType "leastcomment"}}active {{end}}item" href="?type={{$.ViewType}}&sort=leastcomment&state={{$.State}}&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.issues.filter_sort.leastcomment"}}</a>
+							<a class="{{if eq .SortType "nearduedate"}}active {{end}}item" href="?type={{$.ViewType}}&sort=nearduedate&state={{$.State}}&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.issues.filter_sort.nearduedate"}}</a>
+							<a class="{{if eq .SortType "farduedate"}}active {{end}}item" href="?type={{$.ViewType}}&sort=farduedate&state={{$.State}}&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.issues.filter_sort.farduedate"}}</a>
 						</div>
 					</div>
 				</div>
diff --git a/templates/user/dashboard/milestones.tmpl b/templates/user/dashboard/milestones.tmpl
index 7b62c9fc27..944043e806 100644
--- a/templates/user/dashboard/milestones.tmpl
+++ b/templates/user/dashboard/milestones.tmpl
@@ -12,7 +12,7 @@
 					<div class="divider"></div>
 					{{range .Repos}}
 						{{with $Repo := .}}
-							<a class="{{range $.RepoIDs}}{{if eq . $Repo.ID}}active{{end}}{{end}} repo name item" href="{{$.Link}}?repos=[
+							<a class="{{range $.RepoIDs}}{{if eq . $Repo.ID}}active{{end}}{{end}} repo name item" href="?repos=[
 								{{- with $include := true -}}
 										{{- range $.RepoIDs -}}
 											{{- if eq . $Repo.ID -}}
@@ -59,12 +59,12 @@
 						</span>
 						{{svg "octicon-triangle-down" 14 "dropdown icon"}}
 						<div class="menu">
-							<a class="{{if or (eq .SortType "closestduedate") (not .SortType)}}active {{end}}item" href="{{$.Link}}?repos=[{{range $.RepoIDs}}{{.}}%2C{{end}}]&sort=closestduedate&state={{$.State}}&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.milestones.filter_sort.earliest_due_data"}}</a>
-							<a class="{{if eq .SortType "furthestduedate"}}active {{end}}item" href="{{$.Link}}?repos=[{{range $.RepoIDs}}{{.}}%2C{{end}}]&sort=furthestduedate&state={{$.State}}&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.milestones.filter_sort.latest_due_date"}}</a>
-							<a class="{{if eq .SortType "leastcomplete"}}active {{end}}item" href="{{$.Link}}?repos=[{{range $.RepoIDs}}{{.}}%2C{{end}}]&sort=leastcomplete&state={{$.State}}&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.milestones.filter_sort.least_complete"}}</a>
-							<a class="{{if eq .SortType "mostcomplete"}}active {{end}}item" href="{{$.Link}}?repos=[{{range $.RepoIDs}}{{.}}%2C{{end}}]&sort=mostcomplete&state={{$.State}}&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.milestones.filter_sort.most_complete"}}</a>
-							<a class="{{if eq .SortType "mostissues"}}active {{end}}item" href="{{$.Link}}?repos=[{{range $.RepoIDs}}{{.}}%2C{{end}}]&sort=mostissues&state={{$.State}}&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.milestones.filter_sort.most_issues"}}</a>
-							<a class="{{if eq .SortType "leastissues"}}active {{end}}item" href="{{$.Link}}?repos=[{{range $.RepoIDs}}{{.}}%2C{{end}}]&sort=leastissues&state={{$.State}}&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.milestones.filter_sort.least_issues"}}</a>
+							<a class="{{if or (eq .SortType "closestduedate") (not .SortType)}}active {{end}}item" href="?repos=[{{range $.RepoIDs}}{{.}}%2C{{end}}]&sort=closestduedate&state={{$.State}}&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.milestones.filter_sort.earliest_due_data"}}</a>
+							<a class="{{if eq .SortType "furthestduedate"}}active {{end}}item" href="?repos=[{{range $.RepoIDs}}{{.}}%2C{{end}}]&sort=furthestduedate&state={{$.State}}&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.milestones.filter_sort.latest_due_date"}}</a>
+							<a class="{{if eq .SortType "leastcomplete"}}active {{end}}item" href="?repos=[{{range $.RepoIDs}}{{.}}%2C{{end}}]&sort=leastcomplete&state={{$.State}}&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.milestones.filter_sort.least_complete"}}</a>
+							<a class="{{if eq .SortType "mostcomplete"}}active {{end}}item" href="?repos=[{{range $.RepoIDs}}{{.}}%2C{{end}}]&sort=mostcomplete&state={{$.State}}&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.milestones.filter_sort.most_complete"}}</a>
+							<a class="{{if eq .SortType "mostissues"}}active {{end}}item" href="?repos=[{{range $.RepoIDs}}{{.}}%2C{{end}}]&sort=mostissues&state={{$.State}}&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.milestones.filter_sort.most_issues"}}</a>
+							<a class="{{if eq .SortType "leastissues"}}active {{end}}item" href="?repos=[{{range $.RepoIDs}}{{.}}%2C{{end}}]&sort=leastissues&state={{$.State}}&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.milestones.filter_sort.least_issues"}}</a>
 						</div>
 					</div>
 				</div>
diff --git a/templates/user/notification/notification_subscriptions.tmpl b/templates/user/notification/notification_subscriptions.tmpl
index a37f0c352e..d39e628263 100644
--- a/templates/user/notification/notification_subscriptions.tmpl
+++ b/templates/user/notification/notification_subscriptions.tmpl
@@ -14,14 +14,14 @@
 				<div class="gt-df gt-sb">
 					<div class="gt-df">
 						<div class="small-menu-items ui compact tiny menu">
-							<a class="{{if eq .State "all"}}active {{end}}item" href="{{$.Link}}?sort={{$.SortType}}&state=all&issueType={{$.IssueType}}&labels={{$.Labels}}">
+							<a class="{{if eq .State "all"}}active {{end}}item" href="?sort={{$.SortType}}&state=all&issueType={{$.IssueType}}&labels={{$.Labels}}">
 								{{ctx.Locale.Tr "all"}}
 							</a>
-							<a class="{{if eq .State "open"}}active {{end}}item" href="{{$.Link}}?sort={{$.SortType}}&state=open&issueType={{$.IssueType}}&labels={{$.Labels}}">
+							<a class="{{if eq .State "open"}}active {{end}}item" href="?sort={{$.SortType}}&state=open&issueType={{$.IssueType}}&labels={{$.Labels}}">
 								{{svg "octicon-issue-opened" 16 "gt-mr-3"}}
 								{{ctx.Locale.Tr "repo.issues.open_title"}}
 							</a>
-							<a class="{{if eq .State "closed"}}active {{end}}item" href="{{$.Link}}?sort={{$.SortType}}&state=closed&issueType={{$.IssueType}}&labels={{$.Labels}}">
+							<a class="{{if eq .State "closed"}}active {{end}}item" href="?sort={{$.SortType}}&state=closed&issueType={{$.IssueType}}&labels={{$.Labels}}">
 								{{svg "octicon-issue-closed" 16 "gt-mr-3"}}
 								{{ctx.Locale.Tr "repo.issues.closed_title"}}
 							</a>
@@ -36,9 +36,9 @@
 									</span>
 									{{svg "octicon-triangle-down" 14 "dropdown icon"}}
 									<div class="menu">
-										<a class="{{if or (eq .IssueType "all") (not .IssueType)}}active {{end}}item" href="{{$.Link}}?sort={{$.SortType}}&state={{$.State}}&issueType=all&labels={{$.Labels}}">{{ctx.Locale.Tr "all"}}</a>
-										<a class="{{if eq .IssueType "issues"}}active {{end}}item" href="{{$.Link}}?sort={{$.SortType}}&state={{$.State}}&issueType=issues&labels={{$.Labels}}">{{ctx.Locale.Tr "issues"}}</a>
-										<a class="{{if eq .IssueType "pulls"}}active {{end}}item" href="{{$.Link}}?sort={{$.SortType}}&state={{$.State}}&issueType=pulls&labels={{$.Labels}}">{{ctx.Locale.Tr "pull_requests"}}</a>
+										<a class="{{if or (eq .IssueType "all") (not .IssueType)}}active {{end}}item" href="?sort={{$.SortType}}&state={{$.State}}&issueType=all&labels={{$.Labels}}">{{ctx.Locale.Tr "all"}}</a>
+										<a class="{{if eq .IssueType "issues"}}active {{end}}item" href="?sort={{$.SortType}}&state={{$.State}}&issueType=issues&labels={{$.Labels}}">{{ctx.Locale.Tr "issues"}}</a>
+										<a class="{{if eq .IssueType "pulls"}}active {{end}}item" href="?sort={{$.SortType}}&state={{$.State}}&issueType=pulls&labels={{$.Labels}}">{{ctx.Locale.Tr "pull_requests"}}</a>
 									</div>
 								</div>
 
@@ -49,14 +49,14 @@
 								</span>
 								{{svg "octicon-triangle-down" 14 "dropdown icon"}}
 								<div class="menu">
-									<a class="{{if or (eq .SortType "latest") (not .SortType)}}active {{end}}item" href="{{$.Link}}?sort=latest&state={{$.State}}&issueType={{$.IssueType}}&labels={{$.Labels}}">{{ctx.Locale.Tr "repo.issues.filter_sort.latest"}}</a>
-									<a class="{{if eq .SortType "oldest"}}active {{end}}item" href="{{$.Link}}?sort=oldest&state={{$.State}}&issueType={{$.IssueType}}&labels={{$.Labels}}">{{ctx.Locale.Tr "repo.issues.filter_sort.oldest"}}</a>
-									<a class="{{if eq .SortType "recentupdate"}}active {{end}}item" href="{{$.Link}}?sort=recentupdate&state={{$.State}}&issueType={{$.IssueType}}&labels={{$.Labels}}">{{ctx.Locale.Tr "repo.issues.filter_sort.recentupdate"}}</a>
-									<a class="{{if eq .SortType "leastupdate"}}active {{end}}item" href="{{$.Link}}?sort=leastupdate&state={{$.State}}&issueType={{$.IssueType}}&labels={{$.Labels}}">{{ctx.Locale.Tr "repo.issues.filter_sort.leastupdate"}}</a>
-									<a class="{{if eq .SortType "mostcomment"}}active {{end}}item" href="{{$.Link}}?sort=mostcomment&state={{$.State}}&issueType={{$.IssueType}}&labels={{$.Labels}}">{{ctx.Locale.Tr "repo.issues.filter_sort.mostcomment"}}</a>
-									<a class="{{if eq .SortType "leastcomment"}}active {{end}}item" href="{{$.Link}}?sort=leastcomment&state={{$.State}}&issueType={{$.IssueType}}&labels={{$.Labels}}">{{ctx.Locale.Tr "repo.issues.filter_sort.leastcomment"}}</a>
-									<a class="{{if eq .SortType "nearduedate"}}active {{end}}item" href="{{$.Link}}?sort=nearduedate&state={{$.State}}&issueType={{$.IssueType}}&labels={{$.Labels}}">{{ctx.Locale.Tr "repo.issues.filter_sort.nearduedate"}}</a>
-									<a class="{{if eq .SortType "farduedate"}}active {{end}}item" href="{{$.Link}}?sort=farduedate&state={{$.State}}&issueType={{$.IssueType}}&labels={{$.Labels}}">{{ctx.Locale.Tr "repo.issues.filter_sort.farduedate"}}</a>
+									<a class="{{if or (eq .SortType "latest") (not .SortType)}}active {{end}}item" href="?sort=latest&state={{$.State}}&issueType={{$.IssueType}}&labels={{$.Labels}}">{{ctx.Locale.Tr "repo.issues.filter_sort.latest"}}</a>
+									<a class="{{if eq .SortType "oldest"}}active {{end}}item" href="?sort=oldest&state={{$.State}}&issueType={{$.IssueType}}&labels={{$.Labels}}">{{ctx.Locale.Tr "repo.issues.filter_sort.oldest"}}</a>
+									<a class="{{if eq .SortType "recentupdate"}}active {{end}}item" href="?sort=recentupdate&state={{$.State}}&issueType={{$.IssueType}}&labels={{$.Labels}}">{{ctx.Locale.Tr "repo.issues.filter_sort.recentupdate"}}</a>
+									<a class="{{if eq .SortType "leastupdate"}}active {{end}}item" href="?sort=leastupdate&state={{$.State}}&issueType={{$.IssueType}}&labels={{$.Labels}}">{{ctx.Locale.Tr "repo.issues.filter_sort.leastupdate"}}</a>
+									<a class="{{if eq .SortType "mostcomment"}}active {{end}}item" href="?sort=mostcomment&state={{$.State}}&issueType={{$.IssueType}}&labels={{$.Labels}}">{{ctx.Locale.Tr "repo.issues.filter_sort.mostcomment"}}</a>
+									<a class="{{if eq .SortType "leastcomment"}}active {{end}}item" href="?sort=leastcomment&state={{$.State}}&issueType={{$.IssueType}}&labels={{$.Labels}}">{{ctx.Locale.Tr "repo.issues.filter_sort.leastcomment"}}</a>
+									<a class="{{if eq .SortType "nearduedate"}}active {{end}}item" href="?sort=nearduedate&state={{$.State}}&issueType={{$.IssueType}}&labels={{$.Labels}}">{{ctx.Locale.Tr "repo.issues.filter_sort.nearduedate"}}</a>
+									<a class="{{if eq .SortType "farduedate"}}active {{end}}item" href="?sort=farduedate&state={{$.State}}&issueType={{$.IssueType}}&labels={{$.Labels}}">{{ctx.Locale.Tr "repo.issues.filter_sort.farduedate"}}</a>
 								</div>
 							</div>
 						</div>
diff --git a/templates/user/settings/keys_gpg.tmpl b/templates/user/settings/keys_gpg.tmpl
index 2e26d69561..b6bae89512 100644
--- a/templates/user/settings/keys_gpg.tmpl
+++ b/templates/user/settings/keys_gpg.tmpl
@@ -73,7 +73,7 @@
 						{{ctx.Locale.Tr "settings.delete_key"}}
 					</button>
 					{{if and (not .Verified) (ne $.VerifyingID .KeyID)}}
-						<a class="ui primary tiny button" href="{{$.Link}}?verify_gpg={{.KeyID}}">{{ctx.Locale.Tr "settings.gpg_key_verify"}}</a>
+						<a class="ui primary tiny button" href="?verify_gpg={{.KeyID}}">{{ctx.Locale.Tr "settings.gpg_key_verify"}}</a>
 					{{end}}
 				</div>
 			</div>
diff --git a/templates/user/settings/keys_ssh.tmpl b/templates/user/settings/keys_ssh.tmpl
index 6aa803a418..5c14d81bef 100644
--- a/templates/user/settings/keys_ssh.tmpl
+++ b/templates/user/settings/keys_ssh.tmpl
@@ -61,7 +61,7 @@
 						{{ctx.Locale.Tr "settings.delete_key"}}
 					</button>
 					{{if and (not .Verified) (ne $.VerifyingFingerprint .Fingerprint)}}
-						<a class="ui primary tiny button" href="{{$.Link}}?verify_ssh={{.Fingerprint}}">{{ctx.Locale.Tr "settings.ssh_key_verify"}}</a>
+						<a class="ui primary tiny button" href="?verify_ssh={{.Fingerprint}}">{{ctx.Locale.Tr "settings.ssh_key_verify"}}</a>
 					{{end}}
 				</div>
 			</div>
diff --git a/tests/integration/explore_user_test.go b/tests/integration/explore_user_test.go
index 046caf378e..441d89cea5 100644
--- a/tests/integration/explore_user_test.go
+++ b/tests/integration/explore_user_test.go
@@ -16,17 +16,17 @@ func TestExploreUser(t *testing.T) {
 	defer tests.PrepareTestEnv(t)()
 
 	cases := []struct{ sortOrder, expected string }{
-		{"", "/explore/users?sort=newest&q="},
-		{"newest", "/explore/users?sort=newest&q="},
-		{"oldest", "/explore/users?sort=oldest&q="},
-		{"alphabetically", "/explore/users?sort=alphabetically&q="},
-		{"reversealphabetically", "/explore/users?sort=reversealphabetically&q="},
+		{"", "?sort=newest&q="},
+		{"newest", "?sort=newest&q="},
+		{"oldest", "?sort=oldest&q="},
+		{"alphabetically", "?sort=alphabetically&q="},
+		{"reversealphabetically", "?sort=reversealphabetically&q="},
 	}
 	for _, c := range cases {
 		req := NewRequest(t, "GET", "/explore/users?sort="+c.sortOrder)
 		resp := MakeRequest(t, req, http.StatusOK)
 		h := NewHTMLParser(t, resp.Body)
-		href, _ := h.Find(`.ui.dropdown .menu a.active.item[href^="/explore/users"]`).Attr("href")
+		href, _ := h.Find(`.ui.dropdown .menu a.active.item[href^="?sort="]`).Attr("href")
 		assert.Equal(t, c.expected, href)
 	}
 

From 4061cb32f7aac2fb7d094c9868993a841693e0c5 Mon Sep 17 00:00:00 2001
From: Earl Warren <contact@earl-warren.org>
Date: Mon, 25 Mar 2024 07:13:11 +0100
Subject: [PATCH 26/74] Remove unnecessary ".Link" usages (#29909) (part 2)

5143ebb507 Add rel="nofollow" to issue filter links

has a test that fails because it assumes the link starts with the link
where it now starts with a ?
---
 tests/integration/issue_test.go | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tests/integration/issue_test.go b/tests/integration/issue_test.go
index 6c1da4f4a5..38c3a091b5 100644
--- a/tests/integration/issue_test.go
+++ b/tests/integration/issue_test.go
@@ -807,7 +807,7 @@ func TestIssueFilterNoFollow(t *testing.T) {
 	htmlDoc := NewHTMLParser(t, resp.Body)
 
 	// Check that every link in the filter list has rel="nofollow".
-	filterLinks := htmlDoc.Find(".issue-list-toolbar-right a[href*=\"/issues?q=\"]")
+	filterLinks := htmlDoc.Find(".issue-list-toolbar-right a[href*=\"?q=\"]")
 	assert.True(t, filterLinks.Length() > 0)
 	filterLinks.Each(func(i int, link *goquery.Selection) {
 		rel, has := link.Attr("rel")

From 4f8763c4fdc8020ac8c3f69c2c7c61b48fe21f60 Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Wed, 20 Mar 2024 14:58:10 +0800
Subject: [PATCH 27/74] Remove unnecessary ".Link" usages (#29929)

Follow #29909

(cherry picked from commit 91699a9bb1fc59029a2605912f1e380eff7297fa)
---
 templates/admin/notice.tmpl              |  2 +-
 templates/admin/stacktrace.tmpl          |  4 ++--
 templates/repo/wiki/new.tmpl             |  2 +-
 templates/user/auth/reset_passwd.tmpl    |  2 +-
 templates/user/dashboard/issues.tmpl     | 16 ++++++++--------
 templates/user/dashboard/milestones.tmpl |  4 ++--
 6 files changed, 15 insertions(+), 15 deletions(-)

diff --git a/templates/admin/notice.tmpl b/templates/admin/notice.tmpl
index e0abe4f8c0..26462596bc 100644
--- a/templates/admin/notice.tmpl
+++ b/templates/admin/notice.tmpl
@@ -49,7 +49,7 @@
 										</div>
 									</div>
 								</div>
-								<button class="ui small teal button" id="delete-selection" data-link="{{.Link}}/delete" data-redirect="{{.Link}}?page={{.Page.Paginater.Current}}">
+								<button class="ui small teal button" id="delete-selection" data-link="{{.Link}}/delete" data-redirect="?page={{.Page.Paginater.Current}}">
 									{{ctx.Locale.Tr "admin.notices.delete_selected"}}
 								</button>
 							</th>
diff --git a/templates/admin/stacktrace.tmpl b/templates/admin/stacktrace.tmpl
index 42944615c3..950aa0ea86 100644
--- a/templates/admin/stacktrace.tmpl
+++ b/templates/admin/stacktrace.tmpl
@@ -4,8 +4,8 @@
 	<div class="gt-df gt-ac">
 		<div class="gt-f1">
 			<div class="ui compact small menu">
-				<a class="{{if eq .ShowGoroutineList "process"}}active {{end}}item" href="{{.Link}}?show=process">{{ctx.Locale.Tr "admin.monitor.process"}}</a>
-				<a class="{{if eq .ShowGoroutineList "stacktrace"}}active {{end}}item" href="{{.Link}}?show=stacktrace">{{ctx.Locale.Tr "admin.monitor.stacktrace"}}</a>
+				<a class="{{if eq .ShowGoroutineList "process"}}active {{end}}item" href="?show=process">{{ctx.Locale.Tr "admin.monitor.process"}}</a>
+				<a class="{{if eq .ShowGoroutineList "stacktrace"}}active {{end}}item" href="?show=stacktrace">{{ctx.Locale.Tr "admin.monitor.stacktrace"}}</a>
 			</div>
 		</div>
 		<form target="_blank" action="{{AppSubUrl}}/admin/monitor/diagnosis" class="ui form">
diff --git a/templates/repo/wiki/new.tmpl b/templates/repo/wiki/new.tmpl
index 0d0e78c7ad..118dc7c8f0 100644
--- a/templates/repo/wiki/new.tmpl
+++ b/templates/repo/wiki/new.tmpl
@@ -9,7 +9,7 @@
 				<a class="ui tiny primary button" href="{{.RepoLink}}/wiki?action=_new">{{ctx.Locale.Tr "repo.wiki.new_page_button"}}</a>
 			{{end}}
 		</div>
-		<form class="ui form" action="{{.Link}}?action={{if .PageIsWikiEdit}}_edit{{else}}_new{{end}}" method="post">
+		<form class="ui form" action="?action={{if .PageIsWikiEdit}}_edit{{else}}_new{{end}}" method="post">
 			{{.CsrfTokenHtml}}
 			<div class="field {{if .Err_Title}}error{{end}}">
 				<input name="title" value="{{.title}}" aria-label="{{ctx.Locale.Tr "repo.wiki.page_title"}}" placeholder="{{ctx.Locale.Tr "repo.wiki.page_title"}}" autofocus required>
diff --git a/templates/user/auth/reset_passwd.tmpl b/templates/user/auth/reset_passwd.tmpl
index 4d569e206c..f8303feef3 100644
--- a/templates/user/auth/reset_passwd.tmpl
+++ b/templates/user/auth/reset_passwd.tmpl
@@ -51,7 +51,7 @@
 						<div class="inline field">
 							<button class="ui primary button">{{ctx.Locale.Tr "auth.reset_password_helper"}}</button>
 							{{if and .has_two_factor (not .scratch_code)}}
-								<a href="{{.Link}}?code={{.Code}}&amp;scratch_code=true">{{ctx.Locale.Tr "auth.use_scratch_code"}}</a>
+								<a href="?code={{.Code}}&scratch_code=true">{{ctx.Locale.Tr "auth.use_scratch_code"}}</a>
 							{{end}}
 						</div>
 					{{else}}
diff --git a/templates/user/dashboard/issues.tmpl b/templates/user/dashboard/issues.tmpl
index c9972f9426..88afcf58ec 100644
--- a/templates/user/dashboard/issues.tmpl
+++ b/templates/user/dashboard/issues.tmpl
@@ -5,29 +5,29 @@
 		<div class="flex-container">
 			<div class="flex-container-nav">
 				<div class="ui secondary vertical filter menu tw-bg-transparent">
-					<a class="{{if eq .ViewType "your_repositories"}}active{{end}} item" href="{{.Link}}?type=your_repositories&sort={{$.SortType}}&state={{.State}}&q={{$.Keyword}}">
+					<a class="{{if eq .ViewType "your_repositories"}}active{{end}} item" href="?type=your_repositories&sort={{$.SortType}}&state={{.State}}&q={{$.Keyword}}">
 						{{ctx.Locale.Tr "home.issues.in_your_repos"}}
 						<strong>{{CountFmt .IssueStats.YourRepositoriesCount}}</strong>
 					</a>
-					<a class="{{if eq .ViewType "assigned"}}active{{end}} item" href="{{.Link}}?type=assigned&sort={{$.SortType}}&state={{.State}}&q={{$.Keyword}}">
+					<a class="{{if eq .ViewType "assigned"}}active{{end}} item" href="?type=assigned&sort={{$.SortType}}&state={{.State}}&q={{$.Keyword}}">
 						{{ctx.Locale.Tr "repo.issues.filter_type.assigned_to_you"}}
 						<strong>{{CountFmt .IssueStats.AssignCount}}</strong>
 					</a>
-					<a class="{{if eq .ViewType "created_by"}}active{{end}} item" href="{{.Link}}?type=created_by&sort={{$.SortType}}&state={{.State}}&q={{$.Keyword}}">
+					<a class="{{if eq .ViewType "created_by"}}active{{end}} item" href="?type=created_by&sort={{$.SortType}}&state={{.State}}&q={{$.Keyword}}">
 						{{ctx.Locale.Tr "repo.issues.filter_type.created_by_you"}}
 						<strong>{{CountFmt .IssueStats.CreateCount}}</strong>
 					</a>
 					{{if .PageIsPulls}}
-						<a class="{{if eq .ViewType "review_requested"}}active{{end}} item" href="{{.Link}}?type=review_requested&sort={{$.SortType}}&state={{.State}}&q={{$.Keyword}}">
+						<a class="{{if eq .ViewType "review_requested"}}active{{end}} item" href="?type=review_requested&sort={{$.SortType}}&state={{.State}}&q={{$.Keyword}}">
 							{{ctx.Locale.Tr "repo.issues.filter_type.review_requested"}}
 							<strong>{{CountFmt .IssueStats.ReviewRequestedCount}}</strong>
 						</a>
-						<a class="{{if eq .ViewType "reviewed_by"}}active{{end}} item" href="{{.Link}}?type=reviewed_by&sort={{$.SortType}}&state={{.State}}&q={{$.Keyword}}">
+						<a class="{{if eq .ViewType "reviewed_by"}}active{{end}} item" href="?type=reviewed_by&sort={{$.SortType}}&state={{.State}}&q={{$.Keyword}}">
 							{{ctx.Locale.Tr "repo.issues.filter_type.reviewed_by_you"}}
 							<strong>{{CountFmt .IssueStats.ReviewedCount}}</strong>
 						</a>
 					{{end}}
-					<a class="{{if eq .ViewType "mentioned"}}active{{end}} item" href="{{.Link}}?type=mentioned&sort={{$.SortType}}&state={{.State}}&q={{$.Keyword}}">
+					<a class="{{if eq .ViewType "mentioned"}}active{{end}} item" href="?type=mentioned&sort={{$.SortType}}&state={{.State}}&q={{$.Keyword}}">
 						{{ctx.Locale.Tr "repo.issues.filter_type.mentioning_you"}}
 						<strong>{{CountFmt .IssueStats.MentionCount}}</strong>
 					</a>
@@ -36,11 +36,11 @@
 			<div class="flex-container-main content">
 				<div class="list-header">
 					<div class="small-menu-items ui compact tiny menu list-header-toggle">
-						<a class="item{{if not .IsShowClosed}} active{{end}}" href="{{.Link}}?type={{$.ViewType}}&sort={{$.SortType}}&state=open&q={{$.Keyword}}">
+						<a class="item{{if not .IsShowClosed}} active{{end}}" href="?type={{$.ViewType}}&sort={{$.SortType}}&state=open&q={{$.Keyword}}">
 							{{svg "octicon-issue-opened" 16 "gt-mr-3"}}
 							{{ctx.Locale.PrettyNumber .IssueStats.OpenCount}}&nbsp;{{ctx.Locale.Tr "repo.issues.open_title"}}
 						</a>
-						<a class="item{{if .IsShowClosed}} active{{end}}" href="{{.Link}}?type={{$.ViewType}}&sort={{$.SortType}}&state=closed&q={{$.Keyword}}">
+						<a class="item{{if .IsShowClosed}} active{{end}}" href="?type={{$.ViewType}}&sort={{$.SortType}}&state=closed&q={{$.Keyword}}">
 							{{svg "octicon-issue-closed" 16 "gt-mr-3"}}
 							{{ctx.Locale.PrettyNumber .IssueStats.ClosedCount}}&nbsp;{{ctx.Locale.Tr "repo.issues.closed_title"}}
 						</a>
diff --git a/templates/user/dashboard/milestones.tmpl b/templates/user/dashboard/milestones.tmpl
index 944043e806..214081d423 100644
--- a/templates/user/dashboard/milestones.tmpl
+++ b/templates/user/dashboard/milestones.tmpl
@@ -36,11 +36,11 @@
 			<div class="flex-container-main content">
 				<div class="list-header">
 					<div class="small-menu-items ui compact tiny menu list-header-toggle">
-						<a class="item{{if not .IsShowClosed}} active{{end}}" href="{{.Link}}?repos=[{{range $.RepoIDs}}{{.}}%2C{{end}}]&sort={{$.SortType}}&state=open&q={{$.Keyword}}">
+						<a class="item{{if not .IsShowClosed}} active{{end}}" href="?repos=[{{range $.RepoIDs}}{{.}}%2C{{end}}]&sort={{$.SortType}}&state=open&q={{$.Keyword}}">
 							{{svg "octicon-milestone" 16 "gt-mr-3"}}
 							{{ctx.Locale.PrettyNumber .MilestoneStats.OpenCount}}&nbsp;{{ctx.Locale.Tr "repo.issues.open_title"}}
 						</a>
-						<a class="item{{if .IsShowClosed}} active{{end}}" href="{{.Link}}?repos=[{{range $.RepoIDs}}{{.}}%2C{{end}}]&sort={{$.SortType}}&state=closed&q={{$.Keyword}}">
+						<a class="item{{if .IsShowClosed}} active{{end}}" href="?repos=[{{range $.RepoIDs}}{{.}}%2C{{end}}]&sort={{$.SortType}}&state=closed&q={{$.Keyword}}">
 							{{svg "octicon-check" 16 "gt-mr-3"}}
 							{{ctx.Locale.PrettyNumber .MilestoneStats.ClosedCount}}&nbsp;{{ctx.Locale.Tr "repo.issues.closed_title"}}
 						</a>

From 165182a92d0657883e4ce28d63352b79a6416241 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Wed, 20 Mar 2024 12:21:18 +0100
Subject: [PATCH 28/74] Remove the negative margin from `.page-content`
 (#29922)

The negative margin was suboptimal and presents a few unnecessary
challenges while styling the page. Remove it and add custom margin
values, which slightly changes the height a few things near the top of
the page as well:

15px less height of explore and login navbar:

<img width="899" alt="Screenshot 2024-03-20 at 00 52 34"
src="https://github.com/go-gitea/gitea/assets/115237/72a01ca4-5d17-4a0f-b915-61f95054fcb1">

15px reduced padding-top height of "user bar" and equal 4px padding
added:

<img width="484" alt="Screenshot 2024-03-20 at 00 52 50"
src="https://github.com/go-gitea/gitea/assets/115237/a8507e6d-372d-4a8b-9048-66fcf8a5facd">

3px less padding on top of repo:

<img width="552" alt="Screenshot 2024-03-20 at 00 53 49"
src="https://github.com/go-gitea/gitea/assets/115237/dede6e44-7688-440f-a1b6-13532638ae03">

(cherry picked from commit 8cad44f4109b6f87e565d43e137e99ab23b54349)
---
 templates/user/dashboard/navbar.tmpl |  2 +-
 web_src/css/base.css                 | 15 +++++----------
 web_src/css/dashboard.css            |  3 +--
 web_src/css/repo/header.css          |  1 +
 web_src/css/user.css                 |  4 ++++
 5 files changed, 12 insertions(+), 13 deletions(-)

diff --git a/templates/user/dashboard/navbar.tmpl b/templates/user/dashboard/navbar.tmpl
index 3e9442d6fc..480c39e8bf 100644
--- a/templates/user/dashboard/navbar.tmpl
+++ b/templates/user/dashboard/navbar.tmpl
@@ -105,4 +105,4 @@
 	{{end}}
 	</div>
 </div>
-<div class="divider"></div>
+<div class="divider tw-mt-0"></div>
diff --git a/web_src/css/base.css b/web_src/css/base.css
index b4f8be607b..ab20b64394 100644
--- a/web_src/css/base.css
+++ b/web_src/css/base.css
@@ -238,16 +238,6 @@ a.label,
   border-bottom-color: var(--color-secondary);
 }
 
-.page-content {
-  margin-top: 15px;
-}
-
-.page-content .header-wrapper,
-.page-content overflow-menu {
-  margin-top: -15px !important;
-  padding-top: 15px !important;
-}
-
 /* fix Fomantic's line-height cutting off "g" on Windows Chrome with Segoe UI */
 .ui.input > input {
   line-height: var(--line-height-default);
@@ -689,6 +679,11 @@ img.ui.avatar,
   padding-bottom: 80px;
 }
 
+.page-content.new:is(.repo,.migrate,.org),
+.page-content.profile:is(.user,.organization) {
+  padding-top: 15px;
+}
+
 /* overwrite semantic width of containers inside the main page content div (div with class "page-content") */
 .page-content .ui.ui.ui.container:not(.fluid) {
   width: 1280px;
diff --git a/web_src/css/dashboard.css b/web_src/css/dashboard.css
index 57ddb80bc6..6271a99c29 100644
--- a/web_src/css/dashboard.css
+++ b/web_src/css/dashboard.css
@@ -78,8 +78,7 @@
 }
 
 .dashboard .dashboard-navbar {
-  padding-left: 0.5rem;
-  padding-right: 0.5rem;
+  padding: 4px 12px;
 }
 
 .dashboard .dashboard-navbar .right.menu {
diff --git a/web_src/css/repo/header.css b/web_src/css/repo/header.css
index a262b5b3e9..482608a1a6 100644
--- a/web_src/css/repo/header.css
+++ b/web_src/css/repo/header.css
@@ -71,6 +71,7 @@
 }
 
 .repository .header-wrapper {
+  padding-top: 12px;
   background-color: var(--color-header-wrapper);
 }
 
diff --git a/web_src/css/user.css b/web_src/css/user.css
index 9157a53e7c..ece7d9b2cc 100644
--- a/web_src/css/user.css
+++ b/web_src/css/user.css
@@ -125,6 +125,10 @@
   border: 1px solid var(--color-secondary);
 }
 
+#notification_div {
+  padding-top: 15px;
+}
+
 #notification_table {
   background: var(--color-box-body);
   border: 1px solid var(--color-secondary);

From 0710de9d0d5b6626c08af3430c0429f0b4df1cc7 Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Wed, 20 Mar 2024 23:38:22 +0800
Subject: [PATCH 29/74] Fix loadOneBranch panic (#29938)

Try to fix #29936

Far from ideal, but still better than panic.

(cherry picked from commit 21151474e36eecc5b808963b924cd27ec34e0608)
---
 modules/git/repo.go           |  2 +-
 services/repository/branch.go | 10 ++++++----
 2 files changed, 7 insertions(+), 5 deletions(-)

diff --git a/modules/git/repo.go b/modules/git/repo.go
index 60078f3273..e8a7016d99 100644
--- a/modules/git/repo.go
+++ b/modules/git/repo.go
@@ -283,7 +283,7 @@ type DivergeObject struct {
 // GetDivergingCommits returns the number of commits a targetBranch is ahead or behind a baseBranch
 func GetDivergingCommits(ctx context.Context, repoPath, baseBranch, targetBranch string) (do DivergeObject, err error) {
 	cmd := NewCommand(ctx, "rev-list", "--count", "--left-right").
-		AddDynamicArguments(baseBranch + "..." + targetBranch)
+		AddDynamicArguments(baseBranch + "..." + targetBranch).AddArguments("--")
 	stdout, _, err := cmd.RunStdString(&RunOpts{Dir: repoPath})
 	if err != nil {
 		return do, err
diff --git a/services/repository/branch.go b/services/repository/branch.go
index b683553245..64d8eda4e9 100644
--- a/services/repository/branch.go
+++ b/services/repository/branch.go
@@ -127,10 +127,7 @@ func loadOneBranch(ctx context.Context, repo *repo_model.Repository, dbBranch *g
 	p := protectedBranches.GetFirstMatched(branchName)
 	isProtected := p != nil
 
-	divergence := &git.DivergeObject{
-		Ahead:  -1,
-		Behind: -1,
-	}
+	var divergence *git.DivergeObject
 
 	// it's not default branch
 	if repo.DefaultBranch != dbBranch.Name && !dbBranch.IsDeleted {
@@ -141,6 +138,11 @@ func loadOneBranch(ctx context.Context, repo *repo_model.Repository, dbBranch *g
 		}
 	}
 
+	if divergence == nil {
+		// tolerate the error that we cannot get divergence
+		divergence = &git.DivergeObject{Ahead: -1, Behind: -1}
+	}
+
 	pr, err := issues_model.GetLatestPullRequestByHeadInfo(ctx, repo.ID, branchName)
 	if err != nil {
 		return nil, fmt.Errorf("GetLatestPullRequestByHeadInfo: %v", err)

From dda010cddf1c3798877ebee52d5b0801c97f0e63 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Wed, 20 Mar 2024 23:05:24 +0100
Subject: [PATCH 30/74] Remove fomantic grid module (#29894)

Removed the grid module and moved the used parts it into our own CSS,
eliminating around 75% unused CSS in turn.

(cherry picked from commit 286268c9155c9e0b3a3aa0a18675111e5b744a5b)
---
 web_src/css/index.css               |    1 +
 web_src/css/modules/grid.css        |  498 +++++++
 web_src/css/modules/message.css     |    3 +
 web_src/fomantic/build/semantic.css | 2003 ---------------------------
 web_src/fomantic/semantic.json      |    1 -
 5 files changed, 502 insertions(+), 2004 deletions(-)
 create mode 100644 web_src/css/modules/grid.css

diff --git a/web_src/css/index.css b/web_src/css/index.css
index f6e4c196e6..bf568bff4d 100644
--- a/web_src/css/index.css
+++ b/web_src/css/index.css
@@ -1,5 +1,6 @@
 @import "./modules/normalize.css";
 @import "./modules/animations.css";
+@import "./modules/grid.css";
 @import "./modules/button.css";
 @import "./modules/select.css";
 @import "./modules/tippy.css";
diff --git a/web_src/css/modules/grid.css b/web_src/css/modules/grid.css
new file mode 100644
index 0000000000..5a80576c8a
--- /dev/null
+++ b/web_src/css/modules/grid.css
@@ -0,0 +1,498 @@
+/* based on Fomantic UI grid module, with just the parts extracted that we use. If you find any
+   unused rules here after refactoring, please remove them. */
+
+.ui.grid {
+  display: flex;
+  flex-direction: row;
+  flex-wrap: wrap;
+  align-items: stretch;
+  padding: 0;
+  margin-top: -1rem;
+  margin-bottom: -1rem;
+  margin-left: -1rem;
+  margin-right: -1rem;
+}
+
+.ui.relaxed.grid {
+  margin-left: -1.5rem;
+  margin-right: -1.5rem;
+}
+.ui[class*="very relaxed"].grid {
+  margin-left: -2.5rem;
+  margin-right: -2.5rem;
+}
+
+.ui.grid + .grid {
+  margin-top: 1rem;
+}
+
+.ui.grid > .column:not(.row),
+.ui.grid > .row > .column {
+  position: relative;
+  display: inline-block;
+  width: 6.25%;
+  padding-left: 1rem;
+  padding-right: 1rem;
+  vertical-align: top;
+}
+.ui.grid > * {
+  padding-left: 1rem;
+  padding-right: 1rem;
+}
+
+.ui.grid > .row {
+  position: relative;
+  display: flex;
+  flex-direction: row;
+  flex-wrap: wrap;
+  justify-content: inherit;
+  align-items: stretch;
+  width: 100% !important;
+  padding: 0;
+  padding-top: 1rem;
+  padding-bottom: 1rem;
+}
+
+.ui.grid > .column:not(.row) {
+  padding-top: 1rem;
+  padding-bottom: 1rem;
+}
+.ui.grid > .row > .column {
+  margin-top: 0;
+  margin-bottom: 0;
+}
+
+.ui.grid > .row > img,
+.ui.grid > .row > .column > img {
+  max-width: 100%;
+}
+
+.ui.grid > .ui.grid:first-child {
+  margin-top: 0;
+}
+.ui.grid > .ui.grid:last-child {
+  margin-bottom: 0;
+}
+
+.ui.grid .aligned.row > .column > .segment:not(.compact):not(.attached),
+.ui.aligned.grid .column > .segment:not(.compact):not(.attached) {
+  width: 100%;
+}
+
+.ui.grid .row + .ui.divider {
+  flex-grow: 1;
+  margin: 1rem;
+}
+.ui.grid .column + .ui.vertical.divider {
+  height: calc(50% - 1rem);
+}
+
+.ui.grid > .row > .column:last-child > .horizontal.segment,
+.ui.grid > .column:last-child > .horizontal.segment {
+  box-shadow: none;
+}
+
+@media only screen and (max-width: 767.98px) {
+  .ui.page.grid {
+    width: auto;
+    padding-left: 0;
+    padding-right: 0;
+    margin-left: 0;
+    margin-right: 0;
+  }
+}
+@media only screen and (min-width: 768px) and (max-width: 991.98px) {
+  .ui.page.grid {
+    width: auto;
+    margin-left: 0;
+    margin-right: 0;
+    padding-left: 2em;
+    padding-right: 2em;
+  }
+}
+@media only screen and (min-width: 992px) and (max-width: 1199.98px) {
+  .ui.page.grid {
+    width: auto;
+    margin-left: 0;
+    margin-right: 0;
+    padding-left: 3%;
+    padding-right: 3%;
+  }
+}
+@media only screen and (min-width: 1200px) and (max-width: 1919.98px) {
+  .ui.page.grid {
+    width: auto;
+    margin-left: 0;
+    margin-right: 0;
+    padding-left: 15%;
+    padding-right: 15%;
+  }
+}
+@media only screen and (min-width: 1920px) {
+  .ui.page.grid {
+    width: auto;
+    margin-left: 0;
+    margin-right: 0;
+    padding-left: 23%;
+    padding-right: 23%;
+  }
+}
+
+.ui.grid > .column:only-child,
+.ui.grid > .row > .column:only-child {
+  width: 100%;
+}
+
+.ui[class*="one column"].grid > .row > .column,
+.ui[class*="one column"].grid > .column:not(.row) {
+  width: 100%;
+}
+.ui[class*="two column"].grid > .row > .column,
+.ui[class*="two column"].grid > .column:not(.row) {
+  width: 50%;
+}
+.ui[class*="three column"].grid > .row > .column,
+.ui[class*="three column"].grid > .column:not(.row) {
+  width: 33.33333333%;
+}
+.ui[class*="four column"].grid > .row > .column,
+.ui[class*="four column"].grid > .column:not(.row) {
+  width: 25%;
+}
+.ui[class*="five column"].grid > .row > .column,
+.ui[class*="five column"].grid > .column:not(.row) {
+  width: 20%;
+}
+.ui[class*="six column"].grid > .row > .column,
+.ui[class*="six column"].grid > .column:not(.row) {
+  width: 16.66666667%;
+}
+.ui[class*="seven column"].grid > .row > .column,
+.ui[class*="seven column"].grid > .column:not(.row) {
+  width: 14.28571429%;
+}
+.ui[class*="eight column"].grid > .row > .column,
+.ui[class*="eight column"].grid > .column:not(.row) {
+  width: 12.5%;
+}
+.ui[class*="nine column"].grid > .row > .column,
+.ui[class*="nine column"].grid > .column:not(.row) {
+  width: 11.11111111%;
+}
+.ui[class*="ten column"].grid > .row > .column,
+.ui[class*="ten column"].grid > .column:not(.row) {
+  width: 10%;
+}
+.ui[class*="eleven column"].grid > .row > .column,
+.ui[class*="eleven column"].grid > .column:not(.row) {
+  width: 9.09090909%;
+}
+.ui[class*="twelve column"].grid > .row > .column,
+.ui[class*="twelve column"].grid > .column:not(.row) {
+  width: 8.33333333%;
+}
+.ui[class*="thirteen column"].grid > .row > .column,
+.ui[class*="thirteen column"].grid > .column:not(.row) {
+  width: 7.69230769%;
+}
+.ui[class*="fourteen column"].grid > .row > .column,
+.ui[class*="fourteen column"].grid > .column:not(.row) {
+  width: 7.14285714%;
+}
+.ui[class*="fifteen column"].grid > .row > .column,
+.ui[class*="fifteen column"].grid > .column:not(.row) {
+  width: 6.66666667%;
+}
+.ui[class*="sixteen column"].grid > .row > .column,
+.ui[class*="sixteen column"].grid > .column:not(.row) {
+  width: 6.25%;
+}
+
+.ui.grid > [class*="one column"].row > .column {
+  width: 100% !important;
+}
+.ui.grid > [class*="two column"].row > .column {
+  width: 50% !important;
+}
+.ui.grid > [class*="three column"].row > .column {
+  width: 33.33333333% !important;
+}
+.ui.grid > [class*="four column"].row > .column {
+  width: 25% !important;
+}
+.ui.grid > [class*="five column"].row > .column {
+  width: 20% !important;
+}
+.ui.grid > [class*="six column"].row > .column {
+  width: 16.66666667% !important;
+}
+.ui.grid > [class*="seven column"].row > .column {
+  width: 14.28571429% !important;
+}
+.ui.grid > [class*="eight column"].row > .column {
+  width: 12.5% !important;
+}
+.ui.grid > [class*="nine column"].row > .column {
+  width: 11.11111111% !important;
+}
+.ui.grid > [class*="ten column"].row > .column {
+  width: 10% !important;
+}
+.ui.grid > [class*="eleven column"].row > .column {
+  width: 9.09090909% !important;
+}
+.ui.grid > [class*="twelve column"].row > .column {
+  width: 8.33333333% !important;
+}
+.ui.grid > [class*="thirteen column"].row > .column {
+  width: 7.69230769% !important;
+}
+.ui.grid > [class*="fourteen column"].row > .column {
+  width: 7.14285714% !important;
+}
+.ui.grid > [class*="fifteen column"].row > .column {
+  width: 6.66666667% !important;
+}
+.ui.grid > [class*="sixteen column"].row > .column {
+  width: 6.25% !important;
+}
+
+.ui.grid > .row > [class*="one wide"].column,
+.ui.grid > .column.row > [class*="one wide"].column,
+.ui.grid > [class*="one wide"].column,
+.ui.column.grid > [class*="one wide"].column {
+  width: 6.25% !important;
+}
+.ui.grid > .row > [class*="two wide"].column,
+.ui.grid > .column.row > [class*="two wide"].column,
+.ui.grid > [class*="two wide"].column,
+.ui.column.grid > [class*="two wide"].column {
+  width: 12.5% !important;
+}
+.ui.grid > .row > [class*="three wide"].column,
+.ui.grid > .column.row > [class*="three wide"].column,
+.ui.grid > [class*="three wide"].column,
+.ui.column.grid > [class*="three wide"].column {
+  width: 18.75% !important;
+}
+.ui.grid > .row > [class*="four wide"].column,
+.ui.grid > .column.row > [class*="four wide"].column,
+.ui.grid > [class*="four wide"].column,
+.ui.column.grid > [class*="four wide"].column {
+  width: 25% !important;
+}
+.ui.grid > .row > [class*="five wide"].column,
+.ui.grid > .column.row > [class*="five wide"].column,
+.ui.grid > [class*="five wide"].column,
+.ui.column.grid > [class*="five wide"].column {
+  width: 31.25% !important;
+}
+.ui.grid > .row > [class*="six wide"].column,
+.ui.grid > .column.row > [class*="six wide"].column,
+.ui.grid > [class*="six wide"].column,
+.ui.column.grid > [class*="six wide"].column {
+  width: 37.5% !important;
+}
+.ui.grid > .row > [class*="seven wide"].column,
+.ui.grid > .column.row > [class*="seven wide"].column,
+.ui.grid > [class*="seven wide"].column,
+.ui.column.grid > [class*="seven wide"].column {
+  width: 43.75% !important;
+}
+.ui.grid > .row > [class*="eight wide"].column,
+.ui.grid > .column.row > [class*="eight wide"].column,
+.ui.grid > [class*="eight wide"].column,
+.ui.column.grid > [class*="eight wide"].column {
+  width: 50% !important;
+}
+.ui.grid > .row > [class*="nine wide"].column,
+.ui.grid > .column.row > [class*="nine wide"].column,
+.ui.grid > [class*="nine wide"].column,
+.ui.column.grid > [class*="nine wide"].column {
+  width: 56.25% !important;
+}
+.ui.grid > .row > [class*="ten wide"].column,
+.ui.grid > .column.row > [class*="ten wide"].column,
+.ui.grid > [class*="ten wide"].column,
+.ui.column.grid > [class*="ten wide"].column {
+  width: 62.5% !important;
+}
+.ui.grid > .row > [class*="eleven wide"].column,
+.ui.grid > .column.row > [class*="eleven wide"].column,
+.ui.grid > [class*="eleven wide"].column,
+.ui.column.grid > [class*="eleven wide"].column {
+  width: 68.75% !important;
+}
+.ui.grid > .row > [class*="twelve wide"].column,
+.ui.grid > .column.row > [class*="twelve wide"].column,
+.ui.grid > [class*="twelve wide"].column,
+.ui.column.grid > [class*="twelve wide"].column {
+  width: 75% !important;
+}
+.ui.grid > .row > [class*="thirteen wide"].column,
+.ui.grid > .column.row > [class*="thirteen wide"].column,
+.ui.grid > [class*="thirteen wide"].column,
+.ui.column.grid > [class*="thirteen wide"].column {
+  width: 81.25% !important;
+}
+.ui.grid > .row > [class*="fourteen wide"].column,
+.ui.grid > .column.row > [class*="fourteen wide"].column,
+.ui.grid > [class*="fourteen wide"].column,
+.ui.column.grid > [class*="fourteen wide"].column {
+  width: 87.5% !important;
+}
+.ui.grid > .row > [class*="fifteen wide"].column,
+.ui.grid > .column.row > [class*="fifteen wide"].column,
+.ui.grid > [class*="fifteen wide"].column,
+.ui.column.grid > [class*="fifteen wide"].column {
+  width: 93.75% !important;
+}
+.ui.grid > .row > [class*="sixteen wide"].column,
+.ui.grid > .column.row > [class*="sixteen wide"].column,
+.ui.grid > [class*="sixteen wide"].column,
+.ui.column.grid > [class*="sixteen wide"].column {
+  width: 100% !important;
+}
+
+.ui.centered.grid,
+.ui.centered.grid > .row,
+.ui.grid > .centered.row {
+  text-align: center;
+  justify-content: center;
+}
+.ui.centered.grid > .column:not(.aligned):not(.justified):not(.row),
+.ui.centered.grid > .row > .column:not(.aligned):not(.justified),
+.ui.grid .centered.row > .column:not(.aligned):not(.justified) {
+  text-align: left;
+}
+.ui.grid > .centered.column,
+.ui.grid > .row > .centered.column {
+  display: block;
+  margin-left: auto;
+  margin-right: auto;
+}
+
+.ui.relaxed.grid > .column:not(.row),
+.ui.relaxed.grid > .row > .column,
+.ui.grid > .relaxed.row > .column {
+  padding-left: 1.5rem;
+  padding-right: 1.5rem;
+}
+.ui[class*="very relaxed"].grid > .column:not(.row),
+.ui[class*="very relaxed"].grid > .row > .column,
+.ui.grid > [class*="very relaxed"].row > .column {
+  padding-left: 2.5rem;
+  padding-right: 2.5rem;
+}
+
+.ui.relaxed.grid .row + .ui.divider,
+.ui.grid .relaxed.row + .ui.divider {
+  margin-left: 1.5rem;
+  margin-right: 1.5rem;
+}
+.ui[class*="very relaxed"].grid .row + .ui.divider,
+.ui.grid [class*="very relaxed"].row + .ui.divider {
+  margin-left: 2.5rem;
+  margin-right: 2.5rem;
+}
+
+.ui[class*="middle aligned"].grid > .column:not(.row),
+.ui[class*="middle aligned"].grid > .row > .column,
+.ui.grid > [class*="middle aligned"].row > .column,
+.ui.grid > [class*="middle aligned"].column:not(.row),
+.ui.grid > .row > [class*="middle aligned"].column {
+  flex-direction: column;
+  vertical-align: middle;
+  align-self: center !important;
+}
+
+.ui[class*="center aligned"].grid > .column,
+.ui[class*="center aligned"].grid > .row > .column,
+.ui.grid > [class*="center aligned"].row > .column,
+.ui.grid > [class*="center aligned"].column.column,
+.ui.grid > .row > [class*="center aligned"].column.column {
+  text-align: center;
+  align-self: inherit;
+}
+.ui[class*="center aligned"].grid {
+  justify-content: center;
+}
+
+.ui[class*="equal width"].grid > .column:not(.row),
+.ui[class*="equal width"].grid > .row > .column,
+.ui.grid > [class*="equal width"].row > .column {
+  display: inline-block;
+  flex-grow: 1;
+}
+.ui[class*="equal width"].grid > .wide.column,
+.ui[class*="equal width"].grid > .row > .wide.column,
+.ui.grid > [class*="equal width"].row > .wide.column {
+  flex-grow: 0;
+}
+
+@media only screen and (max-width: 767.98px) {
+  .ui[class*="mobile reversed"].grid,
+  .ui[class*="mobile reversed"].grid > .row,
+  .ui.grid > [class*="mobile reversed"].row {
+    flex-direction: row-reverse;
+  }
+  .ui.stackable[class*="mobile reversed"] {
+    flex-direction: column-reverse;
+  }
+}
+
+@media only screen and (max-width: 767.98px) {
+  .ui.stackable.grid {
+    width: auto;
+    margin-left: 0 !important;
+    margin-right: 0 !important;
+  }
+  .ui.stackable.grid > .row > .wide.column,
+  .ui.stackable.grid > .wide.column,
+  .ui.stackable.grid > .column.grid > .column,
+  .ui.stackable.grid > .column.row > .column,
+  .ui.stackable.grid > .row > .column,
+  .ui.stackable.grid > .column:not(.row),
+  .ui.grid > .stackable.stackable.stackable.row > .column {
+    width: 100% !important;
+    margin: 0 !important;
+    box-shadow: none !important;
+    padding: 1rem;
+  }
+  .ui.stackable.grid:not(.vertically) > .row {
+    margin: 0;
+    padding: 0;
+  }
+
+  .ui.container > .ui.stackable.grid > .column,
+  .ui.container > .ui.stackable.grid > .row > .column {
+    padding-left: 0 !important;
+    padding-right: 0 !important;
+  }
+
+  .ui.grid .ui.stackable.grid,
+  .ui.segment:not(.vertical) .ui.stackable.page.grid {
+    margin-left: -1rem !important;
+    margin-right: -1rem !important;
+  }
+}
+
+.ui.ui.ui.compact.grid > .column:not(.row),
+.ui.ui.ui.compact.grid > .row > .column {
+  padding-left: 0.5rem;
+  padding-right: 0.5rem;
+}
+.ui.ui.ui.compact.grid > * {
+  padding-left: 0.5rem;
+  padding-right: 0.5rem;
+}
+
+.ui.ui.ui.compact.grid > .row {
+  padding-top: 0.5rem;
+  padding-bottom: 0.5rem;
+}
+
+.ui.ui.ui.compact.grid > .column:not(.row) {
+  padding-top: 0.5rem;
+  padding-bottom: 0.5rem;
+}
diff --git a/web_src/css/modules/message.css b/web_src/css/modules/message.css
index 22dd03232b..a29603cd91 100644
--- a/web_src/css/modules/message.css
+++ b/web_src/css/modules/message.css
@@ -1,3 +1,6 @@
+/* based on Fomantic UI message module, with just the parts extracted that we use. If you find any
+   unused rules here after refactoring, please remove them. */
+
 .ui.message {
   background: var(--color-box-body);
   color: var(--color-text);
diff --git a/web_src/fomantic/build/semantic.css b/web_src/fomantic/build/semantic.css
index 6f45c1944c..538dfefdc1 100644
--- a/web_src/fomantic/build/semantic.css
+++ b/web_src/fomantic/build/semantic.css
@@ -7340,2009 +7340,6 @@ select.ui.dropdown {
          Theme Overrides
 *******************************/
 
-/*******************************
-         Site Overrides
-*******************************/
-/*!
- * # Fomantic-UI - Grid
- * http://github.com/fomantic/Fomantic-UI/
- *
- *
- * Released under the MIT license
- * http://opensource.org/licenses/MIT
- *
- */
-
-/*******************************
-            Standard
-*******************************/
-
-.ui.grid {
-  display: flex;
-  flex-direction: row;
-  flex-wrap: wrap;
-  align-items: stretch;
-  padding: 0;
-}
-
-/*----------------------
-      Remove Gutters
------------------------*/
-
-.ui.grid {
-  margin-top: -1rem;
-  margin-bottom: -1rem;
-  margin-left: -1rem;
-  margin-right: -1rem;
-}
-
-.ui.relaxed.grid {
-  margin-left: -1.5rem;
-  margin-right: -1.5rem;
-}
-
-.ui[class*="very relaxed"].grid {
-  margin-left: -2.5rem;
-  margin-right: -2.5rem;
-}
-
-/* Preserve Rows Spacing on Consecutive Grids */
-
-.ui.grid + .grid {
-  margin-top: 1rem;
-}
-
-/*-------------------
-       Columns
---------------------*/
-
-/* Standard 16 column */
-
-.ui.grid > .column:not(.row),
-.ui.grid > .row > .column {
-  position: relative;
-  display: inline-block;
-  width: 6.25%;
-  padding-left: 1rem;
-  padding-right: 1rem;
-  vertical-align: top;
-}
-
-.ui.grid > * {
-  padding-left: 1rem;
-  padding-right: 1rem;
-}
-
-/*-------------------
-        Rows
---------------------*/
-
-.ui.grid > .row {
-  position: relative;
-  display: flex;
-  flex-direction: row;
-  flex-wrap: wrap;
-  justify-content: inherit;
-  align-items: stretch;
-  width: 100% !important;
-  padding: 0;
-  padding-top: 1rem;
-  padding-bottom: 1rem;
-}
-
-/*-------------------
-       Columns
---------------------*/
-
-/* Vertical padding when no rows */
-
-.ui.grid > .column:not(.row) {
-  padding-top: 1rem;
-  padding-bottom: 1rem;
-}
-
-.ui.grid > .row > .column {
-  margin-top: 0;
-  margin-bottom: 0;
-}
-
-/*-------------------
-      Content
---------------------*/
-
-.ui.grid > .row > img,
-.ui.grid > .row > .column > img {
-  max-width: 100%;
-}
-
-/*-------------------
-    Loose Coupling
---------------------*/
-
-/* Collapse Margin on Consecutive Grid */
-
-.ui.grid > .ui.grid:first-child {
-  margin-top: 0;
-}
-
-.ui.grid > .ui.grid:last-child {
-  margin-bottom: 0;
-}
-
-/* Segment inside Aligned Grid */
-
-.ui.grid .aligned.row > .column > .segment:not(.compact):not(.attached),
-.ui.aligned.grid .column > .segment:not(.compact):not(.attached) {
-  width: 100%;
-}
-
-/* Align Dividers with Gutter */
-
-.ui.grid .row + .ui.divider {
-  flex-grow: 1;
-  margin: 1rem 1rem;
-}
-
-.ui.grid .column + .ui.vertical.divider {
-  height: calc(50% - 1rem);
-}
-
-/* Remove Border on Last Horizontal Segment */
-
-.ui.grid > .row > .column:last-child > .horizontal.segment,
-.ui.grid > .column:last-child > .horizontal.segment {
-  box-shadow: none;
-}
-
-/*******************************
-           Variations
-*******************************/
-
-/*-----------------------
-         Page Grid
-  -------------------------*/
-
-@media only screen and (max-width: 767.98px) {
-  .ui.page.grid {
-    width: auto;
-    padding-left: 0;
-    padding-right: 0;
-    margin-left: 0;
-    margin-right: 0;
-  }
-}
-
-@media only screen and (min-width: 768px) and (max-width: 991.98px) {
-  .ui.page.grid {
-    width: auto;
-    margin-left: 0;
-    margin-right: 0;
-    padding-left: 2em;
-    padding-right: 2em;
-  }
-}
-
-@media only screen and (min-width: 992px) and (max-width: 1199.98px) {
-  .ui.page.grid {
-    width: auto;
-    margin-left: 0;
-    margin-right: 0;
-    padding-left: 3%;
-    padding-right: 3%;
-  }
-}
-
-@media only screen and (min-width: 1200px) and (max-width: 1919.98px) {
-  .ui.page.grid {
-    width: auto;
-    margin-left: 0;
-    margin-right: 0;
-    padding-left: 15%;
-    padding-right: 15%;
-  }
-}
-
-@media only screen and (min-width: 1920px) {
-  .ui.page.grid {
-    width: auto;
-    margin-left: 0;
-    margin-right: 0;
-    padding-left: 23%;
-    padding-right: 23%;
-  }
-}
-
-/*-------------------
-     Column Count
---------------------*/
-
-/* Assume full width with one column */
-
-.ui.grid > .column:only-child,
-.ui.grid > .row > .column:only-child {
-  width: 100%;
-}
-
-/* Grid Based */
-
-.ui[class*="one column"].grid > .row > .column,
-.ui[class*="one column"].grid > .column:not(.row) {
-  width: 100%;
-}
-
-.ui[class*="two column"].grid > .row > .column,
-.ui[class*="two column"].grid > .column:not(.row) {
-  width: 50%;
-}
-
-.ui[class*="three column"].grid > .row > .column,
-.ui[class*="three column"].grid > .column:not(.row) {
-  width: 33.33333333%;
-}
-
-.ui[class*="four column"].grid > .row > .column,
-.ui[class*="four column"].grid > .column:not(.row) {
-  width: 25%;
-}
-
-.ui[class*="five column"].grid > .row > .column,
-.ui[class*="five column"].grid > .column:not(.row) {
-  width: 20%;
-}
-
-.ui[class*="six column"].grid > .row > .column,
-.ui[class*="six column"].grid > .column:not(.row) {
-  width: 16.66666667%;
-}
-
-.ui[class*="seven column"].grid > .row > .column,
-.ui[class*="seven column"].grid > .column:not(.row) {
-  width: 14.28571429%;
-}
-
-.ui[class*="eight column"].grid > .row > .column,
-.ui[class*="eight column"].grid > .column:not(.row) {
-  width: 12.5%;
-}
-
-.ui[class*="nine column"].grid > .row > .column,
-.ui[class*="nine column"].grid > .column:not(.row) {
-  width: 11.11111111%;
-}
-
-.ui[class*="ten column"].grid > .row > .column,
-.ui[class*="ten column"].grid > .column:not(.row) {
-  width: 10%;
-}
-
-.ui[class*="eleven column"].grid > .row > .column,
-.ui[class*="eleven column"].grid > .column:not(.row) {
-  width: 9.09090909%;
-}
-
-.ui[class*="twelve column"].grid > .row > .column,
-.ui[class*="twelve column"].grid > .column:not(.row) {
-  width: 8.33333333%;
-}
-
-.ui[class*="thirteen column"].grid > .row > .column,
-.ui[class*="thirteen column"].grid > .column:not(.row) {
-  width: 7.69230769%;
-}
-
-.ui[class*="fourteen column"].grid > .row > .column,
-.ui[class*="fourteen column"].grid > .column:not(.row) {
-  width: 7.14285714%;
-}
-
-.ui[class*="fifteen column"].grid > .row > .column,
-.ui[class*="fifteen column"].grid > .column:not(.row) {
-  width: 6.66666667%;
-}
-
-.ui[class*="sixteen column"].grid > .row > .column,
-.ui[class*="sixteen column"].grid > .column:not(.row) {
-  width: 6.25%;
-}
-
-/* Row Based Overrides */
-
-.ui.grid > [class*="one column"].row > .column {
-  width: 100% !important;
-}
-
-.ui.grid > [class*="two column"].row > .column {
-  width: 50% !important;
-}
-
-.ui.grid > [class*="three column"].row > .column {
-  width: 33.33333333% !important;
-}
-
-.ui.grid > [class*="four column"].row > .column {
-  width: 25% !important;
-}
-
-.ui.grid > [class*="five column"].row > .column {
-  width: 20% !important;
-}
-
-.ui.grid > [class*="six column"].row > .column {
-  width: 16.66666667% !important;
-}
-
-.ui.grid > [class*="seven column"].row > .column {
-  width: 14.28571429% !important;
-}
-
-.ui.grid > [class*="eight column"].row > .column {
-  width: 12.5% !important;
-}
-
-.ui.grid > [class*="nine column"].row > .column {
-  width: 11.11111111% !important;
-}
-
-.ui.grid > [class*="ten column"].row > .column {
-  width: 10% !important;
-}
-
-.ui.grid > [class*="eleven column"].row > .column {
-  width: 9.09090909% !important;
-}
-
-.ui.grid > [class*="twelve column"].row > .column {
-  width: 8.33333333% !important;
-}
-
-.ui.grid > [class*="thirteen column"].row > .column {
-  width: 7.69230769% !important;
-}
-
-.ui.grid > [class*="fourteen column"].row > .column {
-  width: 7.14285714% !important;
-}
-
-.ui.grid > [class*="fifteen column"].row > .column {
-  width: 6.66666667% !important;
-}
-
-.ui.grid > [class*="sixteen column"].row > .column {
-  width: 6.25% !important;
-}
-
-/* Celled Page */
-
-.ui.celled.page.grid {
-  box-shadow: none;
-}
-
-/*-------------------
-    Column Width
---------------------*/
-
-/* Sizing Combinations */
-
-.ui.grid > .row > [class*="one wide"].column,
-.ui.grid > .column.row > [class*="one wide"].column,
-.ui.grid > [class*="one wide"].column,
-.ui.column.grid > [class*="one wide"].column {
-  width: 6.25% !important;
-}
-
-.ui.grid > .row > [class*="two wide"].column,
-.ui.grid > .column.row > [class*="two wide"].column,
-.ui.grid > [class*="two wide"].column,
-.ui.column.grid > [class*="two wide"].column {
-  width: 12.5% !important;
-}
-
-.ui.grid > .row > [class*="three wide"].column,
-.ui.grid > .column.row > [class*="three wide"].column,
-.ui.grid > [class*="three wide"].column,
-.ui.column.grid > [class*="three wide"].column {
-  width: 18.75% !important;
-}
-
-.ui.grid > .row > [class*="four wide"].column,
-.ui.grid > .column.row > [class*="four wide"].column,
-.ui.grid > [class*="four wide"].column,
-.ui.column.grid > [class*="four wide"].column {
-  width: 25% !important;
-}
-
-.ui.grid > .row > [class*="five wide"].column,
-.ui.grid > .column.row > [class*="five wide"].column,
-.ui.grid > [class*="five wide"].column,
-.ui.column.grid > [class*="five wide"].column {
-  width: 31.25% !important;
-}
-
-.ui.grid > .row > [class*="six wide"].column,
-.ui.grid > .column.row > [class*="six wide"].column,
-.ui.grid > [class*="six wide"].column,
-.ui.column.grid > [class*="six wide"].column {
-  width: 37.5% !important;
-}
-
-.ui.grid > .row > [class*="seven wide"].column,
-.ui.grid > .column.row > [class*="seven wide"].column,
-.ui.grid > [class*="seven wide"].column,
-.ui.column.grid > [class*="seven wide"].column {
-  width: 43.75% !important;
-}
-
-.ui.grid > .row > [class*="eight wide"].column,
-.ui.grid > .column.row > [class*="eight wide"].column,
-.ui.grid > [class*="eight wide"].column,
-.ui.column.grid > [class*="eight wide"].column {
-  width: 50% !important;
-}
-
-.ui.grid > .row > [class*="nine wide"].column,
-.ui.grid > .column.row > [class*="nine wide"].column,
-.ui.grid > [class*="nine wide"].column,
-.ui.column.grid > [class*="nine wide"].column {
-  width: 56.25% !important;
-}
-
-.ui.grid > .row > [class*="ten wide"].column,
-.ui.grid > .column.row > [class*="ten wide"].column,
-.ui.grid > [class*="ten wide"].column,
-.ui.column.grid > [class*="ten wide"].column {
-  width: 62.5% !important;
-}
-
-.ui.grid > .row > [class*="eleven wide"].column,
-.ui.grid > .column.row > [class*="eleven wide"].column,
-.ui.grid > [class*="eleven wide"].column,
-.ui.column.grid > [class*="eleven wide"].column {
-  width: 68.75% !important;
-}
-
-.ui.grid > .row > [class*="twelve wide"].column,
-.ui.grid > .column.row > [class*="twelve wide"].column,
-.ui.grid > [class*="twelve wide"].column,
-.ui.column.grid > [class*="twelve wide"].column {
-  width: 75% !important;
-}
-
-.ui.grid > .row > [class*="thirteen wide"].column,
-.ui.grid > .column.row > [class*="thirteen wide"].column,
-.ui.grid > [class*="thirteen wide"].column,
-.ui.column.grid > [class*="thirteen wide"].column {
-  width: 81.25% !important;
-}
-
-.ui.grid > .row > [class*="fourteen wide"].column,
-.ui.grid > .column.row > [class*="fourteen wide"].column,
-.ui.grid > [class*="fourteen wide"].column,
-.ui.column.grid > [class*="fourteen wide"].column {
-  width: 87.5% !important;
-}
-
-.ui.grid > .row > [class*="fifteen wide"].column,
-.ui.grid > .column.row > [class*="fifteen wide"].column,
-.ui.grid > [class*="fifteen wide"].column,
-.ui.column.grid > [class*="fifteen wide"].column {
-  width: 93.75% !important;
-}
-
-.ui.grid > .row > [class*="sixteen wide"].column,
-.ui.grid > .column.row > [class*="sixteen wide"].column,
-.ui.grid > [class*="sixteen wide"].column,
-.ui.column.grid > [class*="sixteen wide"].column {
-  width: 100% !important;
-}
-
-/*----------------------
-    Width per Device
------------------------*/
-
-/* Mobile Sizing Combinations */
-
-@media only screen and (min-width: 320px) and (max-width: 767.98px) {
-  .ui.grid > .row > [class*="one wide mobile"].column,
-  .ui.grid > .column.row > [class*="one wide mobile"].column,
-  .ui.grid > [class*="one wide mobile"].column,
-  .ui.column.grid > [class*="one wide mobile"].column {
-    width: 6.25% !important;
-  }
-
-  .ui.grid > .row > [class*="two wide mobile"].column,
-  .ui.grid > .column.row > [class*="two wide mobile"].column,
-  .ui.grid > [class*="two wide mobile"].column,
-  .ui.column.grid > [class*="two wide mobile"].column {
-    width: 12.5% !important;
-  }
-
-  .ui.grid > .row > [class*="three wide mobile"].column,
-  .ui.grid > .column.row > [class*="three wide mobile"].column,
-  .ui.grid > [class*="three wide mobile"].column,
-  .ui.column.grid > [class*="three wide mobile"].column {
-    width: 18.75% !important;
-  }
-
-  .ui.grid > .row > [class*="four wide mobile"].column,
-  .ui.grid > .column.row > [class*="four wide mobile"].column,
-  .ui.grid > [class*="four wide mobile"].column,
-  .ui.column.grid > [class*="four wide mobile"].column {
-    width: 25% !important;
-  }
-
-  .ui.grid > .row > [class*="five wide mobile"].column,
-  .ui.grid > .column.row > [class*="five wide mobile"].column,
-  .ui.grid > [class*="five wide mobile"].column,
-  .ui.column.grid > [class*="five wide mobile"].column {
-    width: 31.25% !important;
-  }
-
-  .ui.grid > .row > [class*="six wide mobile"].column,
-  .ui.grid > .column.row > [class*="six wide mobile"].column,
-  .ui.grid > [class*="six wide mobile"].column,
-  .ui.column.grid > [class*="six wide mobile"].column {
-    width: 37.5% !important;
-  }
-
-  .ui.grid > .row > [class*="seven wide mobile"].column,
-  .ui.grid > .column.row > [class*="seven wide mobile"].column,
-  .ui.grid > [class*="seven wide mobile"].column,
-  .ui.column.grid > [class*="seven wide mobile"].column {
-    width: 43.75% !important;
-  }
-
-  .ui.grid > .row > [class*="eight wide mobile"].column,
-  .ui.grid > .column.row > [class*="eight wide mobile"].column,
-  .ui.grid > [class*="eight wide mobile"].column,
-  .ui.column.grid > [class*="eight wide mobile"].column {
-    width: 50% !important;
-  }
-
-  .ui.grid > .row > [class*="nine wide mobile"].column,
-  .ui.grid > .column.row > [class*="nine wide mobile"].column,
-  .ui.grid > [class*="nine wide mobile"].column,
-  .ui.column.grid > [class*="nine wide mobile"].column {
-    width: 56.25% !important;
-  }
-
-  .ui.grid > .row > [class*="ten wide mobile"].column,
-  .ui.grid > .column.row > [class*="ten wide mobile"].column,
-  .ui.grid > [class*="ten wide mobile"].column,
-  .ui.column.grid > [class*="ten wide mobile"].column {
-    width: 62.5% !important;
-  }
-
-  .ui.grid > .row > [class*="eleven wide mobile"].column,
-  .ui.grid > .column.row > [class*="eleven wide mobile"].column,
-  .ui.grid > [class*="eleven wide mobile"].column,
-  .ui.column.grid > [class*="eleven wide mobile"].column {
-    width: 68.75% !important;
-  }
-
-  .ui.grid > .row > [class*="twelve wide mobile"].column,
-  .ui.grid > .column.row > [class*="twelve wide mobile"].column,
-  .ui.grid > [class*="twelve wide mobile"].column,
-  .ui.column.grid > [class*="twelve wide mobile"].column {
-    width: 75% !important;
-  }
-
-  .ui.grid > .row > [class*="thirteen wide mobile"].column,
-  .ui.grid > .column.row > [class*="thirteen wide mobile"].column,
-  .ui.grid > [class*="thirteen wide mobile"].column,
-  .ui.column.grid > [class*="thirteen wide mobile"].column {
-    width: 81.25% !important;
-  }
-
-  .ui.grid > .row > [class*="fourteen wide mobile"].column,
-  .ui.grid > .column.row > [class*="fourteen wide mobile"].column,
-  .ui.grid > [class*="fourteen wide mobile"].column,
-  .ui.column.grid > [class*="fourteen wide mobile"].column {
-    width: 87.5% !important;
-  }
-
-  .ui.grid > .row > [class*="fifteen wide mobile"].column,
-  .ui.grid > .column.row > [class*="fifteen wide mobile"].column,
-  .ui.grid > [class*="fifteen wide mobile"].column,
-  .ui.column.grid > [class*="fifteen wide mobile"].column {
-    width: 93.75% !important;
-  }
-
-  .ui.grid > .row > [class*="sixteen wide mobile"].column,
-  .ui.grid > .column.row > [class*="sixteen wide mobile"].column,
-  .ui.grid > [class*="sixteen wide mobile"].column,
-  .ui.column.grid > [class*="sixteen wide mobile"].column {
-    width: 100% !important;
-  }
-}
-
-/* Tablet Sizing Combinations */
-
-@media only screen and (min-width: 768px) and (max-width: 991.98px) {
-  .ui.grid > .row > [class*="one wide tablet"].column,
-  .ui.grid > .column.row > [class*="one wide tablet"].column,
-  .ui.grid > [class*="one wide tablet"].column,
-  .ui.column.grid > [class*="one wide tablet"].column {
-    width: 6.25% !important;
-  }
-
-  .ui.grid > .row > [class*="two wide tablet"].column,
-  .ui.grid > .column.row > [class*="two wide tablet"].column,
-  .ui.grid > [class*="two wide tablet"].column,
-  .ui.column.grid > [class*="two wide tablet"].column {
-    width: 12.5% !important;
-  }
-
-  .ui.grid > .row > [class*="three wide tablet"].column,
-  .ui.grid > .column.row > [class*="three wide tablet"].column,
-  .ui.grid > [class*="three wide tablet"].column,
-  .ui.column.grid > [class*="three wide tablet"].column {
-    width: 18.75% !important;
-  }
-
-  .ui.grid > .row > [class*="four wide tablet"].column,
-  .ui.grid > .column.row > [class*="four wide tablet"].column,
-  .ui.grid > [class*="four wide tablet"].column,
-  .ui.column.grid > [class*="four wide tablet"].column {
-    width: 25% !important;
-  }
-
-  .ui.grid > .row > [class*="five wide tablet"].column,
-  .ui.grid > .column.row > [class*="five wide tablet"].column,
-  .ui.grid > [class*="five wide tablet"].column,
-  .ui.column.grid > [class*="five wide tablet"].column {
-    width: 31.25% !important;
-  }
-
-  .ui.grid > .row > [class*="six wide tablet"].column,
-  .ui.grid > .column.row > [class*="six wide tablet"].column,
-  .ui.grid > [class*="six wide tablet"].column,
-  .ui.column.grid > [class*="six wide tablet"].column {
-    width: 37.5% !important;
-  }
-
-  .ui.grid > .row > [class*="seven wide tablet"].column,
-  .ui.grid > .column.row > [class*="seven wide tablet"].column,
-  .ui.grid > [class*="seven wide tablet"].column,
-  .ui.column.grid > [class*="seven wide tablet"].column {
-    width: 43.75% !important;
-  }
-
-  .ui.grid > .row > [class*="eight wide tablet"].column,
-  .ui.grid > .column.row > [class*="eight wide tablet"].column,
-  .ui.grid > [class*="eight wide tablet"].column,
-  .ui.column.grid > [class*="eight wide tablet"].column {
-    width: 50% !important;
-  }
-
-  .ui.grid > .row > [class*="nine wide tablet"].column,
-  .ui.grid > .column.row > [class*="nine wide tablet"].column,
-  .ui.grid > [class*="nine wide tablet"].column,
-  .ui.column.grid > [class*="nine wide tablet"].column {
-    width: 56.25% !important;
-  }
-
-  .ui.grid > .row > [class*="ten wide tablet"].column,
-  .ui.grid > .column.row > [class*="ten wide tablet"].column,
-  .ui.grid > [class*="ten wide tablet"].column,
-  .ui.column.grid > [class*="ten wide tablet"].column {
-    width: 62.5% !important;
-  }
-
-  .ui.grid > .row > [class*="eleven wide tablet"].column,
-  .ui.grid > .column.row > [class*="eleven wide tablet"].column,
-  .ui.grid > [class*="eleven wide tablet"].column,
-  .ui.column.grid > [class*="eleven wide tablet"].column {
-    width: 68.75% !important;
-  }
-
-  .ui.grid > .row > [class*="twelve wide tablet"].column,
-  .ui.grid > .column.row > [class*="twelve wide tablet"].column,
-  .ui.grid > [class*="twelve wide tablet"].column,
-  .ui.column.grid > [class*="twelve wide tablet"].column {
-    width: 75% !important;
-  }
-
-  .ui.grid > .row > [class*="thirteen wide tablet"].column,
-  .ui.grid > .column.row > [class*="thirteen wide tablet"].column,
-  .ui.grid > [class*="thirteen wide tablet"].column,
-  .ui.column.grid > [class*="thirteen wide tablet"].column {
-    width: 81.25% !important;
-  }
-
-  .ui.grid > .row > [class*="fourteen wide tablet"].column,
-  .ui.grid > .column.row > [class*="fourteen wide tablet"].column,
-  .ui.grid > [class*="fourteen wide tablet"].column,
-  .ui.column.grid > [class*="fourteen wide tablet"].column {
-    width: 87.5% !important;
-  }
-
-  .ui.grid > .row > [class*="fifteen wide tablet"].column,
-  .ui.grid > .column.row > [class*="fifteen wide tablet"].column,
-  .ui.grid > [class*="fifteen wide tablet"].column,
-  .ui.column.grid > [class*="fifteen wide tablet"].column {
-    width: 93.75% !important;
-  }
-
-  .ui.grid > .row > [class*="sixteen wide tablet"].column,
-  .ui.grid > .column.row > [class*="sixteen wide tablet"].column,
-  .ui.grid > [class*="sixteen wide tablet"].column,
-  .ui.column.grid > [class*="sixteen wide tablet"].column {
-    width: 100% !important;
-  }
-}
-
-/* Computer/Desktop Sizing Combinations */
-
-@media only screen and (min-width: 992px) {
-  .ui.grid > .row > [class*="one wide computer"].column,
-  .ui.grid > .column.row > [class*="one wide computer"].column,
-  .ui.grid > [class*="one wide computer"].column,
-  .ui.column.grid > [class*="one wide computer"].column {
-    width: 6.25% !important;
-  }
-
-  .ui.grid > .row > [class*="two wide computer"].column,
-  .ui.grid > .column.row > [class*="two wide computer"].column,
-  .ui.grid > [class*="two wide computer"].column,
-  .ui.column.grid > [class*="two wide computer"].column {
-    width: 12.5% !important;
-  }
-
-  .ui.grid > .row > [class*="three wide computer"].column,
-  .ui.grid > .column.row > [class*="three wide computer"].column,
-  .ui.grid > [class*="three wide computer"].column,
-  .ui.column.grid > [class*="three wide computer"].column {
-    width: 18.75% !important;
-  }
-
-  .ui.grid > .row > [class*="four wide computer"].column,
-  .ui.grid > .column.row > [class*="four wide computer"].column,
-  .ui.grid > [class*="four wide computer"].column,
-  .ui.column.grid > [class*="four wide computer"].column {
-    width: 25% !important;
-  }
-
-  .ui.grid > .row > [class*="five wide computer"].column,
-  .ui.grid > .column.row > [class*="five wide computer"].column,
-  .ui.grid > [class*="five wide computer"].column,
-  .ui.column.grid > [class*="five wide computer"].column {
-    width: 31.25% !important;
-  }
-
-  .ui.grid > .row > [class*="six wide computer"].column,
-  .ui.grid > .column.row > [class*="six wide computer"].column,
-  .ui.grid > [class*="six wide computer"].column,
-  .ui.column.grid > [class*="six wide computer"].column {
-    width: 37.5% !important;
-  }
-
-  .ui.grid > .row > [class*="seven wide computer"].column,
-  .ui.grid > .column.row > [class*="seven wide computer"].column,
-  .ui.grid > [class*="seven wide computer"].column,
-  .ui.column.grid > [class*="seven wide computer"].column {
-    width: 43.75% !important;
-  }
-
-  .ui.grid > .row > [class*="eight wide computer"].column,
-  .ui.grid > .column.row > [class*="eight wide computer"].column,
-  .ui.grid > [class*="eight wide computer"].column,
-  .ui.column.grid > [class*="eight wide computer"].column {
-    width: 50% !important;
-  }
-
-  .ui.grid > .row > [class*="nine wide computer"].column,
-  .ui.grid > .column.row > [class*="nine wide computer"].column,
-  .ui.grid > [class*="nine wide computer"].column,
-  .ui.column.grid > [class*="nine wide computer"].column {
-    width: 56.25% !important;
-  }
-
-  .ui.grid > .row > [class*="ten wide computer"].column,
-  .ui.grid > .column.row > [class*="ten wide computer"].column,
-  .ui.grid > [class*="ten wide computer"].column,
-  .ui.column.grid > [class*="ten wide computer"].column {
-    width: 62.5% !important;
-  }
-
-  .ui.grid > .row > [class*="eleven wide computer"].column,
-  .ui.grid > .column.row > [class*="eleven wide computer"].column,
-  .ui.grid > [class*="eleven wide computer"].column,
-  .ui.column.grid > [class*="eleven wide computer"].column {
-    width: 68.75% !important;
-  }
-
-  .ui.grid > .row > [class*="twelve wide computer"].column,
-  .ui.grid > .column.row > [class*="twelve wide computer"].column,
-  .ui.grid > [class*="twelve wide computer"].column,
-  .ui.column.grid > [class*="twelve wide computer"].column {
-    width: 75% !important;
-  }
-
-  .ui.grid > .row > [class*="thirteen wide computer"].column,
-  .ui.grid > .column.row > [class*="thirteen wide computer"].column,
-  .ui.grid > [class*="thirteen wide computer"].column,
-  .ui.column.grid > [class*="thirteen wide computer"].column {
-    width: 81.25% !important;
-  }
-
-  .ui.grid > .row > [class*="fourteen wide computer"].column,
-  .ui.grid > .column.row > [class*="fourteen wide computer"].column,
-  .ui.grid > [class*="fourteen wide computer"].column,
-  .ui.column.grid > [class*="fourteen wide computer"].column {
-    width: 87.5% !important;
-  }
-
-  .ui.grid > .row > [class*="fifteen wide computer"].column,
-  .ui.grid > .column.row > [class*="fifteen wide computer"].column,
-  .ui.grid > [class*="fifteen wide computer"].column,
-  .ui.column.grid > [class*="fifteen wide computer"].column {
-    width: 93.75% !important;
-  }
-
-  .ui.grid > .row > [class*="sixteen wide computer"].column,
-  .ui.grid > .column.row > [class*="sixteen wide computer"].column,
-  .ui.grid > [class*="sixteen wide computer"].column,
-  .ui.column.grid > [class*="sixteen wide computer"].column {
-    width: 100% !important;
-  }
-}
-
-/* Large Monitor Sizing Combinations */
-
-@media only screen and (min-width: 1200px) and (max-width: 1919.98px) {
-  .ui.grid > .row > [class*="one wide large screen"].column,
-  .ui.grid > .column.row > [class*="one wide large screen"].column,
-  .ui.grid > [class*="one wide large screen"].column,
-  .ui.column.grid > [class*="one wide large screen"].column {
-    width: 6.25% !important;
-  }
-
-  .ui.grid > .row > [class*="two wide large screen"].column,
-  .ui.grid > .column.row > [class*="two wide large screen"].column,
-  .ui.grid > [class*="two wide large screen"].column,
-  .ui.column.grid > [class*="two wide large screen"].column {
-    width: 12.5% !important;
-  }
-
-  .ui.grid > .row > [class*="three wide large screen"].column,
-  .ui.grid > .column.row > [class*="three wide large screen"].column,
-  .ui.grid > [class*="three wide large screen"].column,
-  .ui.column.grid > [class*="three wide large screen"].column {
-    width: 18.75% !important;
-  }
-
-  .ui.grid > .row > [class*="four wide large screen"].column,
-  .ui.grid > .column.row > [class*="four wide large screen"].column,
-  .ui.grid > [class*="four wide large screen"].column,
-  .ui.column.grid > [class*="four wide large screen"].column {
-    width: 25% !important;
-  }
-
-  .ui.grid > .row > [class*="five wide large screen"].column,
-  .ui.grid > .column.row > [class*="five wide large screen"].column,
-  .ui.grid > [class*="five wide large screen"].column,
-  .ui.column.grid > [class*="five wide large screen"].column {
-    width: 31.25% !important;
-  }
-
-  .ui.grid > .row > [class*="six wide large screen"].column,
-  .ui.grid > .column.row > [class*="six wide large screen"].column,
-  .ui.grid > [class*="six wide large screen"].column,
-  .ui.column.grid > [class*="six wide large screen"].column {
-    width: 37.5% !important;
-  }
-
-  .ui.grid > .row > [class*="seven wide large screen"].column,
-  .ui.grid > .column.row > [class*="seven wide large screen"].column,
-  .ui.grid > [class*="seven wide large screen"].column,
-  .ui.column.grid > [class*="seven wide large screen"].column {
-    width: 43.75% !important;
-  }
-
-  .ui.grid > .row > [class*="eight wide large screen"].column,
-  .ui.grid > .column.row > [class*="eight wide large screen"].column,
-  .ui.grid > [class*="eight wide large screen"].column,
-  .ui.column.grid > [class*="eight wide large screen"].column {
-    width: 50% !important;
-  }
-
-  .ui.grid > .row > [class*="nine wide large screen"].column,
-  .ui.grid > .column.row > [class*="nine wide large screen"].column,
-  .ui.grid > [class*="nine wide large screen"].column,
-  .ui.column.grid > [class*="nine wide large screen"].column {
-    width: 56.25% !important;
-  }
-
-  .ui.grid > .row > [class*="ten wide large screen"].column,
-  .ui.grid > .column.row > [class*="ten wide large screen"].column,
-  .ui.grid > [class*="ten wide large screen"].column,
-  .ui.column.grid > [class*="ten wide large screen"].column {
-    width: 62.5% !important;
-  }
-
-  .ui.grid > .row > [class*="eleven wide large screen"].column,
-  .ui.grid > .column.row > [class*="eleven wide large screen"].column,
-  .ui.grid > [class*="eleven wide large screen"].column,
-  .ui.column.grid > [class*="eleven wide large screen"].column {
-    width: 68.75% !important;
-  }
-
-  .ui.grid > .row > [class*="twelve wide large screen"].column,
-  .ui.grid > .column.row > [class*="twelve wide large screen"].column,
-  .ui.grid > [class*="twelve wide large screen"].column,
-  .ui.column.grid > [class*="twelve wide large screen"].column {
-    width: 75% !important;
-  }
-
-  .ui.grid > .row > [class*="thirteen wide large screen"].column,
-  .ui.grid > .column.row > [class*="thirteen wide large screen"].column,
-  .ui.grid > [class*="thirteen wide large screen"].column,
-  .ui.column.grid > [class*="thirteen wide large screen"].column {
-    width: 81.25% !important;
-  }
-
-  .ui.grid > .row > [class*="fourteen wide large screen"].column,
-  .ui.grid > .column.row > [class*="fourteen wide large screen"].column,
-  .ui.grid > [class*="fourteen wide large screen"].column,
-  .ui.column.grid > [class*="fourteen wide large screen"].column {
-    width: 87.5% !important;
-  }
-
-  .ui.grid > .row > [class*="fifteen wide large screen"].column,
-  .ui.grid > .column.row > [class*="fifteen wide large screen"].column,
-  .ui.grid > [class*="fifteen wide large screen"].column,
-  .ui.column.grid > [class*="fifteen wide large screen"].column {
-    width: 93.75% !important;
-  }
-
-  .ui.grid > .row > [class*="sixteen wide large screen"].column,
-  .ui.grid > .column.row > [class*="sixteen wide large screen"].column,
-  .ui.grid > [class*="sixteen wide large screen"].column,
-  .ui.column.grid > [class*="sixteen wide large screen"].column {
-    width: 100% !important;
-  }
-}
-
-/* Widescreen Sizing Combinations */
-
-@media only screen and (min-width: 1920px) {
-  .ui.grid > .row > [class*="one wide widescreen"].column,
-  .ui.grid > .column.row > [class*="one wide widescreen"].column,
-  .ui.grid > [class*="one wide widescreen"].column,
-  .ui.column.grid > [class*="one wide widescreen"].column {
-    width: 6.25% !important;
-  }
-
-  .ui.grid > .row > [class*="two wide widescreen"].column,
-  .ui.grid > .column.row > [class*="two wide widescreen"].column,
-  .ui.grid > [class*="two wide widescreen"].column,
-  .ui.column.grid > [class*="two wide widescreen"].column {
-    width: 12.5% !important;
-  }
-
-  .ui.grid > .row > [class*="three wide widescreen"].column,
-  .ui.grid > .column.row > [class*="three wide widescreen"].column,
-  .ui.grid > [class*="three wide widescreen"].column,
-  .ui.column.grid > [class*="three wide widescreen"].column {
-    width: 18.75% !important;
-  }
-
-  .ui.grid > .row > [class*="four wide widescreen"].column,
-  .ui.grid > .column.row > [class*="four wide widescreen"].column,
-  .ui.grid > [class*="four wide widescreen"].column,
-  .ui.column.grid > [class*="four wide widescreen"].column {
-    width: 25% !important;
-  }
-
-  .ui.grid > .row > [class*="five wide widescreen"].column,
-  .ui.grid > .column.row > [class*="five wide widescreen"].column,
-  .ui.grid > [class*="five wide widescreen"].column,
-  .ui.column.grid > [class*="five wide widescreen"].column {
-    width: 31.25% !important;
-  }
-
-  .ui.grid > .row > [class*="six wide widescreen"].column,
-  .ui.grid > .column.row > [class*="six wide widescreen"].column,
-  .ui.grid > [class*="six wide widescreen"].column,
-  .ui.column.grid > [class*="six wide widescreen"].column {
-    width: 37.5% !important;
-  }
-
-  .ui.grid > .row > [class*="seven wide widescreen"].column,
-  .ui.grid > .column.row > [class*="seven wide widescreen"].column,
-  .ui.grid > [class*="seven wide widescreen"].column,
-  .ui.column.grid > [class*="seven wide widescreen"].column {
-    width: 43.75% !important;
-  }
-
-  .ui.grid > .row > [class*="eight wide widescreen"].column,
-  .ui.grid > .column.row > [class*="eight wide widescreen"].column,
-  .ui.grid > [class*="eight wide widescreen"].column,
-  .ui.column.grid > [class*="eight wide widescreen"].column {
-    width: 50% !important;
-  }
-
-  .ui.grid > .row > [class*="nine wide widescreen"].column,
-  .ui.grid > .column.row > [class*="nine wide widescreen"].column,
-  .ui.grid > [class*="nine wide widescreen"].column,
-  .ui.column.grid > [class*="nine wide widescreen"].column {
-    width: 56.25% !important;
-  }
-
-  .ui.grid > .row > [class*="ten wide widescreen"].column,
-  .ui.grid > .column.row > [class*="ten wide widescreen"].column,
-  .ui.grid > [class*="ten wide widescreen"].column,
-  .ui.column.grid > [class*="ten wide widescreen"].column {
-    width: 62.5% !important;
-  }
-
-  .ui.grid > .row > [class*="eleven wide widescreen"].column,
-  .ui.grid > .column.row > [class*="eleven wide widescreen"].column,
-  .ui.grid > [class*="eleven wide widescreen"].column,
-  .ui.column.grid > [class*="eleven wide widescreen"].column {
-    width: 68.75% !important;
-  }
-
-  .ui.grid > .row > [class*="twelve wide widescreen"].column,
-  .ui.grid > .column.row > [class*="twelve wide widescreen"].column,
-  .ui.grid > [class*="twelve wide widescreen"].column,
-  .ui.column.grid > [class*="twelve wide widescreen"].column {
-    width: 75% !important;
-  }
-
-  .ui.grid > .row > [class*="thirteen wide widescreen"].column,
-  .ui.grid > .column.row > [class*="thirteen wide widescreen"].column,
-  .ui.grid > [class*="thirteen wide widescreen"].column,
-  .ui.column.grid > [class*="thirteen wide widescreen"].column {
-    width: 81.25% !important;
-  }
-
-  .ui.grid > .row > [class*="fourteen wide widescreen"].column,
-  .ui.grid > .column.row > [class*="fourteen wide widescreen"].column,
-  .ui.grid > [class*="fourteen wide widescreen"].column,
-  .ui.column.grid > [class*="fourteen wide widescreen"].column {
-    width: 87.5% !important;
-  }
-
-  .ui.grid > .row > [class*="fifteen wide widescreen"].column,
-  .ui.grid > .column.row > [class*="fifteen wide widescreen"].column,
-  .ui.grid > [class*="fifteen wide widescreen"].column,
-  .ui.column.grid > [class*="fifteen wide widescreen"].column {
-    width: 93.75% !important;
-  }
-
-  .ui.grid > .row > [class*="sixteen wide widescreen"].column,
-  .ui.grid > .column.row > [class*="sixteen wide widescreen"].column,
-  .ui.grid > [class*="sixteen wide widescreen"].column,
-  .ui.column.grid > [class*="sixteen wide widescreen"].column {
-    width: 100% !important;
-  }
-}
-
-/*----------------------
-          Centered
-  -----------------------*/
-
-.ui.centered.grid,
-.ui.centered.grid > .row,
-.ui.grid > .centered.row {
-  text-align: center;
-  justify-content: center;
-}
-
-.ui.centered.grid > .column:not(.aligned):not(.justified):not(.row),
-.ui.centered.grid > .row > .column:not(.aligned):not(.justified),
-.ui.grid .centered.row > .column:not(.aligned):not(.justified) {
-  text-align: left;
-}
-
-.ui.grid > .centered.column,
-.ui.grid > .row > .centered.column {
-  display: block;
-  margin-left: auto;
-  margin-right: auto;
-}
-
-/*----------------------
-          Relaxed
-  -----------------------*/
-
-.ui.relaxed.grid > .column:not(.row),
-.ui.relaxed.grid > .row > .column,
-.ui.grid > .relaxed.row > .column {
-  padding-left: 1.5rem;
-  padding-right: 1.5rem;
-}
-
-.ui[class*="very relaxed"].grid > .column:not(.row),
-.ui[class*="very relaxed"].grid > .row > .column,
-.ui.grid > [class*="very relaxed"].row > .column {
-  padding-left: 2.5rem;
-  padding-right: 2.5rem;
-}
-
-/* Coupling with UI Divider */
-
-.ui.relaxed.grid .row + .ui.divider,
-.ui.grid .relaxed.row + .ui.divider {
-  margin-left: 1.5rem;
-  margin-right: 1.5rem;
-}
-
-.ui[class*="very relaxed"].grid .row + .ui.divider,
-.ui.grid [class*="very relaxed"].row + .ui.divider {
-  margin-left: 2.5rem;
-  margin-right: 2.5rem;
-}
-
-/*----------------------
-          Padded
-  -----------------------*/
-
-.ui.padded.grid:not(.vertically):not(.horizontally) {
-  margin: 0 !important;
-}
-
-[class*="horizontally padded"].ui.grid {
-  margin-left: 0 !important;
-  margin-right: 0 !important;
-}
-
-[class*="vertically padded"].ui.grid {
-  margin-top: 0 !important;
-  margin-bottom: 0 !important;
-}
-
-/*----------------------
-         "Floated"
-  -----------------------*/
-
-.ui.grid [class*="left floated"].column {
-  margin-right: auto;
-}
-
-.ui.grid [class*="right floated"].column {
-  margin-left: auto;
-}
-
-/*----------------------
-          Divided
-  -----------------------*/
-
-.ui.divided.grid:not([class*="vertically divided"]) > .column:not(.row),
-.ui.divided.grid:not([class*="vertically divided"]) > .row > .column {
-  box-shadow: -1px 0 0 0 rgba(34, 36, 38, 0.15);
-}
-
-/* Swap from padding to margin on columns to have dividers align */
-
-.ui[class*="vertically divided"].grid > .column:not(.row),
-.ui[class*="vertically divided"].grid > .row > .column {
-  margin-top: 1rem;
-  margin-bottom: 1rem;
-  padding-top: 0;
-  padding-bottom: 0;
-}
-
-.ui[class*="vertically divided"].grid > .row {
-  margin-top: 0;
-  margin-bottom: 0;
-}
-
-/* No divider on first column on row */
-
-.ui.divided.grid:not([class*="vertically divided"]) > .column:first-child,
-.ui.divided.grid:not([class*="vertically divided"]) > .row > .column:first-child {
-  box-shadow: none;
-}
-
-/* No space on top of first row */
-
-.ui[class*="vertically divided"].grid > .row:first-child > .column {
-  margin-top: 0;
-}
-
-/* Divided Row */
-
-.ui.grid > .divided.row > .column {
-  box-shadow: -1px 0 0 0 rgba(34, 36, 38, 0.15);
-}
-
-.ui.grid > .divided.row > .column:first-child {
-  box-shadow: none;
-}
-
-/* Vertically Divided */
-
-.ui[class*="vertically divided"].grid > .row {
-  position: relative;
-}
-
-.ui[class*="vertically divided"].grid > .row:before {
-  position: absolute;
-  content: "";
-  top: 0;
-  left: 0;
-  width: calc(100% - 2rem);
-  height: 1px;
-  margin: 0 1rem;
-  box-shadow: 0 -1px 0 0 rgba(34, 36, 38, 0.15);
-}
-
-/* Padded Horizontally Divided */
-
-[class*="horizontally padded"].ui.divided.grid,
-.ui.padded.divided.grid:not(.vertically):not(.horizontally) {
-  width: 100%;
-}
-
-/* First Row Vertically Divided */
-
-.ui[class*="vertically divided"].grid > .row:first-child:before {
-  box-shadow: none;
-}
-
-/* Relaxed */
-
-.ui.relaxed[class*="vertically divided"].grid > .row:before {
-  margin-left: 1.5rem;
-  margin-right: 1.5rem;
-  width: calc(100% - 3rem);
-}
-
-.ui[class*="very relaxed"][class*="vertically divided"].grid > .row:before {
-  margin-left: 2.5rem;
-  margin-right: 2.5rem;
-  width: calc(100% - 5rem);
-}
-
-/*----------------------
-           Celled
-  -----------------------*/
-
-.ui.celled.grid {
-  width: 100%;
-  margin: 1em 0;
-  box-shadow: 0 0 0 1px #D4D4D5;
-}
-
-.ui.celled.grid > .row {
-  width: 100% !important;
-  margin: 0;
-  padding: 0;
-  box-shadow: 0 -1px 0 0 #D4D4D5;
-}
-
-.ui.celled.grid > .column:not(.row),
-.ui.celled.grid > .row > .column {
-  box-shadow: -1px 0 0 0 #D4D4D5;
-}
-
-.ui.celled.grid > .column:first-child,
-.ui.celled.grid > .row > .column:first-child {
-  box-shadow: none;
-}
-
-.ui.celled.grid > .column:not(.row),
-.ui.celled.grid > .row > .column {
-  padding: 1em;
-}
-
-.ui.relaxed.celled.grid > .column:not(.row),
-.ui.relaxed.celled.grid > .row > .column {
-  padding: 1.5em;
-}
-
-.ui[class*="very relaxed"].celled.grid > .column:not(.row),
-.ui[class*="very relaxed"].celled.grid > .row > .column {
-  padding: 2em;
-}
-
-/* Internally Celled */
-
-.ui[class*="internally celled"].grid {
-  box-shadow: none;
-  margin: 0;
-}
-
-.ui[class*="internally celled"].grid > .row:first-child {
-  box-shadow: none;
-}
-
-.ui[class*="internally celled"].grid > .row > .column:first-child {
-  box-shadow: none;
-}
-
-/*----------------------
-     Vertically Aligned
-  -----------------------*/
-
-/* Top Aligned */
-
-.ui[class*="top aligned"].grid > .column:not(.row),
-.ui[class*="top aligned"].grid > .row > .column,
-.ui.grid > [class*="top aligned"].row > .column,
-.ui.grid > [class*="top aligned"].column:not(.row),
-.ui.grid > .row > [class*="top aligned"].column {
-  flex-direction: column;
-  vertical-align: top;
-  align-self: flex-start !important;
-}
-
-/* Middle Aligned */
-
-.ui[class*="middle aligned"].grid > .column:not(.row),
-.ui[class*="middle aligned"].grid > .row > .column,
-.ui.grid > [class*="middle aligned"].row > .column,
-.ui.grid > [class*="middle aligned"].column:not(.row),
-.ui.grid > .row > [class*="middle aligned"].column {
-  flex-direction: column;
-  vertical-align: middle;
-  align-self: center !important;
-}
-
-/* Bottom Aligned */
-
-.ui[class*="bottom aligned"].grid > .column:not(.row),
-.ui[class*="bottom aligned"].grid > .row > .column,
-.ui.grid > [class*="bottom aligned"].row > .column,
-.ui.grid > [class*="bottom aligned"].column:not(.row),
-.ui.grid > .row > [class*="bottom aligned"].column {
-  flex-direction: column;
-  vertical-align: bottom;
-  align-self: flex-end !important;
-}
-
-/* Stretched */
-
-.ui.stretched.grid > .row > .column,
-.ui.stretched.grid > .column,
-.ui.grid > .stretched.row > .column,
-.ui.grid > .stretched.column:not(.row),
-.ui.grid > .row > .stretched.column {
-  display: inline-flex !important;
-  align-self: stretch;
-  flex-direction: column;
-}
-
-.ui.stretched.grid > .row > .column > *,
-.ui.stretched.grid > .column > *,
-.ui.grid > .stretched.row > .column > *,
-.ui.grid > .stretched.column:not(.row) > *,
-.ui.grid > .row > .stretched.column > * {
-  flex-grow: 1;
-}
-
-/*----------------------
-    Horizontally Centered
-  -----------------------*/
-
-/* Left Aligned */
-
-.ui[class*="left aligned"].grid > .column,
-.ui[class*="left aligned"].grid > .row > .column,
-.ui.grid > [class*="left aligned"].row > .column,
-.ui.grid > [class*="left aligned"].column.column,
-.ui.grid > .row > [class*="left aligned"].column.column {
-  text-align: left;
-  align-self: inherit;
-}
-
-/* Center Aligned */
-
-.ui[class*="center aligned"].grid > .column,
-.ui[class*="center aligned"].grid > .row > .column,
-.ui.grid > [class*="center aligned"].row > .column,
-.ui.grid > [class*="center aligned"].column.column,
-.ui.grid > .row > [class*="center aligned"].column.column {
-  text-align: center;
-  align-self: inherit;
-}
-
-.ui[class*="center aligned"].grid {
-  justify-content: center;
-}
-
-/* Right Aligned */
-
-.ui[class*="right aligned"].grid > .column,
-.ui[class*="right aligned"].grid > .row > .column,
-.ui.grid > [class*="right aligned"].row > .column,
-.ui.grid > [class*="right aligned"].column.column,
-.ui.grid > .row > [class*="right aligned"].column.column {
-  text-align: right;
-  align-self: inherit;
-}
-
-/* Justified */
-
-.ui.justified.grid > .column,
-.ui.justified.grid > .row > .column,
-.ui.grid > .justified.row > .column,
-.ui.grid > .justified.column.column,
-.ui.grid > .row > .justified.column.column {
-  text-align: justify;
-  -webkit-hyphens: auto;
-  hyphens: auto;
-}
-
-/*----------------------
-         Colored
------------------------*/
-
-.ui.grid > .primary.row,
-.ui.grid > .primary.column,
-.ui.grid > .row > .primary.column {
-  background-color: #2185D0;
-  color: #FFFFFF;
-}
-
-.ui.grid > .secondary.row,
-.ui.grid > .secondary.column,
-.ui.grid > .row > .secondary.column {
-  background-color: #1B1C1D;
-  color: #FFFFFF;
-}
-
-.ui.grid > .red.row,
-.ui.grid > .red.column,
-.ui.grid > .row > .red.column {
-  background-color: #DB2828;
-  color: #FFFFFF;
-}
-
-.ui.grid > .orange.row,
-.ui.grid > .orange.column,
-.ui.grid > .row > .orange.column {
-  background-color: #F2711C;
-  color: #FFFFFF;
-}
-
-.ui.grid > .yellow.row,
-.ui.grid > .yellow.column,
-.ui.grid > .row > .yellow.column {
-  background-color: #FBBD08;
-  color: #FFFFFF;
-}
-
-.ui.grid > .olive.row,
-.ui.grid > .olive.column,
-.ui.grid > .row > .olive.column {
-  background-color: #B5CC18;
-  color: #FFFFFF;
-}
-
-.ui.grid > .green.row,
-.ui.grid > .green.column,
-.ui.grid > .row > .green.column {
-  background-color: #21BA45;
-  color: #FFFFFF;
-}
-
-.ui.grid > .teal.row,
-.ui.grid > .teal.column,
-.ui.grid > .row > .teal.column {
-  background-color: #00B5AD;
-  color: #FFFFFF;
-}
-
-.ui.grid > .blue.row,
-.ui.grid > .blue.column,
-.ui.grid > .row > .blue.column {
-  background-color: #2185D0;
-  color: #FFFFFF;
-}
-
-.ui.grid > .violet.row,
-.ui.grid > .violet.column,
-.ui.grid > .row > .violet.column {
-  background-color: #6435C9;
-  color: #FFFFFF;
-}
-
-.ui.grid > .purple.row,
-.ui.grid > .purple.column,
-.ui.grid > .row > .purple.column {
-  background-color: #A333C8;
-  color: #FFFFFF;
-}
-
-.ui.grid > .pink.row,
-.ui.grid > .pink.column,
-.ui.grid > .row > .pink.column {
-  background-color: #E03997;
-  color: #FFFFFF;
-}
-
-.ui.grid > .brown.row,
-.ui.grid > .brown.column,
-.ui.grid > .row > .brown.column {
-  background-color: #A5673F;
-  color: #FFFFFF;
-}
-
-.ui.grid > .grey.row,
-.ui.grid > .grey.column,
-.ui.grid > .row > .grey.column {
-  background-color: #767676;
-  color: #FFFFFF;
-}
-
-.ui.grid > .black.row,
-.ui.grid > .black.column,
-.ui.grid > .row > .black.column {
-  background-color: #1B1C1D;
-  color: #FFFFFF;
-}
-
-/*----------------------
-      Equal Width
------------------------*/
-
-.ui[class*="equal width"].grid > .column:not(.row),
-.ui[class*="equal width"].grid > .row > .column,
-.ui.grid > [class*="equal width"].row > .column {
-  display: inline-block;
-  flex-grow: 1;
-}
-
-.ui[class*="equal width"].grid > .wide.column,
-.ui[class*="equal width"].grid > .row > .wide.column,
-.ui.grid > [class*="equal width"].row > .wide.column {
-  flex-grow: 0;
-}
-
-/*----------------------
-          Reverse
-  -----------------------*/
-
-/* Mobile */
-
-@media only screen and (max-width: 767.98px) {
-  .ui[class*="mobile reversed"].grid,
-  .ui[class*="mobile reversed"].grid > .row,
-  .ui.grid > [class*="mobile reversed"].row {
-    flex-direction: row-reverse;
-  }
-
-  .ui[class*="mobile vertically reversed"].grid,
-  .ui.stackable[class*="mobile reversed"] {
-    flex-direction: column-reverse;
-  }
-
-  /* Divided Reversed */
-
-  .ui[class*="mobile reversed"].divided.grid:not([class*="vertically divided"]) > .column:first-child,
-  .ui[class*="mobile reversed"].divided.grid:not([class*="vertically divided"]) > .row > .column:first-child {
-    box-shadow: -1px 0 0 0 rgba(34, 36, 38, 0.15);
-  }
-
-  .ui[class*="mobile reversed"].divided.grid:not([class*="vertically divided"]) > .column:last-child,
-  .ui[class*="mobile reversed"].divided.grid:not([class*="vertically divided"]) > .row > .column:last-child {
-    box-shadow: none;
-  }
-
-  /* Vertically Divided Reversed */
-
-  .ui.grid[class*="vertically divided"][class*="mobile vertically reversed"] > .row:first-child:before {
-    box-shadow: 0 -1px 0 0 rgba(34, 36, 38, 0.15);
-  }
-
-  .ui.grid[class*="vertically divided"][class*="mobile vertically reversed"] > .row:last-child:before {
-    box-shadow: none;
-  }
-
-  /* Celled Reversed */
-
-  .ui[class*="mobile reversed"].celled.grid > .row > .column:first-child {
-    box-shadow: -1px 0 0 0 #D4D4D5;
-  }
-
-  .ui[class*="mobile reversed"].celled.grid > .row > .column:last-child {
-    box-shadow: none;
-  }
-}
-
-/* Tablet */
-
-@media only screen and (min-width: 768px) and (max-width: 991.98px) {
-  .ui[class*="tablet reversed"].grid,
-  .ui[class*="tablet reversed"].grid > .row,
-  .ui.grid > [class*="tablet reversed"].row {
-    flex-direction: row-reverse;
-  }
-
-  .ui[class*="tablet vertically reversed"].grid {
-    flex-direction: column-reverse;
-  }
-
-  /* Divided Reversed */
-
-  .ui[class*="tablet reversed"].divided.grid:not([class*="vertically divided"]) > .column:first-child,
-  .ui[class*="tablet reversed"].divided.grid:not([class*="vertically divided"]) > .row > .column:first-child {
-    box-shadow: -1px 0 0 0 rgba(34, 36, 38, 0.15);
-  }
-
-  .ui[class*="tablet reversed"].divided.grid:not([class*="vertically divided"]) > .column:last-child,
-  .ui[class*="tablet reversed"].divided.grid:not([class*="vertically divided"]) > .row > .column:last-child {
-    box-shadow: none;
-  }
-
-  /* Vertically Divided Reversed */
-
-  .ui.grid[class*="vertically divided"][class*="tablet vertically reversed"] > .row:first-child:before {
-    box-shadow: 0 -1px 0 0 rgba(34, 36, 38, 0.15);
-  }
-
-  .ui.grid[class*="vertically divided"][class*="tablet vertically reversed"] > .row:last-child:before {
-    box-shadow: none;
-  }
-
-  /* Celled Reversed */
-
-  .ui[class*="tablet reversed"].celled.grid > .row > .column:first-child {
-    box-shadow: -1px 0 0 0 #D4D4D5;
-  }
-
-  .ui[class*="tablet reversed"].celled.grid > .row > .column:last-child {
-    box-shadow: none;
-  }
-}
-
-/* Computer */
-
-@media only screen and (min-width: 992px) {
-  .ui[class*="computer reversed"].grid,
-  .ui[class*="computer reversed"].grid > .row,
-  .ui.grid > [class*="computer reversed"].row {
-    flex-direction: row-reverse;
-  }
-
-  .ui[class*="computer vertically reversed"].grid {
-    flex-direction: column-reverse;
-  }
-
-  /* Divided Reversed */
-
-  .ui[class*="computer reversed"].divided.grid:not([class*="vertically divided"]) > .column:first-child,
-  .ui[class*="computer reversed"].divided.grid:not([class*="vertically divided"]) > .row > .column:first-child {
-    box-shadow: -1px 0 0 0 rgba(34, 36, 38, 0.15);
-  }
-
-  .ui[class*="computer reversed"].divided.grid:not([class*="vertically divided"]) > .column:last-child,
-  .ui[class*="computer reversed"].divided.grid:not([class*="vertically divided"]) > .row > .column:last-child {
-    box-shadow: none;
-  }
-
-  /* Vertically Divided Reversed */
-
-  .ui.grid[class*="vertically divided"][class*="computer vertically reversed"] > .row:first-child:before {
-    box-shadow: 0 -1px 0 0 rgba(34, 36, 38, 0.15);
-  }
-
-  .ui.grid[class*="vertically divided"][class*="computer vertically reversed"] > .row:last-child:before {
-    box-shadow: none;
-  }
-
-  /* Celled Reversed */
-
-  .ui[class*="computer reversed"].celled.grid > .row > .column:first-child {
-    box-shadow: -1px 0 0 0 #D4D4D5;
-  }
-
-  .ui[class*="computer reversed"].celled.grid > .row > .column:last-child {
-    box-shadow: none;
-  }
-}
-
-/*-------------------
-        Stackable
-  --------------------*/
-
-@media only screen and (max-width: 767.98px) {
-  .ui.stackable.grid {
-    width: auto;
-    margin-left: 0 !important;
-    margin-right: 0 !important;
-  }
-
-  .ui.stackable.grid > .row > .wide.column,
-  .ui.stackable.grid > .wide.column,
-  .ui.stackable.grid > .column.grid > .column,
-  .ui.stackable.grid > .column.row > .column,
-  .ui.stackable.grid > .row > .column,
-  .ui.stackable.grid > .column:not(.row),
-  .ui.grid > .stackable.stackable.stackable.row > .column {
-    width: 100% !important;
-    margin: 0 0 !important;
-    box-shadow: none !important;
-    padding: 1rem 1rem;
-  }
-
-  .ui.stackable.grid:not(.vertically) > .row {
-    margin: 0;
-    padding: 0;
-  }
-
-  /* Coupling */
-
-  .ui.container > .ui.stackable.grid > .column,
-  .ui.container > .ui.stackable.grid > .row > .column {
-    padding-left: 0 !important;
-    padding-right: 0 !important;
-  }
-
-  /* Don't pad inside segment or nested grid */
-
-  .ui.grid .ui.stackable.grid,
-  .ui.segment:not(.vertical) .ui.stackable.page.grid {
-    margin-left: -1rem !important;
-    margin-right: -1rem !important;
-  }
-
-  /* Divided Stackable */
-
-  .ui.stackable.divided.grid > .row:first-child > .column:first-child,
-  .ui.stackable.celled.grid > .row:first-child > .column:first-child,
-  .ui.stackable.divided.grid > .column:not(.row):first-child,
-  .ui.stackable.celled.grid > .column:not(.row):first-child {
-    border-top: none !important;
-  }
-
-  .ui.stackable.celled.grid > .column:not(.row),
-  .ui.stackable.divided:not(.vertically).grid > .column:not(.row),
-  .ui.stackable.celled.grid > .row > .column,
-  .ui.stackable.divided:not(.vertically).grid > .row > .column {
-    border-top: 1px solid rgba(34, 36, 38, 0.15);
-    box-shadow: none !important;
-    padding-top: 2rem !important;
-    padding-bottom: 2rem !important;
-  }
-
-  .ui.stackable.celled.grid > .row {
-    box-shadow: none !important;
-  }
-
-  .ui.stackable.divided:not(.vertically).grid > .column:not(.row),
-  .ui.stackable.divided:not(.vertically).grid > .row > .column {
-    padding-left: 0 !important;
-    padding-right: 0 !important;
-  }
-}
-
-/*----------------------
-     Only (Device)
------------------------*/
-
-/* These include arbitrary class repetitions for forced specificity */
-
-/* Mobile Only Hide */
-
-@media only screen and (max-width: 767.98px) {
-  .ui[class*="tablet only"].grid.grid.grid:not(.mobile),
-  .ui.grid.grid.grid > [class*="tablet only"].row:not(.mobile),
-  .ui.grid.grid.grid > [class*="tablet only"].column:not(.mobile),
-  .ui.grid.grid.grid > .row > [class*="tablet only"].column:not(.mobile) {
-    display: none !important;
-  }
-
-  .ui[class*="computer only"].grid.grid.grid:not(.mobile),
-  .ui.grid.grid.grid > [class*="computer only"].row:not(.mobile),
-  .ui.grid.grid.grid > [class*="computer only"].column:not(.mobile),
-  .ui.grid.grid.grid > .row > [class*="computer only"].column:not(.mobile) {
-    display: none !important;
-  }
-
-  .ui[class*="large screen only"].grid.grid.grid:not(.mobile),
-  .ui.grid.grid.grid > [class*="large screen only"].row:not(.mobile),
-  .ui.grid.grid.grid > [class*="large screen only"].column:not(.mobile),
-  .ui.grid.grid.grid > .row > [class*="large screen only"].column:not(.mobile) {
-    display: none !important;
-  }
-
-  .ui[class*="widescreen only"].grid.grid.grid:not(.mobile),
-  .ui.grid.grid.grid > [class*="widescreen only"].row:not(.mobile),
-  .ui.grid.grid.grid > [class*="widescreen only"].column:not(.mobile),
-  .ui.grid.grid.grid > .row > [class*="widescreen only"].column:not(.mobile) {
-    display: none !important;
-  }
-}
-
-/* Tablet Only Hide */
-
-@media only screen and (min-width: 768px) and (max-width: 991.98px) {
-  .ui[class*="mobile only"].grid.grid.grid:not(.tablet),
-  .ui.grid.grid.grid > [class*="mobile only"].row:not(.tablet),
-  .ui.grid.grid.grid > [class*="mobile only"].column:not(.tablet),
-  .ui.grid.grid.grid > .row > [class*="mobile only"].column:not(.tablet) {
-    display: none !important;
-  }
-
-  .ui[class*="computer only"].grid.grid.grid:not(.tablet),
-  .ui.grid.grid.grid > [class*="computer only"].row:not(.tablet),
-  .ui.grid.grid.grid > [class*="computer only"].column:not(.tablet),
-  .ui.grid.grid.grid > .row > [class*="computer only"].column:not(.tablet) {
-    display: none !important;
-  }
-
-  .ui[class*="large screen only"].grid.grid.grid:not(.mobile),
-  .ui.grid.grid.grid > [class*="large screen only"].row:not(.mobile),
-  .ui.grid.grid.grid > [class*="large screen only"].column:not(.mobile),
-  .ui.grid.grid.grid > .row > [class*="large screen only"].column:not(.mobile) {
-    display: none !important;
-  }
-
-  .ui[class*="widescreen only"].grid.grid.grid:not(.mobile),
-  .ui.grid.grid.grid > [class*="widescreen only"].row:not(.mobile),
-  .ui.grid.grid.grid > [class*="widescreen only"].column:not(.mobile),
-  .ui.grid.grid.grid > .row > [class*="widescreen only"].column:not(.mobile) {
-    display: none !important;
-  }
-}
-
-/* Computer Only Hide */
-
-@media only screen and (min-width: 992px) and (max-width: 1199.98px) {
-  .ui[class*="mobile only"].grid.grid.grid:not(.computer),
-  .ui.grid.grid.grid > [class*="mobile only"].row:not(.computer),
-  .ui.grid.grid.grid > [class*="mobile only"].column:not(.computer),
-  .ui.grid.grid.grid > .row > [class*="mobile only"].column:not(.computer) {
-    display: none !important;
-  }
-
-  .ui[class*="tablet only"].grid.grid.grid:not(.computer),
-  .ui.grid.grid.grid > [class*="tablet only"].row:not(.computer),
-  .ui.grid.grid.grid > [class*="tablet only"].column:not(.computer),
-  .ui.grid.grid.grid > .row > [class*="tablet only"].column:not(.computer) {
-    display: none !important;
-  }
-
-  .ui[class*="large screen only"].grid.grid.grid:not(.mobile),
-  .ui.grid.grid.grid > [class*="large screen only"].row:not(.mobile),
-  .ui.grid.grid.grid > [class*="large screen only"].column:not(.mobile),
-  .ui.grid.grid.grid > .row > [class*="large screen only"].column:not(.mobile) {
-    display: none !important;
-  }
-
-  .ui[class*="widescreen only"].grid.grid.grid:not(.mobile),
-  .ui.grid.grid.grid > [class*="widescreen only"].row:not(.mobile),
-  .ui.grid.grid.grid > [class*="widescreen only"].column:not(.mobile),
-  .ui.grid.grid.grid > .row > [class*="widescreen only"].column:not(.mobile) {
-    display: none !important;
-  }
-}
-
-/* Large Screen Only Hide */
-
-@media only screen and (min-width: 1200px) and (max-width: 1919.98px) {
-  .ui[class*="mobile only"].grid.grid.grid:not(.computer),
-  .ui.grid.grid.grid > [class*="mobile only"].row:not(.computer),
-  .ui.grid.grid.grid > [class*="mobile only"].column:not(.computer),
-  .ui.grid.grid.grid > .row > [class*="mobile only"].column:not(.computer) {
-    display: none !important;
-  }
-
-  .ui[class*="tablet only"].grid.grid.grid:not(.computer),
-  .ui.grid.grid.grid > [class*="tablet only"].row:not(.computer),
-  .ui.grid.grid.grid > [class*="tablet only"].column:not(.computer),
-  .ui.grid.grid.grid > .row > [class*="tablet only"].column:not(.computer) {
-    display: none !important;
-  }
-
-  .ui[class*="widescreen only"].grid.grid.grid:not(.mobile),
-  .ui.grid.grid.grid > [class*="widescreen only"].row:not(.mobile),
-  .ui.grid.grid.grid > [class*="widescreen only"].column:not(.mobile),
-  .ui.grid.grid.grid > .row > [class*="widescreen only"].column:not(.mobile) {
-    display: none !important;
-  }
-}
-
-/* Widescreen Only Hide */
-
-@media only screen and (min-width: 1920px) {
-  .ui[class*="mobile only"].grid.grid.grid:not(.computer),
-  .ui.grid.grid.grid > [class*="mobile only"].row:not(.computer),
-  .ui.grid.grid.grid > [class*="mobile only"].column:not(.computer),
-  .ui.grid.grid.grid > .row > [class*="mobile only"].column:not(.computer) {
-    display: none !important;
-  }
-
-  .ui[class*="tablet only"].grid.grid.grid:not(.computer),
-  .ui.grid.grid.grid > [class*="tablet only"].row:not(.computer),
-  .ui.grid.grid.grid > [class*="tablet only"].column:not(.computer),
-  .ui.grid.grid.grid > .row > [class*="tablet only"].column:not(.computer) {
-    display: none !important;
-  }
-}
-
-/*-----------------
-        Compact
-  -----------------*/
-
-.ui.ui.ui.compact.grid > .column:not(.row),
-.ui.ui.ui.compact.grid > .row > .column {
-  padding-left: 0.5rem;
-  padding-right: 0.5rem;
-}
-
-.ui.ui.ui.compact.grid > * {
-  padding-left: 0.5rem;
-  padding-right: 0.5rem;
-}
-
-/* Row */
-
-.ui.ui.ui.compact.grid > .row {
-  padding-top: 0.5rem;
-  padding-bottom: 0.5rem;
-}
-
-/* Columns */
-
-.ui.ui.ui.compact.grid > .column:not(.row) {
-  padding-top: 0.5rem;
-  padding-bottom: 0.5rem;
-}
-
-/* Relaxed + Celled */
-
-.ui.compact.relaxed.celled.grid > .column:not(.row),
-.ui.compact.relaxed.celled.grid > .row > .column {
-  padding: 0.75em;
-}
-
-.ui.compact[class*="very relaxed"].celled.grid > .column:not(.row),
-.ui.compact[class*="very relaxed"].celled.grid > .row > .column {
-  padding: 1em;
-}
-
-/*-----------------
-      Very compact
-  -----------------*/
-
-.ui.ui.ui[class*="very compact"].grid > .column:not(.row),
-.ui.ui.ui[class*="very compact"].grid > .row > .column {
-  padding-left: 0.25rem;
-  padding-right: 0.25rem;
-}
-
-.ui.ui.ui[class*="very compact"].grid > * {
-  padding-left: 0.25rem;
-  padding-right: 0.25rem;
-}
-
-/* Row */
-
-.ui.ui.ui[class*="very compact"].grid > .row {
-  padding-top: 0.25rem;
-  padding-bottom: 0.25rem;
-  padding-left: 0.75rem;
-  padding-right: 0.75rem;
-}
-
-/* Columns */
-
-.ui.ui.ui[class*="very compact"].grid > .column:not(.row) {
-  padding-top: 0.25rem;
-  padding-bottom: 0.25rem;
-}
-
-/* Relaxed + Celled */
-
-.ui[class*="very compact"].relaxed.celled.grid > .column:not(.row),
-.ui[class*="very compact"].relaxed.celled.grid > .row > .column {
-  padding: 0.375em;
-}
-
-.ui[class*="very compact"][class*="very relaxed"].celled.grid > .column:not(.row),
-.ui[class*="very compact"][class*="very relaxed"].celled.grid > .row > .column {
-  padding: 0.5em;
-}
-
-/*******************************
-         Theme Overrides
-*******************************/
-
 /*******************************
          Site Overrides
 *******************************/
diff --git a/web_src/fomantic/semantic.json b/web_src/fomantic/semantic.json
index 6e2facf822..bd2ba15c62 100644
--- a/web_src/fomantic/semantic.json
+++ b/web_src/fomantic/semantic.json
@@ -28,7 +28,6 @@
     "dimmer",
     "dropdown",
     "form",
-    "grid",
     "header",
     "input",
     "label",

From e8f3efe1da5b02d0883dea9545dbbcae826ae5b5 Mon Sep 17 00:00:00 2001
From: Lunny Xiao <xiaolunwen@gmail.com>
Date: Thu, 21 Mar 2024 07:02:53 +0800
Subject: [PATCH 31/74] Fix and rewrite markup anchor processing (#29931)

Fix #29877

---------

Co-authored-by: silverwind <me@silverwind.io>
(cherry picked from commit 76ec5410510f09b3ea2bfd2602fcb8f3251087b6)
---
 web_src/js/markup/anchors.js | 82 ++++++++++++++++++++++--------------
 1 file changed, 50 insertions(+), 32 deletions(-)

diff --git a/web_src/js/markup/anchors.js b/web_src/js/markup/anchors.js
index 6cf83eb428..dac877fd99 100644
--- a/web_src/js/markup/anchors.js
+++ b/web_src/js/markup/anchors.js
@@ -1,50 +1,68 @@
 import {svg} from '../svg.js';
 
-const headingSelector = '.markup h1, .markup h2, .markup h3, .markup h4, .markup h5, .markup h6';
-
 // scroll to anchor while respecting the `user-content` prefix that exists on the target
-function scrollToAnchor(hash, initial) {
+function scrollToAnchor(encodedId, initial) {
   // abort if the browser has already scrolled to another anchor during page load
-  if (initial && document.querySelector(':target')) return;
-  if (hash?.length <= 1) return;
-  const id = decodeURIComponent(hash.substring(1));
-  const el = document.getElementById(`user-content-${id}`);
-  if (el) {
-    el.scrollIntoView();
-  } else if (id.startsWith('user-content-')) { // compat for links with old 'user-content-' prefixed hashes
+  if (!encodedId || (initial && document.querySelector(':target'))) return;
+  const id = decodeURIComponent(encodedId);
+  let el = document.getElementById(`user-content-${id}`);
+
+  // check for matching user-generated `a[name]`
+  if (!el) {
+    const nameAnchors = document.getElementsByName(`user-content-${id}`);
+    if (nameAnchors.length) {
+      el = nameAnchors[0];
+    }
+  }
+
+  // compat for links with old 'user-content-' prefixed hashes
+  if (!el && id.startsWith('user-content-')) {
     const el = document.getElementById(id);
     if (el) el.scrollIntoView();
   }
+
+  if (el) {
+    el.scrollIntoView();
+  }
 }
 
 export function initMarkupAnchors() {
-  if (!document.querySelector('.markup')) return;
+  const markupEls = document.querySelectorAll('.markup');
+  if (!markupEls.length) return;
 
-  // create link icons for markup headings, the resulting link href will remove `user-content-`
-  for (const heading of document.querySelectorAll(headingSelector)) {
-    const originalId = heading.id.replace(/^user-content-/, '');
-    const a = document.createElement('a');
-    a.classList.add('anchor');
-    a.setAttribute('href', `#${encodeURIComponent(originalId)}`);
-    a.innerHTML = svg('octicon-link');
-    a.addEventListener('click', (e) => {
-      scrollToAnchor(e.currentTarget.getAttribute('href'), false);
-    });
-    heading.prepend(a);
-  }
+  for (const markupEl of markupEls) {
+    // create link icons for markup headings, the resulting link href will remove `user-content-`
+    for (const heading of markupEl.querySelectorAll(`:is(h1, h2, h3, h4, h5, h6`)) {
+      const originalId = heading.id.replace(/^user-content-/, '');
+      const a = document.createElement('a');
+      a.classList.add('anchor');
+      a.setAttribute('href', `#${encodeURIComponent(originalId)}`);
+      a.innerHTML = svg('octicon-link');
+      heading.prepend(a);
+    }
 
-  // handle user-defined `name` anchors like `[Link](#link)` linking to `<a name="link"></a>Link`
-  for (const a of document.querySelectorAll('.markup a[href^="#"]')) {
-    const href = a.getAttribute('href');
-    if (!href.startsWith('#user-content-')) continue;
-    const originalId = href.replace(/^#user-content-/, '');
-    a.setAttribute('href', `#${encodeURIComponent(originalId)}`);
-    if (a.closest('.markup').querySelectorAll(`a[name="${originalId}"]`).length !== 1) {
+    // remove `user-content-` prefix from links so they don't show in url bar when clicked
+    for (const a of markupEl.querySelectorAll('a[href^="#"]')) {
+      const href = a.getAttribute('href');
+      if (!href.startsWith('#user-content-')) continue;
+      const originalId = href.replace(/^#user-content-/, '');
+      a.setAttribute('href', `#${originalId}`);
+    }
+
+    // add `user-content-` prefix to user-generated `a[name]` link targets
+    // TODO: this prefix should be added in backend instead
+    for (const a of markupEl.querySelectorAll('a[name]')) {
+      const name = a.getAttribute('name');
+      if (!name) continue;
+      a.setAttribute('name', `user-content-${a.name}`);
+    }
+
+    for (const a of markupEl.querySelectorAll('a[href^="#"]')) {
       a.addEventListener('click', (e) => {
-        scrollToAnchor(e.currentTarget.getAttribute('href'), false);
+        scrollToAnchor(e.currentTarget.getAttribute('href')?.substring(1), false);
       });
     }
   }
 
-  scrollToAnchor(window.location.hash, true);
+  scrollToAnchor(window.location.hash.substring(1), true);
 }

From 01f729f7646d79aa31e0097fe045ac4a71a50bd8 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Thu, 21 Mar 2024 00:48:04 +0100
Subject: [PATCH 32/74] Misc color tweaks (#29943)

Minor color tweaks:

- Better text contrasts
- Better distinguish nav and header wrapper in light theme
- Input boxes are now white on light theme
- Slightly darker dark theme background

<img width="503" alt="Screenshot 2024-03-20 at 19 31 54"
src="https://github.com/go-gitea/gitea/assets/115237/c7802a84-2386-4332-bd91-f419473ff644">
<img width="510" alt="Screenshot 2024-03-20 at 19 32 24"
src="https://github.com/go-gitea/gitea/assets/115237/21d3529e-6e0a-413a-9e9e-a03bea2405eb">

(cherry picked from commit 444460ea807c6a669d1a1467510868335abee5fb)
---
 web_src/css/themes/theme-gitea-dark.css  | 12 ++++++------
 web_src/css/themes/theme-gitea-light.css | 18 +++++++++---------
 2 files changed, 15 insertions(+), 15 deletions(-)

diff --git a/web_src/css/themes/theme-gitea-dark.css b/web_src/css/themes/theme-gitea-dark.css
index 4e38d75f65..9cf8907e45 100644
--- a/web_src/css/themes/theme-gitea-dark.css
+++ b/web_src/css/themes/theme-gitea-dark.css
@@ -180,16 +180,16 @@
   --color-orange-badge-hover-bg: #f2711c4d;
   --color-git: #f05133;
   /* target-based colors */
-  --color-body: #1e2224;
+  --color-body: #1c1f25;
   --color-box-header: #1a1d1f;
   --color-box-body: #14171a;
   --color-box-body-highlight: #121517;
   --color-text-dark: #f8f8f9;
-  --color-text: #ced2d5;
-  --color-text-light: #bec4c8;
-  --color-text-light-1: #acb3b8;
-  --color-text-light-2: #8d969c;
-  --color-text-light-3: #747f87;
+  --color-text: #d1d5d8;
+  --color-text-light: #bdc3c7;
+  --color-text-light-1: #a8afb5;
+  --color-text-light-2: #929ba2;
+  --color-text-light-3: #7c8790;
   --color-footer: var(--color-nav-bg);
   --color-timeline: #353c42;
   --color-input-text: var(--color-text-dark);
diff --git a/web_src/css/themes/theme-gitea-light.css b/web_src/css/themes/theme-gitea-light.css
index eded03e371..2ac83eefed 100644
--- a/web_src/css/themes/theme-gitea-light.css
+++ b/web_src/css/themes/theme-gitea-light.css
@@ -184,20 +184,20 @@
   --color-box-header: #f1f3f5;
   --color-box-body: #ffffff;
   --color-box-body-highlight: #f4faff;
-  --color-text-dark: #03080d;
-  --color-text: #1c2126;
-  --color-text-light: #3c434a;
-  --color-text-light-1: #4b5259;
-  --color-text-light-2: #6a7178;
-  --color-text-light-3: #899097;
+  --color-text-dark: #01050a;
+  --color-text: #181c21;
+  --color-text-light: #30363b;
+  --color-text-light-1: #40474d;
+  --color-text-light-2: #5b6167;
+  --color-text-light-3: #747c84;
   --color-footer: var(--color-nav-bg);
   --color-timeline: #d0d7de;
   --color-input-text: var(--color-text-dark);
-  --color-input-background: #f8f9fb;
+  --color-input-background: #fff;
   --color-input-toggle-background: #d0d7de;
   --color-input-border: var(--color-secondary);
   --color-input-border-hover: var(--color-secondary-dark-1);
-  --color-header-wrapper: #fafbfc;
+  --color-header-wrapper: #f9fafb;
   --color-light: #00001706;
   --color-light-mimic-enabled: rgba(0, 0, 0, calc(6 / 255 * 222 / 255 / var(--opacity-disabled)));
   --color-light-border: #0000171d;
@@ -224,7 +224,7 @@
   --color-reaction-active-bg: var(--color-primary-light-6);
   --color-tooltip-text: #fbfdff;
   --color-tooltip-bg: #000017f0;
-  --color-nav-bg: #f8f9fb;
+  --color-nav-bg: #f6f7fa;
   --color-nav-hover-bg: var(--color-secondary-light-1);
   --color-nav-text: var(--color-text);
   --color-label-text: var(--color-text);

From 6dabb4112931d5475c728000f9e150164c3cd45b Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Thu, 21 Mar 2024 03:17:59 +0100
Subject: [PATCH 33/74] Exclude `routers/private/tests` from air (#29949)

Exclude this and reformat the toml option to multiline.

I wasn't able to get `exclude_regex` to work so it would include a
`tests` directory anywhere. I think that option only works on files.

(cherry picked from commit 3ee39db34efd532626d710de6717bf3c6255c10e)
---
 .air.toml | 11 ++++++++++-
 1 file changed, 10 insertions(+), 1 deletion(-)

diff --git a/.air.toml b/.air.toml
index d13f8c4f99..de97bd8b29 100644
--- a/.air.toml
+++ b/.air.toml
@@ -8,6 +8,15 @@ delay = 1000
 include_ext = ["go", "tmpl"]
 include_file = ["main.go"]
 include_dir = ["cmd", "models", "modules", "options", "routers", "services"]
-exclude_dir = ["modules/git/tests", "services/gitdiff/testdata", "modules/avatar/testdata", "models/fixtures", "models/migrations/fixtures", "modules/migration/file_format_testdata", "modules/avatar/identicon/testdata"]
+exclude_dir = [
+  "models/fixtures",
+  "models/migrations/fixtures",
+  "modules/avatar/identicon/testdata",
+  "modules/avatar/testdata",
+  "modules/git/tests",
+  "modules/migration/file_format_testdata",
+  "routers/private/tests",
+  "services/gitdiff/testdata",
+]
 exclude_regex = ["_test.go$", "_gen.go$"]
 stop_on_error = true

From cb656d1a433cd00cb58278cb27f72a5ee602c91c Mon Sep 17 00:00:00 2001
From: Lunny Xiao <xiaolunwen@gmail.com>
Date: Thu, 21 Mar 2024 16:48:08 +0800
Subject: [PATCH 34/74] Fix the bug that user may logout if he switch pages too
 fast (#29962)

This PR fixed a bug when the user switching pages too fast, he will
logout automatically.

The reason is that when the error is context cancelled, the previous
code think user hasn't login then the session will be deleted. Now it
will return the errors but not think it's not login.

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
(cherry picked from commit 82db9a2ba77d2a6c470b62be3c82b73c0a544fcc)
---
 services/auth/session.go | 26 +++++++++-----------------
 1 file changed, 9 insertions(+), 17 deletions(-)

diff --git a/services/auth/session.go b/services/auth/session.go
index d13813dcbe..35d97e42da 100644
--- a/services/auth/session.go
+++ b/services/auth/session.go
@@ -4,7 +4,6 @@
 package auth
 
 import (
-	"context"
 	"net/http"
 
 	user_model "code.gitea.io/gitea/models/user"
@@ -29,40 +28,33 @@ func (s *Session) Name() string {
 // object for that uid.
 // Returns nil if there is no user uid stored in the session.
 func (s *Session) Verify(req *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) (*user_model.User, error) {
-	user := SessionUser(req.Context(), sess)
-	if user != nil {
-		return user, nil
-	}
-	return nil, nil
-}
-
-// SessionUser returns the user object corresponding to the "uid" session variable.
-func SessionUser(ctx context.Context, sess SessionStore) *user_model.User {
 	if sess == nil {
-		return nil
+		return nil, nil
 	}
 
 	// Get user ID
 	uid := sess.Get("uid")
 	if uid == nil {
-		return nil
+		return nil, nil
 	}
 	log.Trace("Session Authorization: Found user[%d]", uid)
 
 	id, ok := uid.(int64)
 	if !ok {
-		return nil
+		return nil, nil
 	}
 
 	// Get user object
-	user, err := user_model.GetUserByID(ctx, id)
+	user, err := user_model.GetUserByID(req.Context(), id)
 	if err != nil {
 		if !user_model.IsErrUserNotExist(err) {
-			log.Error("GetUserById: %v", err)
+			log.Error("GetUserByID: %v", err)
+			// Return the err as-is to keep current signed-in session, in case the err is something like context.Canceled. Otherwise non-existing user (nil, nil) will make the caller clear the signed-in session.
+			return nil, err
 		}
-		return nil
+		return nil, nil
 	}
 
 	log.Trace("Session Authorization: Logged in user %-v", user)
-	return user
+	return user, nil
 }

From faed93370f0982972b55aefb32a2949123188dd9 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Thu, 21 Mar 2024 11:16:11 +0100
Subject: [PATCH 35/74] Fix JS error and improve error message styles (#29963)

Fixes: https://github.com/go-gitea/gitea/issues/29956. This error
exposed a existing bug in the code, it was just never noticed because
the jQuery expression evaluated without error before while the new one
doesn't.

Also improves error message styles:

Before:
<img width="1338" alt="Screenshot 2024-03-21 at 09 16 07"
src="https://github.com/go-gitea/gitea/assets/115237/1cc1ef89-ad94-491e-bbca-75387f7547a0">

After:
<img width="1335" alt="Screenshot 2024-03-21 at 09 15 44"
src="https://github.com/go-gitea/gitea/assets/115237/312efc79-5353-4e2a-a703-1bccd3c01736">

(cherry picked from commit 1a4f693f9f9723c181b747cb6d658aa37118005a)
---
 web_src/js/bootstrap.js            | 2 +-
 web_src/js/features/repo-common.js | 2 ++
 2 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/web_src/js/bootstrap.js b/web_src/js/bootstrap.js
index 698d17fa36..3034478190 100644
--- a/web_src/js/bootstrap.js
+++ b/web_src/js/bootstrap.js
@@ -23,7 +23,7 @@ export function showGlobalErrorMessage(msg) {
   let msgDiv = pageContent.querySelector(`.js-global-error[data-global-error-msg-compact="${msgCompact}"]`);
   if (!msgDiv) {
     const el = document.createElement('div');
-    el.innerHTML = `<div class="ui container negative message center aligned js-global-error" style="white-space: pre-line;"></div>`;
+    el.innerHTML = `<div class="ui container negative message center aligned js-global-error tw-mt-[15px] tw-whitespace-pre-line"></div>`;
     msgDiv = el.childNodes[0];
   }
   // merge duplicated messages into "the message (count)" format
diff --git a/web_src/js/features/repo-common.js b/web_src/js/features/repo-common.js
index 9e11ffe197..669b47a9c5 100644
--- a/web_src/js/features/repo-common.js
+++ b/web_src/js/features/repo-common.js
@@ -76,6 +76,8 @@ export function initRepoCommonBranchOrTagDropdown(selector) {
 
 export function initRepoCommonFilterSearchDropdown(selector) {
   const $dropdown = $(selector);
+  if (!$dropdown.length) return;
+
   $dropdown.dropdown({
     fullTextSearch: 'exact',
     selectOnKeydown: false,

From 0f4614004eeff3dff3faf02d993f310dcbd1651d Mon Sep 17 00:00:00 2001
From: HEREYUA <37935145+HEREYUA@users.noreply.github.com>
Date: Thu, 21 Mar 2024 18:38:27 +0800
Subject: [PATCH 36/74] Solving the issue of UI disruption when the review is
 deleted without refreshing (#29951)

**After deleting the review and refreshing, the display is normal.
However, Without refreshing, the interface will be broken**

https://github.com/go-gitea/gitea/assets/37935145/f5cb19a6-eb26-47b0-b8ee-15b575bbe1ac

**after**

https://github.com/go-gitea/gitea/assets/37935145/aa65922c-2ebf-4fce-ad91-35661f70329a

(cherry picked from commit 0b4ff15356769db092fd7718da553e8a216c32fa)
---
 web_src/js/features/repo-issue.js | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/web_src/js/features/repo-issue.js b/web_src/js/features/repo-issue.js
index bca062bcc7..ad2956a600 100644
--- a/web_src/js/features/repo-issue.js
+++ b/web_src/js/features/repo-issue.js
@@ -162,7 +162,8 @@ export function initRepoIssueCommentDelete() {
         const response = await POST($this.data('url'));
         if (!response.ok) throw new Error('Failed to delete comment');
         const $conversationHolder = $this.closest('.conversation-holder');
-
+        const $parentTimelineItem = $this.closest('.timeline-item');
+        const $parentTimelineGroup = $this.closest('.timeline-item-group');
         // Check if this was a pending comment.
         if ($conversationHolder.find('.pending-label').length) {
           const $counter = $('#review-box .review-comments-counter');
@@ -185,6 +186,11 @@ export function initRepoIssueCommentDelete() {
           }
           $conversationHolder.remove();
         }
+        // Check if there is no review content, move the time avatar upward to avoid overlapping the content below.
+        if (!$parentTimelineGroup.find('.timeline-item.comment').length && !$parentTimelineItem.find('.conversation-holder').length) {
+          const $timelineAvatar = $parentTimelineGroup.find('.timeline-avatar');
+          $timelineAvatar.removeClass('timeline-avatar-offset');
+        }
       } catch (error) {
         console.error(error);
       }

From d92c2048b30c9ca1eab87291ce6e053739db24c6 Mon Sep 17 00:00:00 2001
From: Lunny Xiao <xiaolunwen@gmail.com>
Date: Thu, 21 Mar 2024 21:13:08 +0800
Subject: [PATCH 37/74] Performance improvements for pull request list page
 (#29900)

This PR will avoid load pullrequest.Issue twice in pull request list
page. It will reduce x times database queries for those WIP pull
requests.

Partially fix #29585

---------

Co-authored-by: Giteabot <teabot@gitea.io>
(cherry picked from commit 62f8174aa2fae1481c7e17a6afcb731a5b178cd0)

Conflicts:
	models/activities/notification_list.go
	moved to models/activities/notification.go
---
 models/activities/notification.go     | 29 +++++++++++++++++++++++++++
 models/issues/issue.go                | 14 -------------
 models/issues/issue_list.go           |  3 +++
 models/issues/pull_list.go            |  9 +++++++++
 models/issues/review.go               |  9 ++++-----
 modules/util/slice.go                 | 11 +++++++++-
 routers/api/v1/repo/issue.go          |  5 +++--
 routers/api/v1/repo/issue_pin.go      | 16 +++++----------
 routers/web/user/notification.go      |  6 ++++++
 services/convert/notification.go      |  5 +++--
 services/pull/review.go               |  4 ++--
 templates/shared/issueicon.tmpl       | 16 +++++++--------
 tests/integration/pull_merge_test.go  |  4 ++--
 tests/integration/pull_update_test.go |  5 ++---
 14 files changed, 86 insertions(+), 50 deletions(-)

diff --git a/models/activities/notification.go b/models/activities/notification.go
index 4ca9f80270..211e84f418 100644
--- a/models/activities/notification.go
+++ b/models/activities/notification.go
@@ -20,6 +20,7 @@ import (
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/setting"
 	"code.gitea.io/gitea/modules/timeutil"
+	"code.gitea.io/gitea/modules/util"
 
 	"xorm.io/builder"
 )
@@ -841,3 +842,31 @@ func UpdateNotificationStatuses(ctx context.Context, user *user_model.User, curr
 		Update(n)
 	return err
 }
+
+// LoadIssuePullRequests loads all issues' pull requests if possible
+func (nl NotificationList) LoadIssuePullRequests(ctx context.Context) error {
+	issues := make(map[int64]*issues_model.Issue, len(nl))
+	for _, notification := range nl {
+		if notification.Issue != nil && notification.Issue.IsPull && notification.Issue.PullRequest == nil {
+			issues[notification.Issue.ID] = notification.Issue
+		}
+	}
+
+	if len(issues) == 0 {
+		return nil
+	}
+
+	pulls, err := issues_model.GetPullRequestByIssueIDs(ctx, util.KeysOfMap(issues))
+	if err != nil {
+		return err
+	}
+
+	for _, pull := range pulls {
+		if issue := issues[pull.IssueID]; issue != nil {
+			issue.PullRequest = pull
+			issue.PullRequest.Issue = issue
+		}
+	}
+
+	return nil
+}
diff --git a/models/issues/issue.go b/models/issues/issue.go
index 54ef5cec7a..11256f788a 100644
--- a/models/issues/issue.go
+++ b/models/issues/issue.go
@@ -194,20 +194,6 @@ func (issue *Issue) IsTimetrackerEnabled(ctx context.Context) bool {
 	return issue.Repo.IsTimetrackerEnabled(ctx)
 }
 
-// GetPullRequest returns the issue pull request
-func (issue *Issue) GetPullRequest(ctx context.Context) (pr *PullRequest, err error) {
-	if !issue.IsPull {
-		return nil, fmt.Errorf("Issue is not a pull request")
-	}
-
-	pr, err = GetPullRequestByIssueID(ctx, issue.ID)
-	if err != nil {
-		return nil, err
-	}
-	pr.Issue = issue
-	return pr, err
-}
-
 // LoadPoster loads poster
 func (issue *Issue) LoadPoster(ctx context.Context) (err error) {
 	if issue.Poster == nil && issue.PosterID != 0 {
diff --git a/models/issues/issue_list.go b/models/issues/issue_list.go
index 0fb8447ff7..9ccb93bf42 100644
--- a/models/issues/issue_list.go
+++ b/models/issues/issue_list.go
@@ -370,6 +370,9 @@ func (issues IssueList) LoadPullRequests(ctx context.Context) error {
 
 	for _, issue := range issues {
 		issue.PullRequest = pullRequestMaps[issue.ID]
+		if issue.PullRequest != nil {
+			issue.PullRequest.Issue = issue
+		}
 	}
 	return nil
 }
diff --git a/models/issues/pull_list.go b/models/issues/pull_list.go
index d539ed5403..6df15e8621 100644
--- a/models/issues/pull_list.go
+++ b/models/issues/pull_list.go
@@ -220,3 +220,12 @@ func HasMergedPullRequestInRepo(ctx context.Context, repoID, posterID int64) (bo
 		Limit(1).
 		Get(new(Issue))
 }
+
+// GetPullRequestByIssueIDs returns all pull requests by issue ids
+func GetPullRequestByIssueIDs(ctx context.Context, issueIDs []int64) (PullRequestList, error) {
+	prs := make([]*PullRequest, 0, len(issueIDs))
+	return prs, db.GetEngine(ctx).
+		Where("issue_id > 0").
+		In("issue_id", issueIDs).
+		Find(&prs)
+}
diff --git a/models/issues/review.go b/models/issues/review.go
index fc110630e0..70aba0f94d 100644
--- a/models/issues/review.go
+++ b/models/issues/review.go
@@ -239,11 +239,11 @@ type CreateReviewOptions struct {
 
 // IsOfficialReviewer check if at least one of the provided reviewers can make official reviews in issue (counts towards required approvals)
 func IsOfficialReviewer(ctx context.Context, issue *Issue, reviewer *user_model.User) (bool, error) {
-	pr, err := GetPullRequestByIssueID(ctx, issue.ID)
-	if err != nil {
+	if err := issue.LoadPullRequest(ctx); err != nil {
 		return false, err
 	}
 
+	pr := issue.PullRequest
 	rule, err := git_model.GetFirstMatchProtectedBranchRule(ctx, pr.BaseRepoID, pr.BaseBranch)
 	if err != nil {
 		return false, err
@@ -271,11 +271,10 @@ func IsOfficialReviewer(ctx context.Context, issue *Issue, reviewer *user_model.
 
 // IsOfficialReviewerTeam check if reviewer in this team can make official reviews in issue (counts towards required approvals)
 func IsOfficialReviewerTeam(ctx context.Context, issue *Issue, team *organization.Team) (bool, error) {
-	pr, err := GetPullRequestByIssueID(ctx, issue.ID)
-	if err != nil {
+	if err := issue.LoadPullRequest(ctx); err != nil {
 		return false, err
 	}
-	pb, err := git_model.GetFirstMatchProtectedBranchRule(ctx, pr.BaseRepoID, pr.BaseBranch)
+	pb, err := git_model.GetFirstMatchProtectedBranchRule(ctx, issue.PullRequest.BaseRepoID, issue.PullRequest.BaseBranch)
 	if err != nil {
 		return false, err
 	}
diff --git a/modules/util/slice.go b/modules/util/slice.go
index f00e84bf06..9c878c24be 100644
--- a/modules/util/slice.go
+++ b/modules/util/slice.go
@@ -54,7 +54,7 @@ func Sorted[S ~[]E, E cmp.Ordered](values S) S {
 	return values
 }
 
-// TODO: Replace with "maps.Values" once available
+// TODO: Replace with "maps.Values" once available, current it only in golang.org/x/exp/maps but not in standard library
 func ValuesOfMap[K comparable, V any](m map[K]V) []V {
 	values := make([]V, 0, len(m))
 	for _, v := range m {
@@ -62,3 +62,12 @@ func ValuesOfMap[K comparable, V any](m map[K]V) []V {
 	}
 	return values
 }
+
+// TODO: Replace with "maps.Keys" once available, current it only in golang.org/x/exp/maps but not in standard library
+func KeysOfMap[K comparable, V any](m map[K]V) []K {
+	keys := make([]K, 0, len(m))
+	for k := range m {
+		keys = append(keys, k)
+	}
+	return keys
+}
diff --git a/routers/api/v1/repo/issue.go b/routers/api/v1/repo/issue.go
index 843da55139..2640819a54 100644
--- a/routers/api/v1/repo/issue.go
+++ b/routers/api/v1/repo/issue.go
@@ -874,10 +874,11 @@ func EditIssue(ctx *context.APIContext) {
 	}
 	if form.State != nil {
 		if issue.IsPull {
-			if pr, err := issue.GetPullRequest(ctx); err != nil {
+			if err := issue.LoadPullRequest(ctx); err != nil {
 				ctx.Error(http.StatusInternalServerError, "GetPullRequest", err)
 				return
-			} else if pr.HasMerged {
+			}
+			if issue.PullRequest.HasMerged {
 				ctx.Error(http.StatusPreconditionFailed, "MergedPRState", "cannot change state of this pull request, it was already merged")
 				return
 			}
diff --git a/routers/api/v1/repo/issue_pin.go b/routers/api/v1/repo/issue_pin.go
index ff1135862b..8fcf670fd0 100644
--- a/routers/api/v1/repo/issue_pin.go
+++ b/routers/api/v1/repo/issue_pin.go
@@ -240,18 +240,12 @@ func ListPinnedPullRequests(ctx *context.APIContext) {
 	}
 
 	apiPrs := make([]*api.PullRequest, len(issues))
+	if err := issues.LoadPullRequests(ctx); err != nil {
+		ctx.Error(http.StatusInternalServerError, "LoadPullRequests", err)
+		return
+	}
 	for i, currentIssue := range issues {
-		pr, err := currentIssue.GetPullRequest(ctx)
-		if err != nil {
-			ctx.Error(http.StatusInternalServerError, "GetPullRequest", err)
-			return
-		}
-
-		if err = pr.LoadIssue(ctx); err != nil {
-			ctx.Error(http.StatusInternalServerError, "LoadIssue", err)
-			return
-		}
-
+		pr := currentIssue.PullRequest
 		if err = pr.LoadAttributes(ctx); err != nil {
 			ctx.Error(http.StatusInternalServerError, "LoadAttributes", err)
 			return
diff --git a/routers/web/user/notification.go b/routers/web/user/notification.go
index 324205ed91..a40c7085ff 100644
--- a/routers/web/user/notification.go
+++ b/routers/web/user/notification.go
@@ -144,6 +144,12 @@ func getNotifications(ctx *context.Context) {
 		ctx.ServerError("LoadIssues", err)
 		return
 	}
+
+	if err = notifications.LoadIssuePullRequests(ctx); err != nil {
+		ctx.ServerError("LoadIssuePullRequests", err)
+		return
+	}
+
 	notifications = notifications.Without(failures)
 	failCount += len(failures)
 
diff --git a/services/convert/notification.go b/services/convert/notification.go
index 0b97530d8b..41063cf399 100644
--- a/services/convert/notification.go
+++ b/services/convert/notification.go
@@ -61,8 +61,9 @@ func ToNotificationThread(ctx context.Context, n *activities_model.Notification)
 				result.Subject.LatestCommentHTMLURL = comment.HTMLURL(ctx)
 			}
 
-			pr, _ := n.Issue.GetPullRequest(ctx)
-			if pr != nil && pr.HasMerged {
+			if err := n.Issue.LoadPullRequest(ctx); err == nil &&
+				n.Issue.PullRequest != nil &&
+				n.Issue.PullRequest.HasMerged {
 				result.Subject.State = "merged"
 			}
 		}
diff --git a/services/pull/review.go b/services/pull/review.go
index 1220d54728..faac885273 100644
--- a/services/pull/review.go
+++ b/services/pull/review.go
@@ -268,11 +268,11 @@ func CreateCodeCommentKnownReviewID(ctx context.Context, doer *user_model.User,
 
 // SubmitReview creates a review out of the existing pending review or creates a new one if no pending review exist
 func SubmitReview(ctx context.Context, doer *user_model.User, gitRepo *git.Repository, issue *issues_model.Issue, reviewType issues_model.ReviewType, content, commitID string, attachmentUUIDs []string) (*issues_model.Review, *issues_model.Comment, error) {
-	pr, err := issue.GetPullRequest(ctx)
-	if err != nil {
+	if err := issue.LoadPullRequest(ctx); err != nil {
 		return nil, nil, err
 	}
 
+	pr := issue.PullRequest
 	var stale bool
 	if reviewType != issues_model.ReviewTypeApprove && reviewType != issues_model.ReviewTypeReject {
 		stale = false
diff --git a/templates/shared/issueicon.tmpl b/templates/shared/issueicon.tmpl
index 089e80bd8b..a62714e988 100644
--- a/templates/shared/issueicon.tmpl
+++ b/templates/shared/issueicon.tmpl
@@ -1,15 +1,15 @@
 {{if .IsPull}}
-	{{if and .PullRequest .PullRequest.HasMerged}}
-		{{svg "octicon-git-merge" 16 "text purple"}}
-	{{else if and (.GetPullRequest ctx) (.GetPullRequest ctx).HasMerged}}
-		{{svg "octicon-git-merge" 16 "text purple"}}
+	{{if not .PullRequest}}
+		No PullRequest
 	{{else}}
 		{{if .IsClosed}}
-			{{svg "octicon-git-pull-request" 16 "text red"}}
+			{{if .PullRequest.HasMerged}}
+				{{svg "octicon-git-merge" 16 "text purple"}}
+			{{else}}
+				{{svg "octicon-git-pull-request" 16 "text red"}}
+			{{end}}
 		{{else}}
-			{{if and .PullRequest (.PullRequest.IsWorkInProgress ctx)}}
-				{{svg "octicon-git-pull-request-draft" 16 "text grey"}}
-			{{else if and (.GetPullRequest ctx) ((.GetPullRequest ctx).IsWorkInProgress ctx)}}
+			{{if .PullRequest.IsWorkInProgress ctx}}
 				{{svg "octicon-git-pull-request-draft" 16 "text grey"}}
 			{{else}}
 				{{svg "octicon-git-pull-request" 16 "text green"}}
diff --git a/tests/integration/pull_merge_test.go b/tests/integration/pull_merge_test.go
index e79d99b052..c9ecadca9a 100644
--- a/tests/integration/pull_merge_test.go
+++ b/tests/integration/pull_merge_test.go
@@ -513,8 +513,8 @@ func TestConflictChecking(t *testing.T) {
 		assert.NoError(t, err)
 
 		issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{Title: "PR with conflict!"})
-		conflictingPR, err := issues_model.GetPullRequestByIssueID(db.DefaultContext, issue.ID)
-		assert.NoError(t, err)
+		assert.NoError(t, issue.LoadPullRequest(db.DefaultContext))
+		conflictingPR := issue.PullRequest
 
 		// Ensure conflictedFiles is populated.
 		assert.Len(t, conflictingPR.ConflictedFiles, 1)
diff --git a/tests/integration/pull_update_test.go b/tests/integration/pull_update_test.go
index 094813d367..d59d2641f5 100644
--- a/tests/integration/pull_update_test.go
+++ b/tests/integration/pull_update_test.go
@@ -167,8 +167,7 @@ func createOutdatedPR(t *testing.T, actor, forkOrg *user_model.User) *issues_mod
 	assert.NoError(t, err)
 
 	issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{Title: "Test Pull -to-update-"})
-	pr, err := issues_model.GetPullRequestByIssueID(db.DefaultContext, issue.ID)
-	assert.NoError(t, err)
+	assert.NoError(t, issue.LoadPullRequest(db.DefaultContext))
 
-	return pr
+	return issue.PullRequest
 }

From f8060bb5d96041ca09295714515001677fb9d829 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Thu, 21 Mar 2024 15:05:24 +0100
Subject: [PATCH 38/74] Switch to happy-dom for testing (#29948)

Use `happy-dom` again in vitest as it has caught up recently to `jsdom`
in terms of features and it is a much more lightweight solution.

I encountered [one
bug](https://github.com/capricorn86/happy-dom/issues/1342), but it's an
easy workaround until fixed.

I regenerated the lockfile to get rid of the transitive dependencies so
that's why the diff also has some upgrades in it.

In total, this change removes 39 npm dependencies.

(cherry picked from commit 82979588f4d8699097451ebb70c56a4bdd090c52)
---
 package-lock.json | 1383 +++++++++++----------------------------------
 package.json      |    2 +-
 vitest.config.js  |    2 +-
 web_src/js/svg.js |    2 +-
 4 files changed, 323 insertions(+), 1066 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index b87b1d5920..f6df16f2a5 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -85,7 +85,7 @@
         "eslint-plugin-vue": "9.23.0",
         "eslint-plugin-vue-scoped-css": "2.7.2",
         "eslint-plugin-wc": "2.0.4",
-        "jsdom": "24.0.0",
+        "happy-dom": "14.2.0",
         "markdownlint-cli": "0.39.0",
         "postcss-html": "1.6.0",
         "stylelint": "16.2.1",
@@ -130,81 +130,17 @@
       }
     },
     "node_modules/@babel/code-frame": {
-      "version": "7.23.5",
-      "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz",
-      "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==",
+      "version": "7.24.2",
+      "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz",
+      "integrity": "sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==",
       "dependencies": {
-        "@babel/highlight": "^7.23.4",
-        "chalk": "^2.4.2"
+        "@babel/highlight": "^7.24.2",
+        "picocolors": "^1.0.0"
       },
       "engines": {
         "node": ">=6.9.0"
       }
     },
-    "node_modules/@babel/code-frame/node_modules/ansi-styles": {
-      "version": "3.2.1",
-      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
-      "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
-      "dependencies": {
-        "color-convert": "^1.9.0"
-      },
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/@babel/code-frame/node_modules/chalk": {
-      "version": "2.4.2",
-      "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
-      "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
-      "dependencies": {
-        "ansi-styles": "^3.2.1",
-        "escape-string-regexp": "^1.0.5",
-        "supports-color": "^5.3.0"
-      },
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/@babel/code-frame/node_modules/color-convert": {
-      "version": "1.9.3",
-      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
-      "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
-      "dependencies": {
-        "color-name": "1.1.3"
-      }
-    },
-    "node_modules/@babel/code-frame/node_modules/color-name": {
-      "version": "1.1.3",
-      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
-      "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="
-    },
-    "node_modules/@babel/code-frame/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==",
-      "engines": {
-        "node": ">=0.8.0"
-      }
-    },
-    "node_modules/@babel/code-frame/node_modules/has-flag": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
-      "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/@babel/code-frame/node_modules/supports-color": {
-      "version": "5.5.0",
-      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
-      "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
-      "dependencies": {
-        "has-flag": "^3.0.0"
-      },
-      "engines": {
-        "node": ">=4"
-      }
-    },
     "node_modules/@babel/helper-validator-identifier": {
       "version": "7.22.20",
       "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz",
@@ -214,13 +150,14 @@
       }
     },
     "node_modules/@babel/highlight": {
-      "version": "7.23.4",
-      "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz",
-      "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==",
+      "version": "7.24.2",
+      "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.2.tgz",
+      "integrity": "sha512-Yac1ao4flkTxTteCDZLEvdxg2fZfz1v8M4QpaGypq/WPDqg3ijHYbDfs+LG5hvzSoqaSZ9/Z9lKSP3CjZjv+pA==",
       "dependencies": {
         "@babel/helper-validator-identifier": "^7.22.20",
         "chalk": "^2.4.2",
-        "js-tokens": "^4.0.0"
+        "js-tokens": "^4.0.0",
+        "picocolors": "^1.0.0"
       },
       "engines": {
         "node": ">=6.9.0"
@@ -296,9 +233,9 @@
       }
     },
     "node_modules/@babel/parser": {
-      "version": "7.24.0",
-      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.0.tgz",
-      "integrity": "sha512-QuP/FxEAzMSjXygs8v4N9dvdXzEHN4W1oF3PxuWAtPo08UdM17u89RDMgjLn/mlc56iM0HlLmVkO/wgR+rDgHg==",
+      "version": "7.24.1",
+      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.1.tgz",
+      "integrity": "sha512-Zo9c7N3xdOIQrNip7Lc9wvRPzlRtovHVE4lkz8WEDr7uYh/GMQhSiIgFxGIArRHYdJE5kxtZjAf8rT0xhdLCzg==",
       "bin": {
         "parser": "bin/babel-parser.js"
       },
@@ -307,9 +244,9 @@
       }
     },
     "node_modules/@babel/runtime": {
-      "version": "7.24.0",
-      "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.0.tgz",
-      "integrity": "sha512-Chk32uHMg6TnQdvw2e9IlqPpFX/6NLuK0Ys2PqLb7/gL5uFn9mXvK715FGLlOLQrcO4qIkNHkvPGktzzXexsFw==",
+      "version": "7.24.1",
+      "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.1.tgz",
+      "integrity": "sha512-+BIznRzyqBf+2wCTxcKE3wDjfGeCoVE61KSHGpkzqrLi8qxqFwBeUFyId2cxkTmm55fzDGnm0+yCxaxygrLUnQ==",
       "dependencies": {
         "regenerator-runtime": "^0.14.0"
       },
@@ -2204,9 +2141,9 @@
       }
     },
     "node_modules/@types/eslint": {
-      "version": "8.56.5",
-      "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.5.tgz",
-      "integrity": "sha512-u5/YPJHo1tvkSF2CE0USEkxon82Z5DBy2xR+qfyYNszpX9qcs4sT6uq2kBbj4BXY1+DBGDPnrhMZV3pKWGNukw==",
+      "version": "8.56.6",
+      "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.6.tgz",
+      "integrity": "sha512-ymwc+qb1XkjT/gfoQwxIeHZ6ixH23A+tCT2ADSA/DPVKzAjwYkTXBMCQ/f6fe4wEa85Lhp26VPeUxI7wMhAi7A==",
       "dependencies": {
         "@types/estree": "*",
         "@types/json-schema": "*"
@@ -2256,9 +2193,9 @@
       "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g=="
     },
     "node_modules/@types/node": {
-      "version": "20.11.27",
-      "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.27.tgz",
-      "integrity": "sha512-qyUZfMnCg1KEz57r7pzFtSGt49f6RPkPBis3Vo4PbS7roQEDn22hiHzl/Lo1q4i4hDEgBJmBF/NTNg2XR0HbFg==",
+      "version": "20.11.30",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.30.tgz",
+      "integrity": "sha512-dHM6ZxwlmuZaRmUPfv1p+KrdD1Dci04FbdEm/9wEMouFqxYoFl5aMkt0VMAUtYRQDyYvD41WJLukhq/ha3YuTw==",
       "dependencies": {
         "undici-types": "~5.26.4"
       }
@@ -2301,16 +2238,16 @@
       "dev": true
     },
     "node_modules/@typescript-eslint/eslint-plugin": {
-      "version": "7.2.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.2.0.tgz",
-      "integrity": "sha512-mdekAHOqS9UjlmyF/LSs6AIEvfceV749GFxoBAjwAv0nkevfKHWQFDMcBZWUiIC5ft6ePWivXoS36aKQ0Cy3sw==",
+      "version": "7.3.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.3.1.tgz",
+      "integrity": "sha512-STEDMVQGww5lhCuNXVSQfbfuNII5E08QWkvAw5Qwf+bj2WT+JkG1uc+5/vXA3AOYMDHVOSpL+9rcbEUiHIm2dw==",
       "dev": true,
       "dependencies": {
         "@eslint-community/regexpp": "^4.5.1",
-        "@typescript-eslint/scope-manager": "7.2.0",
-        "@typescript-eslint/type-utils": "7.2.0",
-        "@typescript-eslint/utils": "7.2.0",
-        "@typescript-eslint/visitor-keys": "7.2.0",
+        "@typescript-eslint/scope-manager": "7.3.1",
+        "@typescript-eslint/type-utils": "7.3.1",
+        "@typescript-eslint/utils": "7.3.1",
+        "@typescript-eslint/visitor-keys": "7.3.1",
         "debug": "^4.3.4",
         "graphemer": "^1.4.0",
         "ignore": "^5.2.4",
@@ -2319,7 +2256,7 @@
         "ts-api-utils": "^1.0.1"
       },
       "engines": {
-        "node": "^16.0.0 || >=18.0.0"
+        "node": "^18.18.0 || >=20.0.0"
       },
       "funding": {
         "type": "opencollective",
@@ -2336,19 +2273,19 @@
       }
     },
     "node_modules/@typescript-eslint/parser": {
-      "version": "7.2.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.2.0.tgz",
-      "integrity": "sha512-5FKsVcHTk6TafQKQbuIVkXq58Fnbkd2wDL4LB7AURN7RUOu1utVP+G8+6u3ZhEroW3DF6hyo3ZEXxgKgp4KeCg==",
+      "version": "7.3.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.3.1.tgz",
+      "integrity": "sha512-Rq49+pq7viTRCH48XAbTA+wdLRrB/3sRq4Lpk0oGDm0VmnjBrAOVXH/Laalmwsv2VpekiEfVFwJYVk6/e8uvQw==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/scope-manager": "7.2.0",
-        "@typescript-eslint/types": "7.2.0",
-        "@typescript-eslint/typescript-estree": "7.2.0",
-        "@typescript-eslint/visitor-keys": "7.2.0",
+        "@typescript-eslint/scope-manager": "7.3.1",
+        "@typescript-eslint/types": "7.3.1",
+        "@typescript-eslint/typescript-estree": "7.3.1",
+        "@typescript-eslint/visitor-keys": "7.3.1",
         "debug": "^4.3.4"
       },
       "engines": {
-        "node": "^16.0.0 || >=18.0.0"
+        "node": "^18.18.0 || >=20.0.0"
       },
       "funding": {
         "type": "opencollective",
@@ -2364,16 +2301,16 @@
       }
     },
     "node_modules/@typescript-eslint/scope-manager": {
-      "version": "7.2.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.2.0.tgz",
-      "integrity": "sha512-Qh976RbQM/fYtjx9hs4XkayYujB/aPwglw2choHmf3zBjB4qOywWSdt9+KLRdHubGcoSwBnXUH2sR3hkyaERRg==",
+      "version": "7.3.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.3.1.tgz",
+      "integrity": "sha512-fVS6fPxldsKY2nFvyT7IP78UO1/I2huG+AYu5AMjCT9wtl6JFiDnsv4uad4jQ0GTFzcUV5HShVeN96/17bTBag==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/types": "7.2.0",
-        "@typescript-eslint/visitor-keys": "7.2.0"
+        "@typescript-eslint/types": "7.3.1",
+        "@typescript-eslint/visitor-keys": "7.3.1"
       },
       "engines": {
-        "node": "^16.0.0 || >=18.0.0"
+        "node": "^18.18.0 || >=20.0.0"
       },
       "funding": {
         "type": "opencollective",
@@ -2381,18 +2318,18 @@
       }
     },
     "node_modules/@typescript-eslint/type-utils": {
-      "version": "7.2.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.2.0.tgz",
-      "integrity": "sha512-xHi51adBHo9O9330J8GQYQwrKBqbIPJGZZVQTHHmy200hvkLZFWJIFtAG/7IYTWUyun6DE6w5InDReePJYJlJA==",
+      "version": "7.3.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.3.1.tgz",
+      "integrity": "sha512-iFhaysxFsMDQlzJn+vr3OrxN8NmdQkHks4WaqD4QBnt5hsq234wcYdyQ9uquzJJIDAj5W4wQne3yEsYA6OmXGw==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/typescript-estree": "7.2.0",
-        "@typescript-eslint/utils": "7.2.0",
+        "@typescript-eslint/typescript-estree": "7.3.1",
+        "@typescript-eslint/utils": "7.3.1",
         "debug": "^4.3.4",
         "ts-api-utils": "^1.0.1"
       },
       "engines": {
-        "node": "^16.0.0 || >=18.0.0"
+        "node": "^18.18.0 || >=20.0.0"
       },
       "funding": {
         "type": "opencollective",
@@ -2408,12 +2345,12 @@
       }
     },
     "node_modules/@typescript-eslint/types": {
-      "version": "7.2.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.2.0.tgz",
-      "integrity": "sha512-XFtUHPI/abFhm4cbCDc5Ykc8npOKBSJePY3a3s+lwumt7XWJuzP5cZcfZ610MIPHjQjNsOLlYK8ASPaNG8UiyA==",
+      "version": "7.3.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.3.1.tgz",
+      "integrity": "sha512-2tUf3uWggBDl4S4183nivWQ2HqceOZh1U4hhu4p1tPiIJoRRXrab7Y+Y0p+dozYwZVvLPRI6r5wKe9kToF9FIw==",
       "dev": true,
       "engines": {
-        "node": "^16.0.0 || >=18.0.0"
+        "node": "^18.18.0 || >=20.0.0"
       },
       "funding": {
         "type": "opencollective",
@@ -2421,13 +2358,13 @@
       }
     },
     "node_modules/@typescript-eslint/typescript-estree": {
-      "version": "7.2.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.2.0.tgz",
-      "integrity": "sha512-cyxS5WQQCoBwSakpMrvMXuMDEbhOo9bNHHrNcEWis6XHx6KF518tkF1wBvKIn/tpq5ZpUYK7Bdklu8qY0MsFIA==",
+      "version": "7.3.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.3.1.tgz",
+      "integrity": "sha512-tLpuqM46LVkduWP7JO7yVoWshpJuJzxDOPYIVWUUZbW+4dBpgGeUdl/fQkhuV0A8eGnphYw3pp8d2EnvPOfxmQ==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/types": "7.2.0",
-        "@typescript-eslint/visitor-keys": "7.2.0",
+        "@typescript-eslint/types": "7.3.1",
+        "@typescript-eslint/visitor-keys": "7.3.1",
         "debug": "^4.3.4",
         "globby": "^11.1.0",
         "is-glob": "^4.0.3",
@@ -2436,7 +2373,7 @@
         "ts-api-utils": "^1.0.1"
       },
       "engines": {
-        "node": "^16.0.0 || >=18.0.0"
+        "node": "^18.18.0 || >=20.0.0"
       },
       "funding": {
         "type": "opencollective",
@@ -2449,21 +2386,21 @@
       }
     },
     "node_modules/@typescript-eslint/utils": {
-      "version": "7.2.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.2.0.tgz",
-      "integrity": "sha512-YfHpnMAGb1Eekpm3XRK8hcMwGLGsnT6L+7b2XyRv6ouDuJU1tZir1GS2i0+VXRatMwSI1/UfcyPe53ADkU+IuA==",
+      "version": "7.3.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.3.1.tgz",
+      "integrity": "sha512-jIERm/6bYQ9HkynYlNZvXpzmXWZGhMbrOvq3jJzOSOlKXsVjrrolzWBjDW6/TvT5Q3WqaN4EkmcfdQwi9tDjBQ==",
       "dev": true,
       "dependencies": {
         "@eslint-community/eslint-utils": "^4.4.0",
         "@types/json-schema": "^7.0.12",
         "@types/semver": "^7.5.0",
-        "@typescript-eslint/scope-manager": "7.2.0",
-        "@typescript-eslint/types": "7.2.0",
-        "@typescript-eslint/typescript-estree": "7.2.0",
+        "@typescript-eslint/scope-manager": "7.3.1",
+        "@typescript-eslint/types": "7.3.1",
+        "@typescript-eslint/typescript-estree": "7.3.1",
         "semver": "^7.5.4"
       },
       "engines": {
-        "node": "^16.0.0 || >=18.0.0"
+        "node": "^18.18.0 || >=20.0.0"
       },
       "funding": {
         "type": "opencollective",
@@ -2474,16 +2411,16 @@
       }
     },
     "node_modules/@typescript-eslint/visitor-keys": {
-      "version": "7.2.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.2.0.tgz",
-      "integrity": "sha512-c6EIQRHhcpl6+tO8EMR+kjkkV+ugUNXOmeASA1rlzkd8EPIriavpWoiEz1HR/VLhbVIdhqnV6E7JZm00cBDx2A==",
+      "version": "7.3.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.3.1.tgz",
+      "integrity": "sha512-9RMXwQF8knsZvfv9tdi+4D/j7dMG28X/wMJ8Jj6eOHyHWwDW4ngQJcqEczSsqIKKjFiLFr40Mnr7a5ulDD3vmw==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/types": "7.2.0",
+        "@typescript-eslint/types": "7.3.1",
         "eslint-visitor-keys": "^3.4.1"
       },
       "engines": {
-        "node": "^16.0.0 || >=18.0.0"
+        "node": "^18.18.0 || >=20.0.0"
       },
       "funding": {
         "type": "opencollective",
@@ -2978,18 +2915,6 @@
         "webpack": ">=5"
       }
     },
-    "node_modules/agent-base": {
-      "version": "7.1.0",
-      "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz",
-      "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==",
-      "dev": true,
-      "dependencies": {
-        "debug": "^4.3.4"
-      },
-      "engines": {
-        "node": ">= 14"
-      }
-    },
     "node_modules/ajv": {
       "version": "8.12.0",
       "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz",
@@ -3146,15 +3071,16 @@
       }
     },
     "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==",
+      "version": "3.1.8",
+      "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz",
+      "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==",
       "dev": true,
       "dependencies": {
-        "call-bind": "^1.0.2",
-        "define-properties": "^1.2.0",
-        "es-abstract": "^1.22.1",
-        "get-intrinsic": "^1.2.1",
+        "call-bind": "^1.0.7",
+        "define-properties": "^1.2.1",
+        "es-abstract": "^1.23.2",
+        "es-object-atoms": "^1.0.0",
+        "get-intrinsic": "^1.2.4",
         "is-string": "^1.0.7"
       },
       "engines": {
@@ -3173,35 +3099,17 @@
         "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==",
+      "version": "1.2.5",
+      "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.5.tgz",
+      "integrity": "sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==",
       "dev": true,
       "dependencies": {
-        "call-bind": "^1.0.5",
+        "call-bind": "^1.0.7",
         "define-properties": "^1.2.1",
-        "es-abstract": "^1.22.3",
+        "es-abstract": "^1.23.2",
         "es-errors": "^1.3.0",
+        "es-object-atoms": "^1.0.0",
         "es-shim-unscopables": "^1.0.2"
       },
       "engines": {
@@ -3332,21 +3240,6 @@
         "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",
-      "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
-      "dev": true
-    },
     "node_modules/atob": {
       "version": "2.1.2",
       "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
@@ -3582,9 +3475,9 @@
       }
     },
     "node_modules/caniuse-lite": {
-      "version": "1.0.30001597",
-      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001597.tgz",
-      "integrity": "sha512-7LjJvmQU6Sj7bL0j5b5WY/3n7utXUJvAe1lxhsHDbLmwX9mdL86Yjtr+5SRCyf8qME4M7pU2hswj0FpyBVCv9w==",
+      "version": "1.0.30001599",
+      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001599.tgz",
+      "integrity": "sha512-LRAQHZ4yT1+f9LemSMeqdMpMxZcc4RMWdj4tiFe3G8tNkWK+E58g+/tzotb5cU6TbcVJLr4fySiAW7XmxQvZQA==",
       "funding": [
         {
           "type": "opencollective",
@@ -3868,18 +3761,6 @@
       "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz",
       "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w=="
     },
-    "node_modules/combined-stream": {
-      "version": "1.0.8",
-      "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
-      "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
-      "dev": true,
-      "dependencies": {
-        "delayed-stream": "~1.0.0"
-      },
-      "engines": {
-        "node": ">= 0.8"
-      }
-    },
     "node_modules/commander": {
       "version": "8.3.0",
       "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz",
@@ -3909,12 +3790,12 @@
       "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
     },
     "node_modules/core-js-compat": {
-      "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==",
+      "version": "3.36.1",
+      "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.36.1.tgz",
+      "integrity": "sha512-Dk997v9ZCt3X/npqzyGdTlq6t7lDBhZwGvV94PKzDArjp7BTRm7WlDAXYd/OWdeFHO8OChQYRJNJvUCqCbrtKA==",
       "dev": true,
       "dependencies": {
-        "browserslist": "^4.22.3"
+        "browserslist": "^4.23.0"
       },
       "funding": {
         "type": "opencollective",
@@ -4106,18 +3987,6 @@
       "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==",
       "dev": true
     },
-    "node_modules/cssstyle": {
-      "version": "4.0.1",
-      "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.0.1.tgz",
-      "integrity": "sha512-8ZYiJ3A/3OkDd093CBT/0UKDWry7ak4BdPTFP2+QEP7cmhouyq/Up709ASSj2cK02BbZiMgk7kYjZNS4QP5qrQ==",
-      "dev": true,
-      "dependencies": {
-        "rrweb-cssom": "^0.6.0"
-      },
-      "engines": {
-        "node": ">=18"
-      }
-    },
     "node_modules/csstype": {
       "version": "3.1.3",
       "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
@@ -4580,17 +4449,55 @@
       "integrity": "sha512-ND9qDTLc6diwj+Xe5cdAgVTbLVdXbtxTJRXRhli8Mowuaan+0EJOtdqJ0QCHNSSPyoXGx9HX2/VMnKeC34AChA==",
       "dev": true
     },
-    "node_modules/data-urls": {
-      "version": "5.0.0",
-      "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz",
-      "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==",
+    "node_modules/data-view-buffer": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz",
+      "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==",
       "dev": true,
       "dependencies": {
-        "whatwg-mimetype": "^4.0.0",
-        "whatwg-url": "^14.0.0"
+        "call-bind": "^1.0.6",
+        "es-errors": "^1.3.0",
+        "is-data-view": "^1.0.1"
       },
       "engines": {
-        "node": ">=18"
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/data-view-byte-length": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz",
+      "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==",
+      "dev": true,
+      "dependencies": {
+        "call-bind": "^1.0.7",
+        "es-errors": "^1.3.0",
+        "is-data-view": "^1.0.1"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/data-view-byte-offset": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz",
+      "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==",
+      "dev": true,
+      "dependencies": {
+        "call-bind": "^1.0.6",
+        "es-errors": "^1.3.0",
+        "is-data-view": "^1.0.1"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
       }
     },
     "node_modules/dayjs": {
@@ -4614,12 +4521,6 @@
         }
       }
     },
-    "node_modules/decimal.js": {
-      "version": "10.4.3",
-      "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz",
-      "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==",
-      "dev": true
-    },
     "node_modules/decode-named-character-reference": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz",
@@ -4710,15 +4611,6 @@
         "robust-predicates": "^3.0.2"
       }
     },
-    "node_modules/delayed-stream": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
-      "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
-      "dev": true,
-      "engines": {
-        "node": ">=0.4.0"
-      }
-    },
     "node_modules/dependency-graph": {
       "version": "0.11.0",
       "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.11.0.tgz",
@@ -4829,9 +4721,9 @@
       }
     },
     "node_modules/dompurify": {
-      "version": "3.0.9",
-      "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.0.9.tgz",
-      "integrity": "sha512-uyb4NDIvQ3hRn6NiC+SIFaP4mJ/MdXlvtunaqK9Bn6dD3RuB/1S/gasEjDHD8eiaqdSael2vBv+hOs7Y+jhYOQ=="
+      "version": "3.0.10",
+      "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.0.10.tgz",
+      "integrity": "sha512-WZDL8ZHTliEVP3Lk4phtvjg8SNQ3YMc5WVstxE8cszKZrFjzI4PF4ZTIk9VGAc9vZADO7uGO2V/ZiStcRSAT4Q=="
     },
     "node_modules/domutils": {
       "version": "3.1.0",
@@ -4874,9 +4766,9 @@
       }
     },
     "node_modules/electron-to-chromium": {
-      "version": "1.4.706",
-      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.706.tgz",
-      "integrity": "sha512-fO01fufoGd6jKK3HR8ofBapF3ZPfgxNJ/ua9xQAhFu93TwWIs4d+weDn3kje3GB4S7aGUTfk5nvdU5F7z5mF9Q=="
+      "version": "1.4.713",
+      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.713.tgz",
+      "integrity": "sha512-vDarADhwntXiULEdmWd77g2dV6FrNGa8ecAC29MZ4TwPut2fvosD0/5sJd1qWNNe8HcJFAC+F5Lf9jW1NPtWmw=="
     },
     "node_modules/elkjs": {
       "version": "0.9.2",
@@ -4947,17 +4839,21 @@
       }
     },
     "node_modules/es-abstract": {
-      "version": "1.22.5",
-      "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.5.tgz",
-      "integrity": "sha512-oW69R+4q2wG+Hc3KZePPZxOiisRIqfKBVo/HLx94QcJeWGU/8sZhCvc829rd1kS366vlJbzBfXf9yWwf0+Ko7w==",
+      "version": "1.23.2",
+      "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.2.tgz",
+      "integrity": "sha512-60s3Xv2T2p1ICykc7c+DNDPLDMm9t4QxCOUU0K9JxiLjM3C1zB9YVdN7tjxrFd4+AkZ8CdX1ovUga4P2+1e+/w==",
       "dev": true,
       "dependencies": {
         "array-buffer-byte-length": "^1.0.1",
         "arraybuffer.prototype.slice": "^1.0.3",
         "available-typed-arrays": "^1.0.7",
         "call-bind": "^1.0.7",
+        "data-view-buffer": "^1.0.1",
+        "data-view-byte-length": "^1.0.1",
+        "data-view-byte-offset": "^1.0.0",
         "es-define-property": "^1.0.0",
         "es-errors": "^1.3.0",
+        "es-object-atoms": "^1.0.0",
         "es-set-tostringtag": "^2.0.3",
         "es-to-primitive": "^1.2.1",
         "function.prototype.name": "^1.1.6",
@@ -4968,10 +4864,11 @@
         "has-property-descriptors": "^1.0.2",
         "has-proto": "^1.0.3",
         "has-symbols": "^1.0.3",
-        "hasown": "^2.0.1",
+        "hasown": "^2.0.2",
         "internal-slot": "^1.0.7",
         "is-array-buffer": "^3.0.4",
         "is-callable": "^1.2.7",
+        "is-data-view": "^1.0.1",
         "is-negative-zero": "^2.0.3",
         "is-regex": "^1.1.4",
         "is-shared-array-buffer": "^1.0.3",
@@ -4982,17 +4879,17 @@
         "object-keys": "^1.1.1",
         "object.assign": "^4.1.5",
         "regexp.prototype.flags": "^1.5.2",
-        "safe-array-concat": "^1.1.0",
+        "safe-array-concat": "^1.1.2",
         "safe-regex-test": "^1.0.3",
-        "string.prototype.trim": "^1.2.8",
-        "string.prototype.trimend": "^1.0.7",
+        "string.prototype.trim": "^1.2.9",
+        "string.prototype.trimend": "^1.0.8",
         "string.prototype.trimstart": "^1.0.7",
         "typed-array-buffer": "^1.0.2",
         "typed-array-byte-length": "^1.0.1",
         "typed-array-byte-offset": "^1.0.2",
         "typed-array-length": "^1.0.5",
         "unbox-primitive": "^1.0.2",
-        "which-typed-array": "^1.1.14"
+        "which-typed-array": "^1.1.15"
       },
       "engines": {
         "node": ">= 0.4"
@@ -5023,12 +4920,6 @@
         "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",
@@ -5051,35 +4942,46 @@
       }
     },
     "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==",
+      "version": "1.0.18",
+      "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.18.tgz",
+      "integrity": "sha512-scxAJaewsahbqTYrGKJihhViaM6DDZDDoucfvzNbK0pOren1g/daDQ3IAhzn+1G14rBG7w+i5N+qul60++zlKA==",
       "dev": true,
       "dependencies": {
-        "asynciterator.prototype": "^1.0.0",
         "call-bind": "^1.0.7",
         "define-properties": "^1.2.1",
-        "es-abstract": "^1.22.4",
+        "es-abstract": "^1.23.0",
         "es-errors": "^1.3.0",
-        "es-set-tostringtag": "^2.0.2",
+        "es-set-tostringtag": "^2.0.3",
         "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-proto": "^1.0.3",
         "has-symbols": "^1.0.3",
         "internal-slot": "^1.0.7",
         "iterator.prototype": "^1.1.2",
-        "safe-array-concat": "^1.1.0"
+        "safe-array-concat": "^1.1.2"
       },
       "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",
-      "integrity": "sha512-cXLGjP0c4T3flZJKQSuziYoq7MlT+rnvfZjfp7h+I7K9BNX54kP9nyWvdbwjQ4u1iWbOL4u96fgeZLToQlZC7w=="
+      "version": "1.4.2",
+      "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.4.2.tgz",
+      "integrity": "sha512-7nOqkomXZEaxUDJw21XZNtRk739QvrPSoZoRtbsEfcii00vdzZUh6zh1CQwHhrib8MdEtJfv5rJiGeb4KuV/vw=="
+    },
+    "node_modules/es-object-atoms": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz",
+      "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==",
+      "dev": true,
+      "dependencies": {
+        "es-errors": "^1.3.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
     },
     "node_modules/es-set-tostringtag": {
       "version": "2.0.3",
@@ -6131,25 +6033,6 @@
         }
       }
     },
-    "node_modules/fetch-ponyfill/node_modules/tr46": {
-      "version": "0.0.3",
-      "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
-      "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
-    },
-    "node_modules/fetch-ponyfill/node_modules/webidl-conversions": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
-      "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
-    },
-    "node_modules/fetch-ponyfill/node_modules/whatwg-url": {
-      "version": "5.0.0",
-      "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
-      "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
-      "dependencies": {
-        "tr46": "~0.0.3",
-        "webidl-conversions": "^3.0.0"
-      }
-    },
     "node_modules/file-entry-cache": {
       "version": "6.0.1",
       "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
@@ -6241,20 +6124,6 @@
         "url": "https://github.com/sponsors/isaacs"
       }
     },
-    "node_modules/form-data": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
-      "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
-      "dev": true,
-      "dependencies": {
-        "asynckit": "^0.4.0",
-        "combined-stream": "^1.0.8",
-        "mime-types": "^2.1.12"
-      },
-      "engines": {
-        "node": ">= 6"
-      }
-    },
     "node_modules/fs-extra": {
       "version": "10.1.0",
       "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
@@ -6632,6 +6501,20 @@
         "node": ">=0.8.0"
       }
     },
+    "node_modules/happy-dom": {
+      "version": "14.2.0",
+      "resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-14.2.0.tgz",
+      "integrity": "sha512-vTqF/9MEkRKgYy5eKq9W0uiNmkgnVAmJhRwn8x8fQBR7lc4C84859jLhgZ1lR4Gi/t70oSdgvtLpxlHjgdJrAw==",
+      "dev": true,
+      "dependencies": {
+        "entities": "^4.5.0",
+        "webidl-conversions": "^7.0.0",
+        "whatwg-mimetype": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=16.0.0"
+      }
+    },
     "node_modules/has-bigints": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz",
@@ -6736,18 +6619,6 @@
         "node": ">=14"
       }
     },
-    "node_modules/html-encoding-sniffer": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz",
-      "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==",
-      "dev": true,
-      "dependencies": {
-        "whatwg-encoding": "^3.1.1"
-      },
-      "engines": {
-        "node": ">=18"
-      }
-    },
     "node_modules/html-tags": {
       "version": "3.3.1",
       "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.3.1.tgz",
@@ -6784,32 +6655,6 @@
       "resolved": "https://registry.npmjs.org/htmx.org/-/htmx.org-1.9.11.tgz",
       "integrity": "sha512-WlVuICn8dfNOOgYmdYzYG8zSnP3++AdHkMHooQAzGZObWpVXYathpz/I37ycF4zikR6YduzfCvEcxk20JkIUsw=="
     },
-    "node_modules/http-proxy-agent": {
-      "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",
-        "debug": "^4.3.4"
-      },
-      "engines": {
-        "node": ">= 14"
-      }
-    },
-    "node_modules/https-proxy-agent": {
-      "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",
-        "debug": "4"
-      },
-      "engines": {
-        "node": ">= 14"
-      }
-    },
     "node_modules/human-signals": {
       "version": "5.0.0",
       "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz",
@@ -7101,6 +6946,21 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/is-data-view": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz",
+      "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==",
+      "dev": true,
+      "dependencies": {
+        "is-typed-array": "^1.1.13"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
     "node_modules/is-date-object": {
       "version": "1.0.5",
       "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz",
@@ -7568,55 +7428,6 @@
         "node": ">=12.0.0"
       }
     },
-    "node_modules/jsdom": {
-      "version": "24.0.0",
-      "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-24.0.0.tgz",
-      "integrity": "sha512-UDS2NayCvmXSXVP6mpTj+73JnNQadZlr9N68189xib2tx5Mls7swlTNao26IoHv46BZJFvXygyRtyXd1feAk1A==",
-      "dev": true,
-      "dependencies": {
-        "cssstyle": "^4.0.1",
-        "data-urls": "^5.0.0",
-        "decimal.js": "^10.4.3",
-        "form-data": "^4.0.0",
-        "html-encoding-sniffer": "^4.0.0",
-        "http-proxy-agent": "^7.0.0",
-        "https-proxy-agent": "^7.0.2",
-        "is-potential-custom-element-name": "^1.0.1",
-        "nwsapi": "^2.2.7",
-        "parse5": "^7.1.2",
-        "rrweb-cssom": "^0.6.0",
-        "saxes": "^6.0.0",
-        "symbol-tree": "^3.2.4",
-        "tough-cookie": "^4.1.3",
-        "w3c-xmlserializer": "^5.0.0",
-        "webidl-conversions": "^7.0.0",
-        "whatwg-encoding": "^3.1.1",
-        "whatwg-mimetype": "^4.0.0",
-        "whatwg-url": "^14.0.0",
-        "ws": "^8.16.0",
-        "xml-name-validator": "^5.0.0"
-      },
-      "engines": {
-        "node": ">=18"
-      },
-      "peerDependencies": {
-        "canvas": "^2.11.2"
-      },
-      "peerDependenciesMeta": {
-        "canvas": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/jsdom/node_modules/xml-name-validator": {
-      "version": "5.0.0",
-      "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz",
-      "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==",
-      "dev": true,
-      "engines": {
-        "node": ">=18"
-      }
-    },
     "node_modules/jsep": {
       "version": "1.3.8",
       "resolved": "https://registry.npmjs.org/jsep/-/jsep-1.3.8.tgz",
@@ -8956,25 +8767,6 @@
         }
       }
     },
-    "node_modules/node-fetch/node_modules/tr46": {
-      "version": "0.0.3",
-      "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
-      "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
-    },
-    "node_modules/node-fetch/node_modules/webidl-conversions": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
-      "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
-    },
-    "node_modules/node-fetch/node_modules/whatwg-url": {
-      "version": "5.0.0",
-      "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
-      "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
-      "dependencies": {
-        "tr46": "~0.0.3",
-        "webidl-conversions": "^3.0.0"
-      }
-    },
     "node_modules/node-releases": {
       "version": "2.0.14",
       "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz",
@@ -9066,12 +8858,6 @@
         "url": "https://github.com/fb55/nth-check?sponsor=1"
       }
     },
-    "node_modules/nwsapi": {
-      "version": "2.2.7",
-      "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.7.tgz",
-      "integrity": "sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ==",
-      "dev": true
-    },
     "node_modules/obj-props": {
       "version": "1.4.0",
       "resolved": "https://registry.npmjs.org/obj-props/-/obj-props-1.4.0.tgz",
@@ -9134,28 +8920,29 @@
       }
     },
     "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==",
+      "version": "1.1.8",
+      "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.8.tgz",
+      "integrity": "sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==",
       "dev": true,
       "dependencies": {
-        "call-bind": "^1.0.2",
-        "define-properties": "^1.2.0",
-        "es-abstract": "^1.22.1"
+        "call-bind": "^1.0.7",
+        "define-properties": "^1.2.1",
+        "es-object-atoms": "^1.0.0"
       },
       "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==",
+      "version": "2.0.8",
+      "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz",
+      "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==",
       "dev": true,
       "dependencies": {
-        "call-bind": "^1.0.2",
-        "define-properties": "^1.2.0",
-        "es-abstract": "^1.22.1"
+        "call-bind": "^1.0.7",
+        "define-properties": "^1.2.1",
+        "es-abstract": "^1.23.2",
+        "es-object-atoms": "^1.0.0"
       },
       "engines": {
         "node": ">= 0.4"
@@ -9165,27 +8952,28 @@
       }
     },
     "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==",
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz",
+      "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==",
       "dev": true,
       "dependencies": {
-        "array.prototype.filter": "^1.0.3",
-        "call-bind": "^1.0.5",
+        "call-bind": "^1.0.7",
         "define-properties": "^1.2.1",
-        "es-abstract": "^1.22.3",
-        "es-errors": "^1.0.0"
+        "es-abstract": "^1.23.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
       }
     },
     "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==",
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.0.tgz",
+      "integrity": "sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==",
       "dev": true,
       "dependencies": {
-        "call-bind": "^1.0.2",
-        "define-properties": "^1.2.0",
-        "es-abstract": "^1.22.1"
+        "call-bind": "^1.0.7",
+        "define-properties": "^1.2.1",
+        "es-object-atoms": "^1.0.0"
       },
       "engines": {
         "node": ">= 0.4"
@@ -9311,18 +9099,6 @@
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
-    "node_modules/parse5": {
-      "version": "7.1.2",
-      "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz",
-      "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==",
-      "dev": true,
-      "dependencies": {
-        "entities": "^4.4.0"
-      },
-      "funding": {
-        "url": "https://github.com/inikulin/parse5?sponsor=1"
-      }
-    },
     "node_modules/path-exists": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
@@ -9949,12 +9725,6 @@
         "node": ">=4"
       }
     },
-    "node_modules/psl": {
-      "version": "1.9.0",
-      "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz",
-      "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==",
-      "dev": true
-    },
     "node_modules/punycode": {
       "version": "2.3.1",
       "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
@@ -9972,12 +9742,6 @@
         "node": ">=6"
       }
     },
-    "node_modules/querystringify": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
-      "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==",
-      "dev": true
-    },
     "node_modules/queue-microtask": {
       "version": "1.2.3",
       "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
@@ -10156,16 +9920,16 @@
       }
     },
     "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==",
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.6.tgz",
+      "integrity": "sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg==",
       "dev": true,
       "dependencies": {
-        "call-bind": "^1.0.5",
+        "call-bind": "^1.0.7",
         "define-properties": "^1.2.1",
-        "es-abstract": "^1.22.3",
-        "es-errors": "^1.0.0",
-        "get-intrinsic": "^1.2.3",
+        "es-abstract": "^1.23.1",
+        "es-errors": "^1.3.0",
+        "get-intrinsic": "^1.2.4",
         "globalthis": "^1.0.3",
         "which-builtin-type": "^1.1.3"
       },
@@ -10259,12 +10023,6 @@
         "node": ">=0.10.0"
       }
     },
-    "node_modules/requires-port": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
-      "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==",
-      "dev": true
-    },
     "node_modules/reserved": {
       "version": "0.1.2",
       "resolved": "https://registry.npmjs.org/reserved/-/reserved-0.1.2.tgz",
@@ -10369,12 +10127,6 @@
         "fsevents": "~2.3.2"
       }
     },
-    "node_modules/rrweb-cssom": {
-      "version": "0.6.0",
-      "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz",
-      "integrity": "sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw==",
-      "dev": true
-    },
     "node_modules/run-con": {
       "version": "1.3.2",
       "resolved": "https://registry.npmjs.org/run-con/-/run-con-1.3.2.tgz",
@@ -10499,18 +10251,6 @@
       "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==",
       "dev": true
     },
-    "node_modules/saxes": {
-      "version": "6.0.0",
-      "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz",
-      "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==",
-      "dev": true,
-      "dependencies": {
-        "xmlchars": "^2.2.0"
-      },
-      "engines": {
-        "node": ">=v12.22.7"
-      }
-    },
     "node_modules/schema-utils": {
       "version": "4.2.0",
       "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz",
@@ -10720,12 +10460,12 @@
       }
     },
     "node_modules/solid-js": {
-      "version": "1.8.15",
-      "resolved": "https://registry.npmjs.org/solid-js/-/solid-js-1.8.15.tgz",
-      "integrity": "sha512-d0QP/efr3UVcwGgWVPveQQ0IHOH6iU7yUhc2piy8arNG8wxKmvUy1kFxyF8owpmfCWGB87usDKMaVnsNYZm+Vw==",
+      "version": "1.8.16",
+      "resolved": "https://registry.npmjs.org/solid-js/-/solid-js-1.8.16.tgz",
+      "integrity": "sha512-rja94MNU9flF3qQRLNsu60QHKBDKBkVE1DldJZPIfn2ypIn3NV2WpSbGTQIvsyGPBo+9E2IMjwqnqpbgfWuzeg==",
       "dependencies": {
         "csstype": "^3.1.0",
-        "seroval": "^1.0.3",
+        "seroval": "^1.0.4",
         "seroval-plugins": "^1.0.3"
       }
     },
@@ -10748,9 +10488,9 @@
       }
     },
     "node_modules/source-map-js": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
-      "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz",
+      "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==",
       "engines": {
         "node": ">=0.10.0"
       }
@@ -10904,14 +10644,15 @@
       "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
     },
     "node_modules/string.prototype.trim": {
-      "version": "1.2.8",
-      "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz",
-      "integrity": "sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==",
+      "version": "1.2.9",
+      "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz",
+      "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==",
       "dev": true,
       "dependencies": {
-        "call-bind": "^1.0.2",
-        "define-properties": "^1.2.0",
-        "es-abstract": "^1.22.1"
+        "call-bind": "^1.0.7",
+        "define-properties": "^1.2.1",
+        "es-abstract": "^1.23.0",
+        "es-object-atoms": "^1.0.0"
       },
       "engines": {
         "node": ">= 0.4"
@@ -10921,14 +10662,14 @@
       }
     },
     "node_modules/string.prototype.trimend": {
-      "version": "1.0.7",
-      "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz",
-      "integrity": "sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==",
+      "version": "1.0.8",
+      "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz",
+      "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==",
       "dev": true,
       "dependencies": {
-        "call-bind": "^1.0.2",
-        "define-properties": "^1.2.0",
-        "es-abstract": "^1.22.1"
+        "call-bind": "^1.0.7",
+        "define-properties": "^1.2.1",
+        "es-object-atoms": "^1.0.0"
       },
       "funding": {
         "url": "https://github.com/sponsors/ljharb"
@@ -11386,12 +11127,6 @@
       "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.12.0.tgz",
       "integrity": "sha512-Rt1xUpbHulJVGbiQjq9yy9/r/0Pg6TmpcG+fXTaMePDc8z5WUw4LfaWts5qcNv/8ewPvBIbY7DKq7qReIKNCCQ=="
     },
-    "node_modules/symbol-tree": {
-      "version": "3.2.4",
-      "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",
-      "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==",
-      "dev": true
-    },
     "node_modules/sync-fetch": {
       "version": "0.4.5",
       "resolved": "https://registry.npmjs.org/sync-fetch/-/sync-fetch-0.4.5.tgz",
@@ -11724,41 +11459,10 @@
       "resolved": "https://registry.npmjs.org/toastify-js/-/toastify-js-1.12.0.tgz",
       "integrity": "sha512-HeMHCO9yLPvP9k0apGSdPUWrUbLnxUKNFzgUoZp1PHCLploIX/4DSQ7V8H25ef+h4iO9n0he7ImfcndnN6nDrQ=="
     },
-    "node_modules/tough-cookie": {
-      "version": "4.1.3",
-      "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz",
-      "integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==",
-      "dev": true,
-      "dependencies": {
-        "psl": "^1.1.33",
-        "punycode": "^2.1.1",
-        "universalify": "^0.2.0",
-        "url-parse": "^1.5.3"
-      },
-      "engines": {
-        "node": ">=6"
-      }
-    },
-    "node_modules/tough-cookie/node_modules/universalify": {
-      "version": "0.2.0",
-      "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz",
-      "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==",
-      "dev": true,
-      "engines": {
-        "node": ">= 4.0.0"
-      }
-    },
     "node_modules/tr46": {
-      "version": "5.0.0",
-      "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.0.0.tgz",
-      "integrity": "sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==",
-      "dev": true,
-      "dependencies": {
-        "punycode": "^2.3.1"
-      },
-      "engines": {
-        "node": ">=18"
-      }
+      "version": "0.0.3",
+      "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
+      "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
     },
     "node_modules/tributejs": {
       "version": "5.1.3",
@@ -11927,9 +11631,9 @@
       }
     },
     "node_modules/typescript": {
-      "version": "5.4.2",
-      "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.2.tgz",
-      "integrity": "sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==",
+      "version": "5.4.3",
+      "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.3.tgz",
+      "integrity": "sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg==",
       "devOptional": true,
       "peer": true,
       "bin": {
@@ -11952,9 +11656,9 @@
       "dev": true
     },
     "node_modules/ufo": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.4.0.tgz",
-      "integrity": "sha512-Hhy+BhRBleFjpJ2vchUNN40qgkh0366FWJGqVLYBHev0vpHTrXSA0ryT+74UiW6KWsldNurQMKGqCm1M2zBciQ==",
+      "version": "1.5.3",
+      "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.5.3.tgz",
+      "integrity": "sha512-Y7HYmWaFwPUmkoQCUIAYpKqkOf+SbVj/2fJJZ4RJMCfZp0rTGwRbzQD+HghfnhKOjL9E01okqz+ncJskGYfBNw==",
       "dev": true
     },
     "node_modules/uint8-to-base64": {
@@ -12058,16 +11762,6 @@
       "integrity": "sha512-HXgFDgDommxn5/bIv0cnQZsPhHDA90NPHD6+c/v21U5+Sx5hoP8+dP9IZXBU1gIfvdRfhG8cel9QNPeionfcCQ==",
       "dev": true
     },
-    "node_modules/url-parse": {
-      "version": "1.5.10",
-      "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz",
-      "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==",
-      "dev": true,
-      "dependencies": {
-        "querystringify": "^2.1.1",
-        "requires-port": "^1.0.0"
-      }
-    },
     "node_modules/util-deprecate": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
@@ -12131,14 +11825,14 @@
       }
     },
     "node_modules/vite": {
-      "version": "5.1.6",
-      "resolved": "https://registry.npmjs.org/vite/-/vite-5.1.6.tgz",
-      "integrity": "sha512-yYIAZs9nVfRJ/AiOLCA91zzhjsHUgMjB+EigzFb6W2XTLO8JixBCKCjvhKZaye+NKYHCrkv3Oh50dH9EdLU2RA==",
+      "version": "5.2.2",
+      "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.2.tgz",
+      "integrity": "sha512-FWZbz0oSdLq5snUI0b6sULbz58iXFXdvkZfZWR/F0ZJuKTSPO7v72QPXt6KqYeMFb0yytNp6kZosxJ96Nr/wDQ==",
       "dev": true,
       "dependencies": {
-        "esbuild": "^0.19.3",
-        "postcss": "^8.4.35",
-        "rollup": "^4.2.0"
+        "esbuild": "^0.20.1",
+        "postcss": "^8.4.36",
+        "rollup": "^4.13.0"
       },
       "bin": {
         "vite": "bin/vite.js"
@@ -12213,418 +11907,12 @@
       "integrity": "sha512-KRCIFX3PWVUuEjpi9O7EKLT9E27OqOA3RimIvVx6cziLAUxvnk2VvHQfMrP+mKkqyqqSmnnYyTig3OyDnK/zlA==",
       "dev": true
     },
-    "node_modules/vite/node_modules/@esbuild/aix-ppc64": {
-      "version": "0.19.12",
-      "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz",
-      "integrity": "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==",
-      "cpu": [
-        "ppc64"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "aix"
-      ],
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/vite/node_modules/@esbuild/android-arm": {
-      "version": "0.19.12",
-      "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.12.tgz",
-      "integrity": "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==",
-      "cpu": [
-        "arm"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "android"
-      ],
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/vite/node_modules/@esbuild/android-arm64": {
-      "version": "0.19.12",
-      "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz",
-      "integrity": "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==",
-      "cpu": [
-        "arm64"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "android"
-      ],
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/vite/node_modules/@esbuild/android-x64": {
-      "version": "0.19.12",
-      "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.12.tgz",
-      "integrity": "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==",
-      "cpu": [
-        "x64"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "android"
-      ],
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/vite/node_modules/@esbuild/darwin-arm64": {
-      "version": "0.19.12",
-      "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz",
-      "integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==",
-      "cpu": [
-        "arm64"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "darwin"
-      ],
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/vite/node_modules/@esbuild/darwin-x64": {
-      "version": "0.19.12",
-      "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz",
-      "integrity": "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==",
-      "cpu": [
-        "x64"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "darwin"
-      ],
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/vite/node_modules/@esbuild/freebsd-arm64": {
-      "version": "0.19.12",
-      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz",
-      "integrity": "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==",
-      "cpu": [
-        "arm64"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "freebsd"
-      ],
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/vite/node_modules/@esbuild/freebsd-x64": {
-      "version": "0.19.12",
-      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz",
-      "integrity": "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==",
-      "cpu": [
-        "x64"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "freebsd"
-      ],
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/vite/node_modules/@esbuild/linux-arm": {
-      "version": "0.19.12",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz",
-      "integrity": "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==",
-      "cpu": [
-        "arm"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "linux"
-      ],
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/vite/node_modules/@esbuild/linux-arm64": {
-      "version": "0.19.12",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz",
-      "integrity": "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==",
-      "cpu": [
-        "arm64"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "linux"
-      ],
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/vite/node_modules/@esbuild/linux-ia32": {
-      "version": "0.19.12",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz",
-      "integrity": "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==",
-      "cpu": [
-        "ia32"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "linux"
-      ],
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/vite/node_modules/@esbuild/linux-loong64": {
-      "version": "0.19.12",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz",
-      "integrity": "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==",
-      "cpu": [
-        "loong64"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "linux"
-      ],
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/vite/node_modules/@esbuild/linux-mips64el": {
-      "version": "0.19.12",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz",
-      "integrity": "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==",
-      "cpu": [
-        "mips64el"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "linux"
-      ],
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/vite/node_modules/@esbuild/linux-ppc64": {
-      "version": "0.19.12",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz",
-      "integrity": "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==",
-      "cpu": [
-        "ppc64"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "linux"
-      ],
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/vite/node_modules/@esbuild/linux-riscv64": {
-      "version": "0.19.12",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz",
-      "integrity": "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==",
-      "cpu": [
-        "riscv64"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "linux"
-      ],
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/vite/node_modules/@esbuild/linux-s390x": {
-      "version": "0.19.12",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz",
-      "integrity": "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==",
-      "cpu": [
-        "s390x"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "linux"
-      ],
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/vite/node_modules/@esbuild/linux-x64": {
-      "version": "0.19.12",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz",
-      "integrity": "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==",
-      "cpu": [
-        "x64"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "linux"
-      ],
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/vite/node_modules/@esbuild/netbsd-x64": {
-      "version": "0.19.12",
-      "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz",
-      "integrity": "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==",
-      "cpu": [
-        "x64"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "netbsd"
-      ],
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/vite/node_modules/@esbuild/openbsd-x64": {
-      "version": "0.19.12",
-      "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz",
-      "integrity": "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==",
-      "cpu": [
-        "x64"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "openbsd"
-      ],
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/vite/node_modules/@esbuild/sunos-x64": {
-      "version": "0.19.12",
-      "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz",
-      "integrity": "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==",
-      "cpu": [
-        "x64"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "sunos"
-      ],
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/vite/node_modules/@esbuild/win32-arm64": {
-      "version": "0.19.12",
-      "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz",
-      "integrity": "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==",
-      "cpu": [
-        "arm64"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "win32"
-      ],
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/vite/node_modules/@esbuild/win32-ia32": {
-      "version": "0.19.12",
-      "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz",
-      "integrity": "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==",
-      "cpu": [
-        "ia32"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "win32"
-      ],
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/vite/node_modules/@esbuild/win32-x64": {
-      "version": "0.19.12",
-      "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz",
-      "integrity": "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==",
-      "cpu": [
-        "x64"
-      ],
-      "dev": true,
-      "optional": true,
-      "os": [
-        "win32"
-      ],
-      "engines": {
-        "node": ">=12"
-      }
-    },
     "node_modules/vite/node_modules/@types/estree": {
       "version": "1.0.5",
       "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
       "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
       "dev": true
     },
-    "node_modules/vite/node_modules/esbuild": {
-      "version": "0.19.12",
-      "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz",
-      "integrity": "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==",
-      "dev": true,
-      "hasInstallScript": true,
-      "bin": {
-        "esbuild": "bin/esbuild"
-      },
-      "engines": {
-        "node": ">=12"
-      },
-      "optionalDependencies": {
-        "@esbuild/aix-ppc64": "0.19.12",
-        "@esbuild/android-arm": "0.19.12",
-        "@esbuild/android-arm64": "0.19.12",
-        "@esbuild/android-x64": "0.19.12",
-        "@esbuild/darwin-arm64": "0.19.12",
-        "@esbuild/darwin-x64": "0.19.12",
-        "@esbuild/freebsd-arm64": "0.19.12",
-        "@esbuild/freebsd-x64": "0.19.12",
-        "@esbuild/linux-arm": "0.19.12",
-        "@esbuild/linux-arm64": "0.19.12",
-        "@esbuild/linux-ia32": "0.19.12",
-        "@esbuild/linux-loong64": "0.19.12",
-        "@esbuild/linux-mips64el": "0.19.12",
-        "@esbuild/linux-ppc64": "0.19.12",
-        "@esbuild/linux-riscv64": "0.19.12",
-        "@esbuild/linux-s390x": "0.19.12",
-        "@esbuild/linux-x64": "0.19.12",
-        "@esbuild/netbsd-x64": "0.19.12",
-        "@esbuild/openbsd-x64": "0.19.12",
-        "@esbuild/sunos-x64": "0.19.12",
-        "@esbuild/win32-arm64": "0.19.12",
-        "@esbuild/win32-ia32": "0.19.12",
-        "@esbuild/win32-x64": "0.19.12"
-      }
-    },
     "node_modules/vite/node_modules/fsevents": {
       "version": "2.3.3",
       "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
@@ -12639,6 +11927,34 @@
         "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
       }
     },
+    "node_modules/vite/node_modules/postcss": {
+      "version": "8.4.38",
+      "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz",
+      "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/postcss/"
+        },
+        {
+          "type": "tidelift",
+          "url": "https://tidelift.com/funding/github/npm/postcss"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "dependencies": {
+        "nanoid": "^3.3.7",
+        "picocolors": "^1.0.0",
+        "source-map-js": "^1.2.0"
+      },
+      "engines": {
+        "node": "^10 || ^12 || >=14"
+      }
+    },
     "node_modules/vite/node_modules/rollup": {
       "version": "4.13.0",
       "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.13.0.tgz",
@@ -12843,27 +12159,6 @@
         "vue": "^3.2.29"
       }
     },
-    "node_modules/w3c-xmlserializer": {
-      "version": "5.0.0",
-      "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz",
-      "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==",
-      "dev": true,
-      "dependencies": {
-        "xml-name-validator": "^5.0.0"
-      },
-      "engines": {
-        "node": ">=18"
-      }
-    },
-    "node_modules/w3c-xmlserializer/node_modules/xml-name-validator": {
-      "version": "5.0.0",
-      "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz",
-      "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==",
-      "dev": true,
-      "engines": {
-        "node": ">=18"
-      }
-    },
     "node_modules/watchpack": {
       "version": "2.4.1",
       "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.1.tgz",
@@ -13088,40 +12383,29 @@
         "node": ">=10.13.0"
       }
     },
-    "node_modules/whatwg-encoding": {
-      "version": "3.1.1",
-      "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz",
-      "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==",
-      "dev": true,
-      "dependencies": {
-        "iconv-lite": "0.6.3"
-      },
-      "engines": {
-        "node": ">=18"
-      }
-    },
     "node_modules/whatwg-mimetype": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz",
-      "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==",
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz",
+      "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==",
       "dev": true,
       "engines": {
-        "node": ">=18"
+        "node": ">=12"
       }
     },
     "node_modules/whatwg-url": {
-      "version": "14.0.0",
-      "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.0.0.tgz",
-      "integrity": "sha512-1lfMEm2IEr7RIV+f4lUNPOqfFL+pO+Xw3fJSqmjX9AbXcXcYOkCe1P6+9VBZB6n94af16NfZf+sSk0JCBZC9aw==",
-      "dev": true,
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
+      "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
       "dependencies": {
-        "tr46": "^5.0.0",
-        "webidl-conversions": "^7.0.0"
-      },
-      "engines": {
-        "node": ">=18"
+        "tr46": "~0.0.3",
+        "webidl-conversions": "^3.0.0"
       }
     },
+    "node_modules/whatwg-url/node_modules/webidl-conversions": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
+      "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
+    },
     "node_modules/which": {
       "version": "2.0.2",
       "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
@@ -13344,27 +12628,6 @@
         "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
       }
     },
-    "node_modules/ws": {
-      "version": "8.16.0",
-      "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz",
-      "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==",
-      "dev": true,
-      "engines": {
-        "node": ">=10.0.0"
-      },
-      "peerDependencies": {
-        "bufferutil": "^4.0.1",
-        "utf-8-validate": ">=5.0.2"
-      },
-      "peerDependenciesMeta": {
-        "bufferutil": {
-          "optional": true
-        },
-        "utf-8-validate": {
-          "optional": true
-        }
-      }
-    },
     "node_modules/xml-name-validator": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz",
@@ -13374,12 +12637,6 @@
         "node": ">=12"
       }
     },
-    "node_modules/xmlchars": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
-      "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==",
-      "dev": true
-    },
     "node_modules/y18n": {
       "version": "5.0.8",
       "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
diff --git a/package.json b/package.json
index b8594e8b3e..77a58dacf5 100644
--- a/package.json
+++ b/package.json
@@ -84,7 +84,7 @@
     "eslint-plugin-vue": "9.23.0",
     "eslint-plugin-vue-scoped-css": "2.7.2",
     "eslint-plugin-wc": "2.0.4",
-    "jsdom": "24.0.0",
+    "happy-dom": "14.2.0",
     "markdownlint-cli": "0.39.0",
     "postcss-html": "1.6.0",
     "stylelint": "16.2.1",
diff --git a/vitest.config.js b/vitest.config.js
index be6c0eadfa..ea0fafeee8 100644
--- a/vitest.config.js
+++ b/vitest.config.js
@@ -6,7 +6,7 @@ export default defineConfig({
   test: {
     include: ['web_src/**/*.test.js'],
     setupFiles: ['web_src/js/vitest.setup.js'],
-    environment: 'jsdom',
+    environment: 'happy-dom',
     testTimeout: 20000,
     open: false,
     allowOnly: true,
diff --git a/web_src/js/svg.js b/web_src/js/svg.js
index 471b5136bd..6ad06f599d 100644
--- a/web_src/js/svg.js
+++ b/web_src/js/svg.js
@@ -205,7 +205,7 @@ export const SvgIcon = {
 
     // make the <SvgIcon class="foo" class-name="bar"> classes work together
     const classes = [];
-    for (const cls of svgOuter.classList) {
+    for (const cls of svgOuter.classList.values()) {
       classes.push(cls);
     }
     // TODO: drop the `className/class-name` prop in the future, only use "class" prop

From e5920b4a62655794ad469f7189603654645dc46b Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Thu, 21 Mar 2024 23:07:35 +0800
Subject: [PATCH 39/74] Refactor StringsToInt64s (#29967)

And close #27176

(cherry picked from commit cdb4d1a8db096d60dba04728924dab85def45b19)
---
 models/issues/pull_list.go                 |  9 +++------
 models/issues/pull_test.go                 |  2 --
 modules/base/tool.go                       | 13 ++++++++-----
 modules/base/tool_test.go                  |  7 ++++---
 options/locale/locale_en-US.ini            |  1 +
 routers/api/v1/repo/pull.go                |  9 +++++++--
 routers/web/repo/issue.go                  |  3 +--
 routers/web/user/home.go                   |  7 ++-----
 routers/web/user/notification.go           |  3 +--
 templates/repo/issue/list.tmpl             |  1 +
 templates/repo/issue/milestone_issues.tmpl |  1 +
 templates/user/dashboard/issues.tmpl       |  1 +
 12 files changed, 30 insertions(+), 27 deletions(-)

diff --git a/models/issues/pull_list.go b/models/issues/pull_list.go
index 6df15e8621..084f7d6242 100644
--- a/models/issues/pull_list.go
+++ b/models/issues/pull_list.go
@@ -11,7 +11,6 @@ import (
 	access_model "code.gitea.io/gitea/models/perm/access"
 	"code.gitea.io/gitea/models/unit"
 	user_model "code.gitea.io/gitea/models/user"
-	"code.gitea.io/gitea/modules/base"
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/util"
 
@@ -23,7 +22,7 @@ type PullRequestsOptions struct {
 	db.ListOptions
 	State       string
 	SortType    string
-	Labels      []string
+	Labels      []int64
 	MilestoneID int64
 }
 
@@ -36,11 +35,9 @@ func listPullRequestStatement(ctx context.Context, baseRepoID int64, opts *PullR
 		sess.And("issue.is_closed=?", opts.State == "closed")
 	}
 
-	if labelIDs, err := base.StringsToInt64s(opts.Labels); err != nil {
-		return nil, err
-	} else if len(labelIDs) > 0 {
+	if len(opts.Labels) > 0 {
 		sess.Join("INNER", "issue_label", "issue.id = issue_label.issue_id").
-			In("issue_label.label_id", labelIDs)
+			In("issue_label.label_id", opts.Labels)
 	}
 
 	if opts.MilestoneID > 0 {
diff --git a/models/issues/pull_test.go b/models/issues/pull_test.go
index 213838abec..1a43d8994e 100644
--- a/models/issues/pull_test.go
+++ b/models/issues/pull_test.go
@@ -67,7 +67,6 @@ func TestPullRequestsNewest(t *testing.T) {
 		},
 		State:    "open",
 		SortType: "newest",
-		Labels:   []string{},
 	})
 	assert.NoError(t, err)
 	assert.EqualValues(t, 3, count)
@@ -114,7 +113,6 @@ func TestPullRequestsOldest(t *testing.T) {
 		},
 		State:    "open",
 		SortType: "oldest",
-		Labels:   []string{},
 	})
 	assert.NoError(t, err)
 	assert.EqualValues(t, 3, count)
diff --git a/modules/base/tool.go b/modules/base/tool.go
index 231507546d..e4c3fb1818 100644
--- a/modules/base/tool.go
+++ b/modules/base/tool.go
@@ -150,13 +150,16 @@ func TruncateString(str string, limit int) string {
 
 // StringsToInt64s converts a slice of string to a slice of int64.
 func StringsToInt64s(strs []string) ([]int64, error) {
-	ints := make([]int64, len(strs))
-	for i := range strs {
-		n, err := strconv.ParseInt(strs[i], 10, 64)
+	if strs == nil {
+		return nil, nil
+	}
+	ints := make([]int64, 0, len(strs))
+	for _, s := range strs {
+		n, err := strconv.ParseInt(s, 10, 64)
 		if err != nil {
-			return ints, err
+			return nil, err
 		}
-		ints[i] = n
+		ints = append(ints, n)
 	}
 	return ints, nil
 }
diff --git a/modules/base/tool_test.go b/modules/base/tool_test.go
index d28deb593d..f21b89c74c 100644
--- a/modules/base/tool_test.go
+++ b/modules/base/tool_test.go
@@ -138,12 +138,13 @@ func TestStringsToInt64s(t *testing.T) {
 		assert.NoError(t, err)
 		assert.Equal(t, expected, result)
 	}
+	testSuccess(nil, nil)
 	testSuccess([]string{}, []int64{})
 	testSuccess([]string{"-1234"}, []int64{-1234})
-	testSuccess([]string{"1", "4", "16", "64", "256"},
-		[]int64{1, 4, 16, 64, 256})
+	testSuccess([]string{"1", "4", "16", "64", "256"}, []int64{1, 4, 16, 64, 256})
 
-	_, err := StringsToInt64s([]string{"-1", "a", "$"})
+	ints, err := StringsToInt64s([]string{"-1", "a"})
+	assert.Len(t, ints, 0)
 	assert.Error(t, err)
 }
 
diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini
index f18ab87ef6..8e1f2bedb1 100644
--- a/options/locale/locale_en-US.ini
+++ b/options/locale/locale_en-US.ini
@@ -114,6 +114,7 @@ loading = Loading…
 error = Error
 error404 = The page you are trying to reach either <strong>does not exist</strong> or <strong>you are not authorized</strong> to view it.
 go_back = Go Back
+invalid_data = Invalid data: %v
 
 never = Never
 unknown = Unknown
diff --git a/routers/api/v1/repo/pull.go b/routers/api/v1/repo/pull.go
index c2d86541b6..eec3c49bc4 100644
--- a/routers/api/v1/repo/pull.go
+++ b/routers/api/v1/repo/pull.go
@@ -21,6 +21,7 @@ import (
 	repo_model "code.gitea.io/gitea/models/repo"
 	"code.gitea.io/gitea/models/unit"
 	user_model "code.gitea.io/gitea/models/user"
+	"code.gitea.io/gitea/modules/base"
 	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/gitrepo"
 	"code.gitea.io/gitea/modules/log"
@@ -96,13 +97,17 @@ func ListPullRequests(ctx *context.APIContext) {
 	//   "404":
 	//     "$ref": "#/responses/notFound"
 
+	labelIDs, err := base.StringsToInt64s(ctx.FormStrings("labels"))
+	if err != nil {
+		ctx.Error(http.StatusInternalServerError, "PullRequests", err)
+		return
+	}
 	listOptions := utils.GetListOptions(ctx)
-
 	prs, maxResults, err := issues_model.PullRequests(ctx, ctx.Repo.Repository.ID, &issues_model.PullRequestsOptions{
 		ListOptions: listOptions,
 		State:       ctx.FormTrim("state"),
 		SortType:    ctx.FormTrim("sort"),
-		Labels:      ctx.FormStrings("labels"),
+		Labels:      labelIDs,
 		MilestoneID: ctx.FormInt64("milestone"),
 	})
 	if err != nil {
diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go
index 39b33e15b3..c0ec121336 100644
--- a/routers/web/repo/issue.go
+++ b/routers/web/repo/issue.go
@@ -192,8 +192,7 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption opt
 	if len(selectLabels) > 0 {
 		labelIDs, err = base.StringsToInt64s(strings.Split(selectLabels, ","))
 		if err != nil {
-			ctx.ServerError("StringsToInt64s", err)
-			return
+			ctx.Flash.Error(ctx.Tr("invalid_data", selectLabels), true)
 		}
 	}
 
diff --git a/routers/web/user/home.go b/routers/web/user/home.go
index e58fb95131..f122dc5d9c 100644
--- a/routers/web/user/home.go
+++ b/routers/web/user/home.go
@@ -529,17 +529,14 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
 
 	// Get IDs for labels (a filter option for issues/pulls).
 	// Required for IssuesOptions.
-	var labelIDs []int64
 	selectedLabels := ctx.FormString("labels")
 	if len(selectedLabels) > 0 && selectedLabels != "0" {
 		var err error
-		labelIDs, err = base.StringsToInt64s(strings.Split(selectedLabels, ","))
+		opts.LabelIDs, err = base.StringsToInt64s(strings.Split(selectedLabels, ","))
 		if err != nil {
-			ctx.ServerError("StringsToInt64s", err)
-			return
+			ctx.Flash.Error(ctx.Tr("invalid_data", selectedLabels), true)
 		}
 	}
-	opts.LabelIDs = labelIDs
 
 	// ------------------------------
 	// Get issues as defined by opts.
diff --git a/routers/web/user/notification.go b/routers/web/user/notification.go
index a40c7085ff..2105cfe5c5 100644
--- a/routers/web/user/notification.go
+++ b/routers/web/user/notification.go
@@ -268,8 +268,7 @@ func NotificationSubscriptions(ctx *context.Context) {
 		var err error
 		labelIDs, err = base.StringsToInt64s(strings.Split(selectedLabels, ","))
 		if err != nil {
-			ctx.ServerError("StringsToInt64s", err)
-			return
+			ctx.Flash.Error(ctx.Tr("invalid_data", selectedLabels), true)
 		}
 	}
 
diff --git a/templates/repo/issue/list.tmpl b/templates/repo/issue/list.tmpl
index 62c1d00f00..45bddefa42 100644
--- a/templates/repo/issue/list.tmpl
+++ b/templates/repo/issue/list.tmpl
@@ -2,6 +2,7 @@
 <div role="main" aria-label="{{.Title}}" class="page-content repository issue-list">
 	{{template "repo/header" .}}
 	<div class="ui container">
+	{{template "base/alert" .}}
 
 	{{if .PinnedIssues}}
 		<div id="issue-pins" {{if .IsRepoAdmin}}data-is-repo-admin{{end}}>
diff --git a/templates/repo/issue/milestone_issues.tmpl b/templates/repo/issue/milestone_issues.tmpl
index 35a8a77680..507c3ce37a 100644
--- a/templates/repo/issue/milestone_issues.tmpl
+++ b/templates/repo/issue/milestone_issues.tmpl
@@ -2,6 +2,7 @@
 <div role="main" aria-label="{{.Title}}" class="page-content repository milestone-issue-list">
 	{{template "repo/header" .}}
 	<div class="ui container">
+		{{template "base/alert" .}}
 		<div class="gt-df">
 			<h1 class="gt-mb-3">{{.Milestone.Name}}</h1>
 			{{if not .Repository.IsArchived}}
diff --git a/templates/user/dashboard/issues.tmpl b/templates/user/dashboard/issues.tmpl
index 88afcf58ec..5080821dd1 100644
--- a/templates/user/dashboard/issues.tmpl
+++ b/templates/user/dashboard/issues.tmpl
@@ -2,6 +2,7 @@
 <div role="main" aria-label="{{.Title}}" class="page-content dashboard issues">
 	{{template "user/dashboard/navbar" .}}
 	<div class="ui container">
+		{{template "base/alert" .}}
 		<div class="flex-container">
 			<div class="flex-container-nav">
 				<div class="ui secondary vertical filter menu tw-bg-transparent">

From 64fcf0cb64d455d5ca1420aa832aa057cf61e6dd Mon Sep 17 00:00:00 2001
From: yp05327 <576951401@qq.com>
Date: Fri, 22 Mar 2024 00:48:13 +0900
Subject: [PATCH 40/74] Update register application URL for GitLab (#29959)

Fix #26593
The old URL was updated 7 years ago. Maybe no need to hold it any more.

![image](https://github.com/go-gitea/gitea/assets/18380374/95a0b364-832b-4f10-800e-21845d64df1a)

(cherry picked from commit 4bef1fb3e49127316596cef1d3ca103a199c0536)
---
 options/locale/locale_en-US.ini | 2 +-
 templates/admin/auth/new.tmpl   | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini
index 8e1f2bedb1..aaa30033f0 100644
--- a/options/locale/locale_en-US.ini
+++ b/options/locale/locale_en-US.ini
@@ -3135,7 +3135,7 @@ auths.tip.nextcloud = Register a new OAuth consumer on your instance using the f
 auths.tip.dropbox = Create a new application at https://www.dropbox.com/developers/apps
 auths.tip.facebook = Register a new application at https://developers.facebook.com/apps and add the product "Facebook Login"
 auths.tip.github = Register a new OAuth application on https://github.com/settings/applications/new
-auths.tip.gitlab = Register a new application on https://gitlab.com/profile/applications
+auths.tip.gitlab_new = Register a new application on https://gitlab.com/-/profile/applications
 auths.tip.google_plus = Obtain OAuth2 client credentials from the Google API console at https://console.developers.google.com/
 auths.tip.openid_connect = Use the OpenID Connect Discovery URL (<server>/.well-known/openid-configuration) to specify the endpoints
 auths.tip.twitter = Go to https://dev.twitter.com/apps, create an application and ensure that the “Allow this application to be used to Sign in with Twitter” option is enabled
diff --git a/templates/admin/auth/new.tmpl b/templates/admin/auth/new.tmpl
index d8935341f4..255eac202c 100644
--- a/templates/admin/auth/new.tmpl
+++ b/templates/admin/auth/new.tmpl
@@ -99,7 +99,7 @@
 				<li>GitHub</li>
 				<span>{{ctx.Locale.Tr "admin.auths.tip.github"}}</span>
 				<li>GitLab</li>
-				<span>{{ctx.Locale.Tr "admin.auths.tip.gitlab"}}</span>
+				<span>{{ctx.Locale.Tr "admin.auths.tip.gitlab_new"}}</span>
 				<li>Google</li>
 				<span>{{ctx.Locale.Tr "admin.auths.tip.google_plus"}}</span>
 				<li>OpenID Connect</li>

From 1f1178ce9b8f2c85b88a5b7f9c5180fd187b1f08 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Thu, 21 Mar 2024 17:31:15 +0100
Subject: [PATCH 41/74] Fix various loading states, remove `.loading` class
 (#29920)

Various code was using fomantic `loading` class which I think got broken
a while ago and rendered only a full circle. Fix those to use
`is-loading`.

Before:
<img width="295" alt="Screenshot 2024-03-19 at 22 56 26"
src="https://github.com/go-gitea/gitea/assets/115237/dbe83395-5db4-4868-90bc-3613866a35f0">

After:
<img width="60" alt="Screenshot 2024-03-19 at 22 54 35"
src="https://github.com/go-gitea/gitea/assets/115237/8ac19b7e-035a-4c6d-850b-53a234ef69c2">
<img width="294" alt="Screenshot 2024-03-19 at 22 54 56"
src="https://github.com/go-gitea/gitea/assets/115237/34e819d7-25f7-43a1-9d48-4a68dcd2b6ad">
<img width="320" alt="Screenshot 2024-03-19 at 22 55 16"
src="https://github.com/go-gitea/gitea/assets/115237/05127544-47ff-4e18-9fd8-c84e44c374f8">
<img width="153" alt="Screenshot 2024-03-19 at 23 01 43"
src="https://github.com/go-gitea/gitea/assets/115237/a33248c6-b11d-40ff-82d8-f5a3d85b55aa">
<img width="1300" alt="Screenshot 2024-03-19 at 23 56 25"
src="https://github.com/go-gitea/gitea/assets/115237/562ca876-b5d5-4295-961e-9d2cdab31ab0">
<img width="136" alt="Screenshot 2024-03-20 at 00 00 38"
src="https://github.com/go-gitea/gitea/assets/115237/44838ac4-67f3-4fec-a8e3-978cc5dbdb72">

(cherry picked from commit d6fed9ab88b13e124c5e59ceac5b21a3af52ad24)
---
 templates/admin/notice.tmpl                  |  2 +-
 templates/repo/graph.tmpl                    |  2 +-
 templates/repo/settings/webhook/history.tmpl |  4 +++-
 web_src/css/base.css                         | 14 --------------
 web_src/css/features/gitgraph.css            |  6 ------
 web_src/js/features/admin/common.js          |  2 +-
 web_src/js/features/comp/WebHookEditor.js    |  2 +-
 web_src/js/features/repo-common.js           | 10 ++++++----
 web_src/js/features/repo-issue.js            | 10 +++++-----
 9 files changed, 18 insertions(+), 34 deletions(-)

diff --git a/templates/admin/notice.tmpl b/templates/admin/notice.tmpl
index 26462596bc..f7d77eab1d 100644
--- a/templates/admin/notice.tmpl
+++ b/templates/admin/notice.tmpl
@@ -50,7 +50,7 @@
 									</div>
 								</div>
 								<button class="ui small teal button" id="delete-selection" data-link="{{.Link}}/delete" data-redirect="?page={{.Page.Paginater.Current}}">
-									{{ctx.Locale.Tr "admin.notices.delete_selected"}}
+									<span class="text">{{ctx.Locale.Tr "admin.notices.delete_selected"}}</span>
 								</button>
 							</th>
 						</tr>
diff --git a/templates/repo/graph.tmpl b/templates/repo/graph.tmpl
index 37305d278a..67804f117d 100644
--- a/templates/repo/graph.tmpl
+++ b/templates/repo/graph.tmpl
@@ -50,7 +50,7 @@
 				</div>
 			</h2>
 			<div class="ui dividing"></div>
-			<div class="ui segment loading gt-hidden" id="loading-indicator"></div>
+			<div class="is-loading tw-py-32 gt-hidden" id="loading-indicator"></div>
 			{{template "repo/graph/svgcontainer" .}}
 			{{template "repo/graph/commits" .}}
 		</div>
diff --git a/templates/repo/settings/webhook/history.tmpl b/templates/repo/settings/webhook/history.tmpl
index 4e0f0e9c3e..9f7a7816ea 100644
--- a/templates/repo/settings/webhook/history.tmpl
+++ b/templates/repo/settings/webhook/history.tmpl
@@ -6,7 +6,9 @@
 			<div class="ui right">
 				<!-- the button is wrapped with a span because the tooltip doesn't show on hover if we put data-tooltip-content directly on the button -->
 				<span data-tooltip-content="{{if or $isNew .Webhook.IsActive}}{{ctx.Locale.Tr "repo.settings.webhook.test_delivery_desc"}}{{else}}{{ctx.Locale.Tr "repo.settings.webhook.test_delivery_desc_disabled"}}{{end}}">
-					<button class="ui teal tiny button{{if not (or $isNew .Webhook.IsActive)}} disabled{{end}}" id="test-delivery" data-link="{{.Link}}/test" data-redirect="{{.Link}}">{{ctx.Locale.Tr "repo.settings.webhook.test_delivery"}}</button>
+					<button class="ui teal tiny button{{if not (or $isNew .Webhook.IsActive)}} disabled{{end}}" id="test-delivery" data-link="{{.Link}}/test" data-redirect="{{.Link}}">
+						<span class="text">{{ctx.Locale.Tr "repo.settings.webhook.test_delivery"}}</span>
+					</button>
 			</span>
 			</div>
 		{{end}}
diff --git a/web_src/css/base.css b/web_src/css/base.css
index ab20b64394..5efc751390 100644
--- a/web_src/css/base.css
+++ b/web_src/css/base.css
@@ -720,16 +720,6 @@ img.ui.avatar,
   background: var(--color-active);
 }
 
-.ui.loading.segment::before,
-.ui.loading.form::before {
-  background: none;
-}
-
-.ui.loading.form > *,
-.ui.loading.segment > * {
-  opacity: 0.35;
-}
-
 .ui.form .fields.error .field textarea,
 .ui.form .fields.error .field select,
 .ui.form .fields.error .field input:not([type]),
@@ -821,10 +811,6 @@ input:-webkit-autofill:active,
   opacity: var(--opacity-disabled);
 }
 
-.ui.loading.loading.input > i.icon svg {
-  visibility: hidden;
-}
-
 .text.primary {
   color: var(--color-primary) !important;
 }
diff --git a/web_src/css/features/gitgraph.css b/web_src/css/features/gitgraph.css
index 795e1f2d61..6a04c44e51 100644
--- a/web_src/css/features/gitgraph.css
+++ b/web_src/css/features/gitgraph.css
@@ -4,12 +4,6 @@
   min-height: 350px;
 }
 
-#git-graph-container > .ui.segment.loading {
-  border: 0;
-  z-index: 1;
-  min-height: 246px;
-}
-
 #git-graph-container h2 {
   display: flex;
   justify-content: space-between;
diff --git a/web_src/js/features/admin/common.js b/web_src/js/features/admin/common.js
index 31d840c3e1..0c65f04ab8 100644
--- a/web_src/js/features/admin/common.js
+++ b/web_src/js/features/admin/common.js
@@ -208,7 +208,7 @@ export function initAdminCommon() {
     $('#delete-selection').on('click', async function (e) {
       e.preventDefault();
       const $this = $(this);
-      $this.addClass('loading disabled');
+      $this.addClass('is-loading disabled');
       const data = new FormData();
       $checkboxes.each(function () {
         if ($(this).checkbox('is checked')) {
diff --git a/web_src/js/features/comp/WebHookEditor.js b/web_src/js/features/comp/WebHookEditor.js
index b7ca5a0fcf..d74b59fd2a 100644
--- a/web_src/js/features/comp/WebHookEditor.js
+++ b/web_src/js/features/comp/WebHookEditor.js
@@ -35,7 +35,7 @@ export function initCompWebHookEditor() {
 
   // Test delivery
   document.getElementById('test-delivery')?.addEventListener('click', async function () {
-    this.classList.add('loading', 'disabled');
+    this.classList.add('is-loading', 'disabled');
     await POST(this.getAttribute('data-link'));
     setTimeout(() => {
       window.location.href = this.getAttribute('data-redirect');
diff --git a/web_src/js/features/repo-common.js b/web_src/js/features/repo-common.js
index 669b47a9c5..2c5746c738 100644
--- a/web_src/js/features/repo-common.js
+++ b/web_src/js/features/repo-common.js
@@ -3,18 +3,20 @@ import {hideElem, showElem} from '../utils/dom.js';
 import {POST} from '../modules/fetch.js';
 
 async function getArchive($target, url, first) {
+  const dropdownBtn = $target[0].closest('.ui.dropdown.button');
+
   try {
+    dropdownBtn.classList.add('is-loading');
     const response = await POST(url);
     if (response.status === 200) {
       const data = await response.json();
       if (!data) {
         // XXX Shouldn't happen?
-        $target.closest('.dropdown').children('i').removeClass('loading');
+        dropdownBtn.classList.remove('is-loading');
         return;
       }
 
       if (!data.complete) {
-        $target.closest('.dropdown').children('i').addClass('loading');
         // Wait for only three quarters of a second initially, in case it's
         // quickly archived.
         setTimeout(() => {
@@ -22,12 +24,12 @@ async function getArchive($target, url, first) {
         }, first ? 750 : 2000);
       } else {
         // We don't need to continue checking.
-        $target.closest('.dropdown').children('i').removeClass('loading');
+        dropdownBtn.classList.remove('is-loading');
         window.location.href = url;
       }
     }
   } catch {
-    $target.closest('.dropdown').children('i').removeClass('loading');
+    dropdownBtn.classList.remove('is-loading');
   }
 }
 
diff --git a/web_src/js/features/repo-issue.js b/web_src/js/features/repo-issue.js
index ad2956a600..c91dd06ac9 100644
--- a/web_src/js/features/repo-issue.js
+++ b/web_src/js/features/repo-issue.js
@@ -43,14 +43,14 @@ export function initRepoIssueTimeTracking() {
 
 async function updateDeadline(deadlineString) {
   hideElem($('#deadline-err-invalid-date'));
-  $('#deadline-loader').addClass('loading');
+  $('#deadline-loader').addClass('is-loading');
 
   let realDeadline = null;
   if (deadlineString !== '') {
     const newDate = Date.parse(deadlineString);
 
     if (Number.isNaN(newDate)) {
-      $('#deadline-loader').removeClass('loading');
+      $('#deadline-loader').removeClass('is-loading');
       showElem($('#deadline-err-invalid-date'));
       return false;
     }
@@ -69,7 +69,7 @@ async function updateDeadline(deadlineString) {
     }
   } catch (error) {
     console.error(error);
-    $('#deadline-loader').removeClass('loading');
+    $('#deadline-loader').removeClass('is-loading');
     showElem($('#deadline-err-invalid-date'));
   }
 }
@@ -237,14 +237,14 @@ export function initRepoPullRequestUpdate() {
     e.preventDefault();
     const $this = $(this);
     const redirect = $this.data('redirect');
-    $this.addClass('loading');
+    $this.addClass('is-loading');
     let response;
     try {
       response = await POST($this.data('do'));
     } catch (error) {
       console.error(error);
     } finally {
-      $this.removeClass('loading');
+      $this.removeClass('is-loading');
     }
     let data;
     try {

From 3d99b43dd2b160216d6f742ba3c00b4a94fb453d Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Fri, 22 Mar 2024 00:58:14 +0100
Subject: [PATCH 42/74] remove PATH and GOPATH modification in Makefile
 (#29978)

We don't need these modifications anymore since all tool
dependencies run via `go run`.

(cherry picked from commit ef33dcf946cc9754b51c955975d67f871702b958)
---
 Makefile | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/Makefile b/Makefile
index 4cf8837f72..b1c4864c7a 100644
--- a/Makefile
+++ b/Makefile
@@ -44,9 +44,6 @@ DOCKER_TAG ?= latest
 DOCKER_REF := $(DOCKER_IMAGE):$(DOCKER_TAG)
 
 ifeq ($(HAS_GO), yes)
-	GOPATH ?= $(shell $(GO) env GOPATH)
-	export PATH := $(GOPATH)/bin:$(PATH)
-
 	CGO_EXTRA_CFLAGS := -DSQLITE_MAX_VARIABLE_NUMBER=32766
 	CGO_CFLAGS ?= $(shell $(GO) env CGO_CFLAGS) $(CGO_EXTRA_CFLAGS)
 endif

From 8848b0ea2b34cca5f9a2d1f54e77b609579d4d97 Mon Sep 17 00:00:00 2001
From: Zettat123 <zettat123@gmail.com>
Date: Fri, 22 Mar 2024 11:19:17 +0800
Subject: [PATCH 43/74] Fix bugs in rerunning jobs (#29955)

Fix #28761
Fix #27884
Fix #28093

## Changes

### Rerun all jobs
When rerun all jobs, status of the jobs with `needs` will be set to
`blocked` instead of `waiting`. Therefore, these jobs will not run until
the required jobs are completed.

### Rerun a single job
When a single job is rerun, its dependents should also be rerun, just
like GitHub does
(https://github.com/go-gitea/gitea/issues/28761#issuecomment-2008620820).
In this case, only the specified job will be set to `waiting`, its
dependents will be set to `blocked` to wait the job.

### Show warning if every job has `needs`
If every job in a workflow has `needs`, all jobs will be blocked and no
job can be run. So I add a warning message.

<img
src="https://github.com/go-gitea/gitea/assets/15528715/88f43511-2360-465d-be96-ee92b57ff67b"
width="480px" />

(cherry picked from commit 2f060c5834d81f0317c795fc281f9a07e03e5962)
---
 options/locale/locale_en-US.ini     |  1 +
 routers/web/repo/actions/actions.go | 10 +++++-
 routers/web/repo/actions/view.go    | 26 +++++++++++++---
 services/actions/rerun.go           | 38 +++++++++++++++++++++++
 services/actions/rerun_test.go      | 48 +++++++++++++++++++++++++++++
 5 files changed, 117 insertions(+), 6 deletions(-)
 create mode 100644 services/actions/rerun.go
 create mode 100644 services/actions/rerun_test.go

diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini
index aaa30033f0..1e32cb86f5 100644
--- a/options/locale/locale_en-US.ini
+++ b/options/locale/locale_en-US.ini
@@ -3673,6 +3673,7 @@ runs.pushed_by = pushed by
 runs.workflow = Workflow
 runs.invalid_workflow_helper = Workflow config file is invalid. Please check your config file: %s
 runs.no_matching_online_runner_helper = No matching online runner with label: %s
+runs.no_job_without_needs = The workflow must contain at least one job without dependencies.
 runs.actor = Actor
 runs.status = Status
 runs.actors_no_select = All actors
diff --git a/routers/web/repo/actions/actions.go b/routers/web/repo/actions/actions.go
index f27329aa0f..6059ad1414 100644
--- a/routers/web/repo/actions/actions.go
+++ b/routers/web/repo/actions/actions.go
@@ -104,8 +104,13 @@ func List(ctx *context.Context) {
 				workflows = append(workflows, workflow)
 				continue
 			}
-			// Check whether have matching runner
+			// The workflow must contain at least one job without "needs". Otherwise, a deadlock will occur and no jobs will be able to run.
+			hasJobWithoutNeeds := false
+			// Check whether have matching runner and a job without "needs"
 			for _, j := range wf.Jobs {
+				if !hasJobWithoutNeeds && len(j.Needs()) == 0 {
+					hasJobWithoutNeeds = true
+				}
 				runsOnList := j.RunsOn()
 				for _, ro := range runsOnList {
 					if strings.Contains(ro, "${{") {
@@ -123,6 +128,9 @@ func List(ctx *context.Context) {
 					break
 				}
 			}
+			if !hasJobWithoutNeeds {
+				workflow.ErrMsg = ctx.Locale.TrString("actions.runs.no_job_without_needs")
+			}
 			workflows = append(workflows, workflow)
 		}
 	}
diff --git a/routers/web/repo/actions/view.go b/routers/web/repo/actions/view.go
index 23ce70a153..fa687bbb93 100644
--- a/routers/web/repo/actions/view.go
+++ b/routers/web/repo/actions/view.go
@@ -353,12 +353,25 @@ func Rerun(ctx *context_module.Context) {
 		return
 	}
 
-	if jobIndexStr != "" {
-		jobs = []*actions_model.ActionRunJob{job}
+	if jobIndexStr == "" { // rerun all jobs
+		for _, j := range jobs {
+			// if the job has needs, it should be set to "blocked" status to wait for other jobs
+			shouldBlock := len(j.Needs) > 0
+			if err := rerunJob(ctx, j, shouldBlock); err != nil {
+				ctx.Error(http.StatusInternalServerError, err.Error())
+				return
+			}
+		}
+		ctx.JSON(http.StatusOK, struct{}{})
+		return
 	}
 
-	for _, j := range jobs {
-		if err := rerunJob(ctx, j); err != nil {
+	rerunJobs := actions_service.GetAllRerunJobs(job, jobs)
+
+	for _, j := range rerunJobs {
+		// jobs other than the specified one should be set to "blocked" status
+		shouldBlock := j.JobID != job.JobID
+		if err := rerunJob(ctx, j, shouldBlock); err != nil {
 			ctx.Error(http.StatusInternalServerError, err.Error())
 			return
 		}
@@ -367,7 +380,7 @@ func Rerun(ctx *context_module.Context) {
 	ctx.JSON(http.StatusOK, struct{}{})
 }
 
-func rerunJob(ctx *context_module.Context, job *actions_model.ActionRunJob) error {
+func rerunJob(ctx *context_module.Context, job *actions_model.ActionRunJob, shouldBlock bool) error {
 	status := job.Status
 	if !status.IsDone() {
 		return nil
@@ -375,6 +388,9 @@ func rerunJob(ctx *context_module.Context, job *actions_model.ActionRunJob) erro
 
 	job.TaskID = 0
 	job.Status = actions_model.StatusWaiting
+	if shouldBlock {
+		job.Status = actions_model.StatusBlocked
+	}
 	job.Started = 0
 	job.Stopped = 0
 
diff --git a/services/actions/rerun.go b/services/actions/rerun.go
new file mode 100644
index 0000000000..60f6650905
--- /dev/null
+++ b/services/actions/rerun.go
@@ -0,0 +1,38 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package actions
+
+import (
+	actions_model "code.gitea.io/gitea/models/actions"
+	"code.gitea.io/gitea/modules/container"
+)
+
+// GetAllRerunJobs get all jobs that need to be rerun when job should be rerun
+func GetAllRerunJobs(job *actions_model.ActionRunJob, allJobs []*actions_model.ActionRunJob) []*actions_model.ActionRunJob {
+	rerunJobs := []*actions_model.ActionRunJob{job}
+	rerunJobsIDSet := make(container.Set[string])
+	rerunJobsIDSet.Add(job.JobID)
+
+	for {
+		found := false
+		for _, j := range allJobs {
+			if rerunJobsIDSet.Contains(j.JobID) {
+				continue
+			}
+			for _, need := range j.Needs {
+				if rerunJobsIDSet.Contains(need) {
+					found = true
+					rerunJobs = append(rerunJobs, j)
+					rerunJobsIDSet.Add(j.JobID)
+					break
+				}
+			}
+		}
+		if !found {
+			break
+		}
+	}
+
+	return rerunJobs
+}
diff --git a/services/actions/rerun_test.go b/services/actions/rerun_test.go
new file mode 100644
index 0000000000..a98de7b788
--- /dev/null
+++ b/services/actions/rerun_test.go
@@ -0,0 +1,48 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package actions
+
+import (
+	"testing"
+
+	actions_model "code.gitea.io/gitea/models/actions"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestGetAllRerunJobs(t *testing.T) {
+	job1 := &actions_model.ActionRunJob{JobID: "job1"}
+	job2 := &actions_model.ActionRunJob{JobID: "job2", Needs: []string{"job1"}}
+	job3 := &actions_model.ActionRunJob{JobID: "job3", Needs: []string{"job2"}}
+	job4 := &actions_model.ActionRunJob{JobID: "job4", Needs: []string{"job2", "job3"}}
+
+	jobs := []*actions_model.ActionRunJob{job1, job2, job3, job4}
+
+	testCases := []struct {
+		job       *actions_model.ActionRunJob
+		rerunJobs []*actions_model.ActionRunJob
+	}{
+		{
+			job1,
+			[]*actions_model.ActionRunJob{job1, job2, job3, job4},
+		},
+		{
+			job2,
+			[]*actions_model.ActionRunJob{job2, job3, job4},
+		},
+		{
+			job3,
+			[]*actions_model.ActionRunJob{job3, job4},
+		},
+		{
+			job4,
+			[]*actions_model.ActionRunJob{job4},
+		},
+	}
+
+	for _, tc := range testCases {
+		rerunJobs := GetAllRerunJobs(tc.job, jobs)
+		assert.ElementsMatch(t, tc.rerunJobs, rerunJobs)
+	}
+}

From d4ea1c2559ed1e814fd4f3f52ff601c1d42c2b85 Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Fri, 22 Mar 2024 19:17:30 +0800
Subject: [PATCH 44/74] Fix some pending problems (#29985)

These changes are quite independent and trivial, so I don't want to open
too many PRs.

* https://github.com/go-gitea/gitea/pull/29882#discussion_r1529607091
    * the `f.Close` should be called properly
* the error message could be more meaningful
(https://github.com/go-gitea/gitea/pull/29882#pullrequestreview-1942557935)
*
https://github.com/go-gitea/gitea/pull/29859#pullrequestreview-1942324716
    * the new translation strings don't take arguments
* https://github.com/go-gitea/gitea/pull/28710#discussion_r1443778807
    * stale for long time
*  #28140
    * a form was forgotten to be changed to work with backend code

(cherry picked from commit 226231ea27d4f2b0f09fa4efb39501507613b284)

Conflicts:
	templates/repo/issue/view_content/pull.tmpl
	discarded because unexplained
	templates/status/404.tmpl
	implemented differently in Forgejo
---
 models/asymkey/ssh_key_authorized_keys.go       | 9 ++++-----
 models/asymkey/ssh_key_authorized_principals.go | 9 ++++-----
 modules/actions/log.go                          | 2 +-
 modules/git/commit.go                           | 5 ++---
 modules/git/repo_stats.go                       | 6 +++---
 modules/markup/csv/csv.go                       | 5 ++---
 routers/web/repo/compare.go                     | 5 ++---
 routers/web/repo/editor.go                      | 4 ++--
 services/doctor/authorizedkeys.go               | 6 +++---
 web_src/js/components/PullRequestMergeForm.vue  | 1 +
 10 files changed, 24 insertions(+), 28 deletions(-)

diff --git a/models/asymkey/ssh_key_authorized_keys.go b/models/asymkey/ssh_key_authorized_keys.go
index 8c3e0aa81d..d3f9f3f3be 100644
--- a/models/asymkey/ssh_key_authorized_keys.go
+++ b/models/asymkey/ssh_key_authorized_keys.go
@@ -198,6 +198,8 @@ func RegeneratePublicKeys(ctx context.Context, t io.StringWriter) error {
 		if err != nil {
 			return err
 		}
+		defer f.Close()
+
 		scanner := bufio.NewScanner(f)
 		for scanner.Scan() {
 			line := scanner.Text()
@@ -207,15 +209,12 @@ func RegeneratePublicKeys(ctx context.Context, t io.StringWriter) error {
 			}
 			_, err = t.WriteString(line + "\n")
 			if err != nil {
-				f.Close()
 				return err
 			}
 		}
-		err = scanner.Err()
-		if err != nil {
-			return fmt.Errorf("scan: %w", err)
+		if err = scanner.Err(); err != nil {
+			return fmt.Errorf("RegeneratePublicKeys scan: %w", err)
 		}
-		f.Close()
 	}
 	return nil
 }
diff --git a/models/asymkey/ssh_key_authorized_principals.go b/models/asymkey/ssh_key_authorized_principals.go
index 9e6cc3685b..f85de12aae 100644
--- a/models/asymkey/ssh_key_authorized_principals.go
+++ b/models/asymkey/ssh_key_authorized_principals.go
@@ -120,6 +120,8 @@ func regeneratePrincipalKeys(ctx context.Context, t io.StringWriter) error {
 		if err != nil {
 			return err
 		}
+		defer f.Close()
+
 		scanner := bufio.NewScanner(f)
 		for scanner.Scan() {
 			line := scanner.Text()
@@ -129,15 +131,12 @@ func regeneratePrincipalKeys(ctx context.Context, t io.StringWriter) error {
 			}
 			_, err = t.WriteString(line + "\n")
 			if err != nil {
-				f.Close()
 				return err
 			}
 		}
-		err = scanner.Err()
-		if err != nil {
-			return fmt.Errorf("scan: %w", err)
+		if err = scanner.Err(); err != nil {
+			return fmt.Errorf("regeneratePrincipalKeys scan: %w", err)
 		}
-		f.Close()
 	}
 	return nil
 }
diff --git a/modules/actions/log.go b/modules/actions/log.go
index cdf18646aa..c38082b5dc 100644
--- a/modules/actions/log.go
+++ b/modules/actions/log.go
@@ -100,7 +100,7 @@ func ReadLogs(ctx context.Context, inStorage bool, filename string, offset, limi
 	}
 
 	if err := scanner.Err(); err != nil {
-		return nil, fmt.Errorf("scan: %w", err)
+		return nil, fmt.Errorf("ReadLogs scan: %w", err)
 	}
 
 	return rows, nil
diff --git a/modules/git/commit.go b/modules/git/commit.go
index 47237d2d58..00681e3b2f 100644
--- a/modules/git/commit.go
+++ b/modules/git/commit.go
@@ -391,9 +391,8 @@ func (c *Commit) GetSubModules() (*ObjectCache, error) {
 			}
 		}
 	}
-	err = scanner.Err()
-	if err != nil {
-		return nil, fmt.Errorf("scan: %w", err)
+	if err = scanner.Err(); err != nil {
+		return nil, fmt.Errorf("GetSubModules scan: %w", err)
 	}
 
 	return c.submoduleCache, nil
diff --git a/modules/git/repo_stats.go b/modules/git/repo_stats.go
index ce82946873..83220104bd 100644
--- a/modules/git/repo_stats.go
+++ b/modules/git/repo_stats.go
@@ -124,9 +124,9 @@ func (repo *Repository) GetCodeActivityStats(fromTime time.Time, branch string)
 					}
 				}
 			}
-			err = scanner.Err()
-			if err != nil {
-				return fmt.Errorf("scan: %w", err)
+			if err = scanner.Err(); err != nil {
+				_ = stdoutReader.Close()
+				return fmt.Errorf("GetCodeActivityStats scan: %w", err)
 			}
 			a := make([]*CodeActivityAuthor, 0, len(authors))
 			for _, v := range authors {
diff --git a/modules/markup/csv/csv.go b/modules/markup/csv/csv.go
index 50bb918442..1dd26eb8ac 100644
--- a/modules/markup/csv/csv.go
+++ b/modules/markup/csv/csv.go
@@ -124,9 +124,8 @@ func (Renderer) fallbackRender(input io.Reader, tmpBlock *bufio.Writer) error {
 			return err
 		}
 	}
-	err = scan.Err()
-	if err != nil {
-		return fmt.Errorf("scan: %w", err)
+	if err = scan.Err(); err != nil {
+		return fmt.Errorf("fallbackRender scan: %w", err)
 	}
 
 	_, err = tmpBlock.WriteString("</pre>")
diff --git a/routers/web/repo/compare.go b/routers/web/repo/compare.go
index bf42b77b66..7b5243e6b7 100644
--- a/routers/web/repo/compare.go
+++ b/routers/web/repo/compare.go
@@ -980,9 +980,8 @@ func getExcerptLines(commit *git.Commit, filePath string, idxLeft, idxRight, chu
 		}
 		diffLines = append(diffLines, diffLine)
 	}
-	err = scanner.Err()
-	if err != nil {
-		return nil, fmt.Errorf("scan: %w", err)
+	if err = scanner.Err(); err != nil {
+		return nil, fmt.Errorf("getExcerptLines scan: %w", err)
 	}
 	return diffLines, nil
 }
diff --git a/routers/web/repo/editor.go b/routers/web/repo/editor.go
index edfae96050..ea0c8ddb7e 100644
--- a/routers/web/repo/editor.go
+++ b/routers/web/repo/editor.go
@@ -374,9 +374,9 @@ func editFilePost(ctx *context.Context, form forms.EditRepoFileForm, isNewFile b
 				ctx.Error(http.StatusInternalServerError, err.Error())
 			}
 		} else if models.IsErrCommitIDDoesNotMatch(err) {
-			ctx.RenderWithErr(ctx.Tr("repo.editor.commit_id_not_matching", ctx.Repo.RepoLink+"/compare/"+util.PathEscapeSegments(form.LastCommit)+"..."+util.PathEscapeSegments(ctx.Repo.CommitID)), tplEditFile, &form)
+			ctx.RenderWithErr(ctx.Tr("repo.editor.commit_id_not_matching"), tplEditFile, &form)
 		} else if git.IsErrPushOutOfDate(err) {
-			ctx.RenderWithErr(ctx.Tr("repo.editor.push_out_of_date", ctx.Repo.RepoLink+"/compare/"+util.PathEscapeSegments(form.LastCommit)+"..."+util.PathEscapeSegments(form.NewBranchName)), tplEditFile, &form)
+			ctx.RenderWithErr(ctx.Tr("repo.editor.push_out_of_date"), tplEditFile, &form)
 		} else if git.IsErrPushRejected(err) {
 			errPushRej := err.(*git.ErrPushRejected)
 			if len(errPushRej.Message) == 0 {
diff --git a/services/doctor/authorizedkeys.go b/services/doctor/authorizedkeys.go
index 43740af3db..eb6dec613f 100644
--- a/services/doctor/authorizedkeys.go
+++ b/services/doctor/authorizedkeys.go
@@ -50,11 +50,11 @@ func checkAuthorizedKeys(ctx context.Context, logger log.Logger, autofix bool) e
 		}
 		linesInAuthorizedKeys.Add(line)
 	}
-	err = scanner.Err()
-	if err != nil {
+	if err = scanner.Err(); err != nil {
 		return fmt.Errorf("scan: %w", err)
 	}
-	f.Close()
+	// although there is a "defer close" above, here close explicitly before the generating, because it needs to open the file for writing again
+	_ = f.Close()
 
 	// now we regenerate and check if there are any lines missing
 	regenerated := &bytes.Buffer{}
diff --git a/web_src/js/components/PullRequestMergeForm.vue b/web_src/js/components/PullRequestMergeForm.vue
index b0b10b6252..170d0d85c6 100644
--- a/web_src/js/components/PullRequestMergeForm.vue
+++ b/web_src/js/components/PullRequestMergeForm.vue
@@ -94,6 +94,7 @@ export default {
     <!-- eslint-disable-next-line vue/no-v-html -->
     <div v-if="mergeForm.hasPendingPullRequestMerge" v-html="mergeForm.hasPendingPullRequestMergeTip" class="ui info message"/>
 
+    <!-- another similar form is in pull.tmpl (manual merge)-->
     <form class="ui form form-fetch-action" v-if="showActionForm" :action="mergeForm.baseLink+'/merge'" method="post">
       <input type="hidden" name="_csrf" :value="csrfToken">
       <input type="hidden" name="head_commit_id" v-model="mergeForm.pullHeadCommitID">

From 9cf4ab033818c01eeaa0fe7ac11d07519697fdcb Mon Sep 17 00:00:00 2001
From: HEREYUA <37935145+HEREYUA@users.noreply.github.com>
Date: Fri, 22 Mar 2024 19:22:36 +0800
Subject: [PATCH 45/74] Fix: Abnormal strings appear when comments are saved
 after editing (#29991)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Partially resolved(The second problem):
[#29986](https://github.com/go-gitea/gitea/issues/29986)

**Before**
HTML strings appear when comments are saved after editing

![image](https://github.com/go-gitea/gitea/assets/37935145/c356d99a-8473-4cc5-8e38-1b207ccd8b12)

**After**

https://github.com/go-gitea/gitea/assets/37935145/525601f9-3ee1-4266-9105-36d82b91b1c8
(cherry picked from commit bf34723491dcbb45dee7888c574e295cae6096be)
---
 web_src/js/features/repo-legacy.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/web_src/js/features/repo-legacy.js b/web_src/js/features/repo-legacy.js
index 11bb758187..33d8ab2918 100644
--- a/web_src/js/features/repo-legacy.js
+++ b/web_src/js/features/repo-legacy.js
@@ -436,7 +436,7 @@ async function onEditContent(event) {
       const $content = $segment;
       if (!$content.find('.dropzone-attachments').length) {
         if (data.attachments !== '') {
-          $content[0].append(data.attachments);
+          $content[0].insertAdjacentHTML('beforeend', data.attachments);
         }
       } else if (data.attachments === '') {
         $content.find('.dropzone-attachments').remove();

From f63ed42087342673f8d8a46ca5e55c5f5cef2065 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Fri, 22 Mar 2024 12:47:50 +0100
Subject: [PATCH 46/74] Remove fomantic site module (#29980)

Had to fiddle a bit with the css ordering, but seems to work well now
and should render exactly like before. Some of the CSS may be
unnecessary, but I kept it for now.

(cherry picked from commit 6845717158991d41fc52690de857e2a4987f1c5c)
---
 web_src/css/base.css                |  88 ++++-
 web_src/fomantic/build/semantic.css | 177 ----------
 web_src/fomantic/build/semantic.js  | 494 ----------------------------
 web_src/fomantic/semantic.json      |   1 -
 4 files changed, 79 insertions(+), 681 deletions(-)

diff --git a/web_src/css/base.css b/web_src/css/base.css
index 5efc751390..e0c43442af 100644
--- a/web_src/css/base.css
+++ b/web_src/css/base.css
@@ -37,6 +37,23 @@
   border-color: currentcolor;
 }
 
+html, body {
+  height: 100%;
+  font-size: 14px;
+}
+
+body {
+  line-height: 1.4285rem;
+  font-family: var(--fonts-regular);
+  color: var(--color-text);
+  background-color: var(--color-body);
+  tab-size: var(--tab-size);
+  display: flex;
+  flex-direction: column;
+  overflow-x: visible;
+  overflow-wrap: break-word;
+}
+
 textarea {
   font-family: var(--fonts-regular);
 }
@@ -60,13 +77,65 @@ h6 {
   font-weight: var(--font-weight-semibold);
 }
 
-body {
-  color: var(--color-text);
-  background-color: var(--color-body);
-  tab-size: var(--tab-size);
-  display: flex;
-  flex-direction: column;
-  overflow-wrap: break-word;
+h1,
+h2,
+h3,
+h4,
+h5 {
+  line-height: 1.28571429;
+  margin: calc(2rem - 0.1428571428571429em) 0 1rem;
+  font-weight: var(--font-weight-medium);
+  padding: 0;
+}
+
+h1 {
+  min-height: 1rem;
+  font-size: 2rem;
+}
+
+h2 {
+  font-size: 1.71428571rem;
+}
+
+h3 {
+  font-size: 1.28571429rem;
+}
+
+h4 {
+  font-size: 1.07142857rem;
+}
+
+h5 {
+  font-size: 1rem;
+}
+
+h1:first-child,
+h2:first-child,
+h3:first-child,
+h4:first-child,
+h5:first-child {
+  margin-top: 0;
+}
+
+h1:last-child,
+h2:last-child,
+h3:last-child,
+h4:last-child,
+h5:last-child {
+  margin-bottom: 0;
+}
+
+p {
+  margin: 0 0 1em;
+  line-height: 1.4285;
+}
+
+p:first-child {
+  margin-top: 0;
+}
+
+p:last-child {
+  margin-bottom: 0;
 }
 
 table {
@@ -132,8 +201,8 @@ h1.error-code {
 }
 
 ::selection {
-  background: var(--color-primary-light-1) !important;
-  color: var(--color-white) !important;
+  background: var(--color-primary-light-1);
+  color: var(--color-white);
 }
 
 ::placeholder,
@@ -157,6 +226,7 @@ h1.error-code {
 a {
   color: var(--color-primary);
   cursor: pointer;
+  text-decoration: none;
   text-decoration-skip-ink: all;
 }
 
diff --git a/web_src/fomantic/build/semantic.css b/web_src/fomantic/build/semantic.css
index 538dfefdc1..099bb94c39 100644
--- a/web_src/fomantic/build/semantic.css
+++ b/web_src/fomantic/build/semantic.css
@@ -14952,183 +14952,6 @@ Floated Menu / Item
          Theme Overrides
 *******************************/
 
-/*******************************
-         Site Overrides
-*******************************/
-/*!
- * # Fomantic-UI - Site
- * http://github.com/fomantic/Fomantic-UI/
- *
- *
- * Released under the MIT license
- * http://opensource.org/licenses/MIT
- *
- */
-
-/*******************************
-             Page
-*******************************/
-
-html,
-body {
-  height: 100%;
-}
-
-html {
-  font-size: 14px;
-}
-
-body {
-  margin: 0;
-  padding: 0;
-  overflow-x: visible;
-  min-width: 320px;
-  background: #FFFFFF;
-  font-family: var(--fonts-regular);
-  font-size: 14px;
-  line-height: 1.4285em;
-  color: rgba(0, 0, 0, 0.87);
-}
-
-/*******************************
-             Headers
-*******************************/
-
-h1,
-h2,
-h3,
-h4,
-h5 {
-  font-family: var(--fonts-regular);
-  line-height: 1.28571429em;
-  margin: calc(2rem - 0.1428571428571429em) 0 1rem;
-  font-weight: 500;
-  padding: 0;
-}
-
-h1 {
-  min-height: 1rem;
-  font-size: 2rem;
-}
-
-h2 {
-  font-size: 1.71428571rem;
-}
-
-h3 {
-  font-size: 1.28571429rem;
-}
-
-h4 {
-  font-size: 1.07142857rem;
-}
-
-h5 {
-  font-size: 1rem;
-}
-
-h1:first-child,
-h2:first-child,
-h3:first-child,
-h4:first-child,
-h5:first-child {
-  margin-top: 0;
-}
-
-h1:last-child,
-h2:last-child,
-h3:last-child,
-h4:last-child,
-h5:last-child {
-  margin-bottom: 0;
-}
-
-/*******************************
-             Text
-*******************************/
-
-p {
-  margin: 0 0 1em;
-  line-height: 1.4285em;
-}
-
-p:first-child {
-  margin-top: 0;
-}
-
-p:last-child {
-  margin-bottom: 0;
-}
-
-/*-------------------
-        Links
---------------------*/
-
-a {
-  color: #4183C4;
-  text-decoration: none;
-}
-
-a:hover {
-  color: #1e70bf;
-  text-decoration: underline;
-}
-
-/*******************************
-         Scrollbars
-*******************************/
-
-/*******************************
-          Highlighting
-*******************************/
-
-/* Site */
-
-::-webkit-selection {
-  background-color: #CCE2FF;
-  color: rgba(0, 0, 0, 0.87);
-}
-
-::-moz-selection {
-  background-color: #CCE2FF;
-  color: rgba(0, 0, 0, 0.87);
-}
-
-::selection {
-  background-color: #CCE2FF;
-  color: rgba(0, 0, 0, 0.87);
-}
-
-/* Form */
-
-textarea::-webkit-selection,
-input::-webkit-selection {
-  background-color: rgba(100, 100, 100, 0.4);
-  color: rgba(0, 0, 0, 0.87);
-}
-
-textarea::-moz-selection,
-input::-moz-selection {
-  background-color: rgba(100, 100, 100, 0.4);
-  color: rgba(0, 0, 0, 0.87);
-}
-
-textarea::-moz-selection,
-input::-moz-selection {
-  background-color: rgba(100, 100, 100, 0.4);
-  color: rgba(0, 0, 0, 0.87);
-}
-
-textarea::selection,
-input::selection {
-  background-color: rgba(100, 100, 100, 0.4);
-  color: rgba(0, 0, 0, 0.87);
-}
-
-/*******************************
-        Global Overrides
-*******************************/
-
 /*******************************
          Site Overrides
 *******************************/
diff --git a/web_src/fomantic/build/semantic.js b/web_src/fomantic/build/semantic.js
index 2a05d94d72..1199e9c82f 100644
--- a/web_src/fomantic/build/semantic.js
+++ b/web_src/fomantic/build/semantic.js
@@ -11864,500 +11864,6 @@ $.fn.search.settings = {
   }
 };
 
-})( jQuery, window, document );
-
-/*!
- * # Fomantic-UI - Site
- * http://github.com/fomantic/Fomantic-UI/
- *
- *
- * Released under the MIT license
- * http://opensource.org/licenses/MIT
- *
- */
-
-;(function ($, window, document, undefined) {
-
-$.isFunction = $.isFunction || function(obj) {
-    return typeof obj === "function" && typeof obj.nodeType !== "number";
-};
-
-$.site = $.fn.site = function(parameters) {
-  var
-    time           = new Date().getTime(),
-    performance    = [],
-
-    query          = arguments[0],
-    methodInvoked  = (typeof query == 'string'),
-    queryArguments = [].slice.call(arguments, 1),
-
-    settings        = ( $.isPlainObject(parameters) )
-      ? $.extend(true, {}, $.site.settings, parameters)
-      : $.extend({}, $.site.settings),
-
-    namespace       = settings.namespace,
-    error           = settings.error,
-
-    moduleNamespace = 'module-' + namespace,
-
-    $document       = $(document),
-    $module         = $document,
-    element         = this,
-    instance        = $module.data(moduleNamespace),
-
-    module,
-    returnedValue
-  ;
-  module = {
-
-    initialize: function() {
-      module.instantiate();
-    },
-
-    instantiate: function() {
-      module.verbose('Storing instance of site', module);
-      instance = module;
-      $module
-        .data(moduleNamespace, module)
-      ;
-    },
-
-    normalize: function() {
-      module.fix.console();
-      module.fix.requestAnimationFrame();
-    },
-
-    fix: {
-      console: function() {
-        module.debug('Normalizing window.console');
-        if (console === undefined || console.log === undefined) {
-          module.verbose('Console not available, normalizing events');
-          module.disable.console();
-        }
-        if (typeof console.group == 'undefined' || typeof console.groupEnd == 'undefined' || typeof console.groupCollapsed == 'undefined') {
-          module.verbose('Console group not available, normalizing events');
-          window.console.group = function() {};
-          window.console.groupEnd = function() {};
-          window.console.groupCollapsed = function() {};
-        }
-        if (typeof console.markTimeline == 'undefined') {
-          module.verbose('Mark timeline not available, normalizing events');
-          window.console.markTimeline = function() {};
-        }
-      },
-      consoleClear: function() {
-        module.debug('Disabling programmatic console clearing');
-        window.console.clear = function() {};
-      },
-      requestAnimationFrame: function() {
-        module.debug('Normalizing requestAnimationFrame');
-        if(window.requestAnimationFrame === undefined) {
-          module.debug('RequestAnimationFrame not available, normalizing event');
-          window.requestAnimationFrame = window.requestAnimationFrame
-            || window.mozRequestAnimationFrame
-            || window.webkitRequestAnimationFrame
-            || window.msRequestAnimationFrame
-            || function(callback) { setTimeout(callback, 0); }
-          ;
-        }
-      }
-    },
-
-    moduleExists: function(name) {
-      return ($.fn[name] !== undefined && $.fn[name].settings !== undefined);
-    },
-
-    enabled: {
-      modules: function(modules) {
-        var
-          enabledModules = []
-        ;
-        modules = modules || settings.modules;
-        $.each(modules, function(index, name) {
-          if(module.moduleExists(name)) {
-            enabledModules.push(name);
-          }
-        });
-        return enabledModules;
-      }
-    },
-
-    disabled: {
-      modules: function(modules) {
-        var
-          disabledModules = []
-        ;
-        modules = modules || settings.modules;
-        $.each(modules, function(index, name) {
-          if(!module.moduleExists(name)) {
-            disabledModules.push(name);
-          }
-        });
-        return disabledModules;
-      }
-    },
-
-    change: {
-      setting: function(setting, value, modules, modifyExisting) {
-        modules = (typeof modules === 'string')
-          ? (modules === 'all')
-            ? settings.modules
-            : [modules]
-          : modules || settings.modules
-        ;
-        modifyExisting = (modifyExisting !== undefined)
-          ? modifyExisting
-          : true
-        ;
-        $.each(modules, function(index, name) {
-          var
-            namespace = (module.moduleExists(name))
-              ? $.fn[name].settings.namespace || false
-              : true,
-            $existingModules
-          ;
-          if(module.moduleExists(name)) {
-            module.verbose('Changing default setting', setting, value, name);
-            $.fn[name].settings[setting] = value;
-            if(modifyExisting && namespace) {
-              $existingModules = $(':data(module-' + namespace + ')');
-              if($existingModules.length > 0) {
-                module.verbose('Modifying existing settings', $existingModules);
-                $existingModules[name]('setting', setting, value);
-              }
-            }
-          }
-        });
-      },
-      settings: function(newSettings, modules, modifyExisting) {
-        modules = (typeof modules === 'string')
-          ? [modules]
-          : modules || settings.modules
-        ;
-        modifyExisting = (modifyExisting !== undefined)
-          ? modifyExisting
-          : true
-        ;
-        $.each(modules, function(index, name) {
-          var
-            $existingModules
-          ;
-          if(module.moduleExists(name)) {
-            module.verbose('Changing default setting', newSettings, name);
-            $.extend(true, $.fn[name].settings, newSettings);
-            if(modifyExisting && namespace) {
-              $existingModules = $(':data(module-' + namespace + ')');
-              if($existingModules.length > 0) {
-                module.verbose('Modifying existing settings', $existingModules);
-                $existingModules[name]('setting', newSettings);
-              }
-            }
-          }
-        });
-      }
-    },
-
-    enable: {
-      console: function() {
-        module.console(true);
-      },
-      debug: function(modules, modifyExisting) {
-        modules = modules || settings.modules;
-        module.debug('Enabling debug for modules', modules);
-        module.change.setting('debug', true, modules, modifyExisting);
-      },
-      verbose: function(modules, modifyExisting) {
-        modules = modules || settings.modules;
-        module.debug('Enabling verbose debug for modules', modules);
-        module.change.setting('verbose', true, modules, modifyExisting);
-      }
-    },
-    disable: {
-      console: function() {
-        module.console(false);
-      },
-      debug: function(modules, modifyExisting) {
-        modules = modules || settings.modules;
-        module.debug('Disabling debug for modules', modules);
-        module.change.setting('debug', false, modules, modifyExisting);
-      },
-      verbose: function(modules, modifyExisting) {
-        modules = modules || settings.modules;
-        module.debug('Disabling verbose debug for modules', modules);
-        module.change.setting('verbose', false, modules, modifyExisting);
-      }
-    },
-
-    console: function(enable) {
-      if(enable) {
-        if(instance.cache.console === undefined) {
-          module.error(error.console);
-          return;
-        }
-        module.debug('Restoring console function');
-        window.console = instance.cache.console;
-      }
-      else {
-        module.debug('Disabling console function');
-        instance.cache.console = window.console;
-        window.console = {
-          clear          : function(){},
-          error          : function(){},
-          group          : function(){},
-          groupCollapsed : function(){},
-          groupEnd       : function(){},
-          info           : function(){},
-          log            : function(){},
-          markTimeline   : function(){},
-          warn           : function(){}
-        };
-      }
-    },
-
-    destroy: function() {
-      module.verbose('Destroying previous site for', $module);
-      $module
-        .removeData(moduleNamespace)
-      ;
-    },
-
-    cache: {},
-
-    setting: function(name, value) {
-      if( $.isPlainObject(name) ) {
-        $.extend(true, settings, name);
-      }
-      else if(value !== undefined) {
-        settings[name] = value;
-      }
-      else {
-        return settings[name];
-      }
-    },
-    internal: function(name, value) {
-      if( $.isPlainObject(name) ) {
-        $.extend(true, module, name);
-      }
-      else if(value !== undefined) {
-        module[name] = value;
-      }
-      else {
-        return module[name];
-      }
-    },
-    debug: function() {
-      if(settings.debug) {
-        if(settings.performance) {
-          module.performance.log(arguments);
-        }
-        else {
-          module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
-          module.debug.apply(console, arguments);
-        }
-      }
-    },
-    verbose: function() {
-      if(settings.verbose && settings.debug) {
-        if(settings.performance) {
-          module.performance.log(arguments);
-        }
-        else {
-          module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
-          module.verbose.apply(console, arguments);
-        }
-      }
-    },
-    error: function() {
-      module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
-      module.error.apply(console, arguments);
-    },
-    performance: {
-      log: function(message) {
-        var
-          currentTime,
-          executionTime,
-          previousTime
-        ;
-        if(settings.performance) {
-          currentTime   = new Date().getTime();
-          previousTime  = time || currentTime;
-          executionTime = currentTime - previousTime;
-          time          = currentTime;
-          performance.push({
-            'Element'        : element,
-            'Name'           : message[0],
-            'Arguments'      : [].slice.call(message, 1) || '',
-            'Execution Time' : executionTime
-          });
-        }
-        clearTimeout(module.performance.timer);
-        module.performance.timer = setTimeout(module.performance.display, 500);
-      },
-      display: function() {
-        var
-          title = settings.name + ':',
-          totalTime = 0
-        ;
-        time = false;
-        clearTimeout(module.performance.timer);
-        $.each(performance, function(index, data) {
-          totalTime += data['Execution Time'];
-        });
-        title += ' ' + totalTime + 'ms';
-        if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
-          console.groupCollapsed(title);
-          if(console.table) {
-            console.table(performance);
-          }
-          else {
-            $.each(performance, function(index, data) {
-              console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
-            });
-          }
-          console.groupEnd();
-        }
-        performance = [];
-      }
-    },
-    invoke: function(query, passedArguments, context) {
-      var
-        object = instance,
-        maxDepth,
-        found,
-        response
-      ;
-      passedArguments = passedArguments || queryArguments;
-      context         = element         || context;
-      if(typeof query == 'string' && object !== undefined) {
-        query    = query.split(/[\. ]/);
-        maxDepth = query.length - 1;
-        $.each(query, function(depth, value) {
-          var camelCaseValue = (depth != maxDepth)
-            ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
-            : query
-          ;
-          if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
-            object = object[camelCaseValue];
-          }
-          else if( object[camelCaseValue] !== undefined ) {
-            found = object[camelCaseValue];
-            return false;
-          }
-          else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
-            object = object[value];
-          }
-          else if( object[value] !== undefined ) {
-            found = object[value];
-            return false;
-          }
-          else {
-            module.error(error.method, query);
-            return false;
-          }
-        });
-      }
-      if ( $.isFunction( found ) ) {
-        response = found.apply(context, passedArguments);
-      }
-      else if(found !== undefined) {
-        response = found;
-      }
-      if(Array.isArray(returnedValue)) {
-        returnedValue.push(response);
-      }
-      else if(returnedValue !== undefined) {
-        returnedValue = [returnedValue, response];
-      }
-      else if(response !== undefined) {
-        returnedValue = response;
-      }
-      return found;
-    }
-  };
-
-  if(methodInvoked) {
-    if(instance === undefined) {
-      module.initialize();
-    }
-    module.invoke(query);
-  }
-  else {
-    if(instance !== undefined) {
-      module.destroy();
-    }
-    module.initialize();
-  }
-  return (returnedValue !== undefined)
-    ? returnedValue
-    : this
-  ;
-};
-
-$.site.settings = {
-
-  name        : 'Site',
-  namespace   : 'site',
-
-  error : {
-    console : 'Console cannot be restored, most likely it was overwritten outside of module',
-    method : 'The method you called is not defined.'
-  },
-
-  debug       : false,
-  verbose     : false,
-  performance : true,
-
-  modules: [
-    'accordion',
-    'api',
-    'calendar',
-    'checkbox',
-    'dimmer',
-    'dropdown',
-    'embed',
-    'form',
-    'modal',
-    'nag',
-    'popup',
-    'slider',
-    'rating',
-    'shape',
-    'sidebar',
-    'state',
-    'sticky',
-    'tab',
-    'toast',
-    'transition',
-    'visibility',
-    'visit'
-  ],
-
-  siteNamespace   : 'site',
-  namespaceStub   : {
-    cache     : {},
-    config    : {},
-    sections  : {},
-    section   : {},
-    utilities : {}
-  }
-
-};
-
-// allows for selection of elements with data attributes
-$.extend($.expr[ ":" ], {
-  data: ($.expr.createPseudo)
-    ? $.expr.createPseudo(function(dataName) {
-        return function(elem) {
-          return !!$.data(elem, dataName);
-        };
-      })
-    : function(elem, i, match) {
-      // support: jQuery < 1.8
-      return !!$.data(elem, match[ 3 ]);
-    }
-});
-
-
 })( jQuery, window, document );
 
 /*!
diff --git a/web_src/fomantic/semantic.json b/web_src/fomantic/semantic.json
index bd2ba15c62..367bdf3642 100644
--- a/web_src/fomantic/semantic.json
+++ b/web_src/fomantic/semantic.json
@@ -36,7 +36,6 @@
     "modal",
     "search",
     "segment",
-    "site",
     "tab",
     "table"
   ]

From c97a7b8347e62b580cf6e72181a4f7e5cd17ac30 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Fri, 22 Mar 2024 13:28:38 +0100
Subject: [PATCH 47/74] Small refactors in anchors.js (#29947)

Some minor refactors, remove unnecessary `:is` selector and move the
`:target` check out of the function. Might as well backport for the rare
browser that does not support `:is`.

(cherry picked from commit 29118743a58cf3172bddb6a4fa287484c62b4eb1)
---
 web_src/js/markup/anchors.js | 40 +++++++++++++++++++-----------------
 1 file changed, 21 insertions(+), 19 deletions(-)

diff --git a/web_src/js/markup/anchors.js b/web_src/js/markup/anchors.js
index dac877fd99..0e2c92713a 100644
--- a/web_src/js/markup/anchors.js
+++ b/web_src/js/markup/anchors.js
@@ -1,29 +1,30 @@
 import {svg} from '../svg.js';
 
+const addPrefix = (str) => `user-content-${str}`;
+const removePrefix = (str) => str.replace(/^user-content-/, '');
+const hasPrefix = (str) => str.startsWith('user-content-');
+
 // scroll to anchor while respecting the `user-content` prefix that exists on the target
-function scrollToAnchor(encodedId, initial) {
-  // abort if the browser has already scrolled to another anchor during page load
-  if (!encodedId || (initial && document.querySelector(':target'))) return;
+function scrollToAnchor(encodedId) {
+  if (!encodedId) return;
   const id = decodeURIComponent(encodedId);
-  let el = document.getElementById(`user-content-${id}`);
+  const prefixedId = addPrefix(id);
+  let el = document.getElementById(prefixedId);
 
   // check for matching user-generated `a[name]`
   if (!el) {
-    const nameAnchors = document.getElementsByName(`user-content-${id}`);
+    const nameAnchors = document.getElementsByName(prefixedId);
     if (nameAnchors.length) {
       el = nameAnchors[0];
     }
   }
 
   // compat for links with old 'user-content-' prefixed hashes
-  if (!el && id.startsWith('user-content-')) {
-    const el = document.getElementById(id);
-    if (el) el.scrollIntoView();
+  if (!el && hasPrefix(id)) {
+    return document.getElementById(id)?.scrollIntoView();
   }
 
-  if (el) {
-    el.scrollIntoView();
-  }
+  el?.scrollIntoView();
 }
 
 export function initMarkupAnchors() {
@@ -32,11 +33,10 @@ export function initMarkupAnchors() {
 
   for (const markupEl of markupEls) {
     // create link icons for markup headings, the resulting link href will remove `user-content-`
-    for (const heading of markupEl.querySelectorAll(`:is(h1, h2, h3, h4, h5, h6`)) {
-      const originalId = heading.id.replace(/^user-content-/, '');
+    for (const heading of markupEl.querySelectorAll('h1, h2, h3, h4, h5, h6')) {
       const a = document.createElement('a');
       a.classList.add('anchor');
-      a.setAttribute('href', `#${encodeURIComponent(originalId)}`);
+      a.setAttribute('href', `#${encodeURIComponent(removePrefix(heading.id))}`);
       a.innerHTML = svg('octicon-link');
       heading.prepend(a);
     }
@@ -45,8 +45,7 @@ export function initMarkupAnchors() {
     for (const a of markupEl.querySelectorAll('a[href^="#"]')) {
       const href = a.getAttribute('href');
       if (!href.startsWith('#user-content-')) continue;
-      const originalId = href.replace(/^#user-content-/, '');
-      a.setAttribute('href', `#${originalId}`);
+      a.setAttribute('href', `#${removePrefix(href.substring(1))}`);
     }
 
     // add `user-content-` prefix to user-generated `a[name]` link targets
@@ -54,15 +53,18 @@ export function initMarkupAnchors() {
     for (const a of markupEl.querySelectorAll('a[name]')) {
       const name = a.getAttribute('name');
       if (!name) continue;
-      a.setAttribute('name', `user-content-${a.name}`);
+      a.setAttribute('name', addPrefix(a.name));
     }
 
     for (const a of markupEl.querySelectorAll('a[href^="#"]')) {
       a.addEventListener('click', (e) => {
-        scrollToAnchor(e.currentTarget.getAttribute('href')?.substring(1), false);
+        scrollToAnchor(e.currentTarget.getAttribute('href')?.substring(1));
       });
     }
   }
 
-  scrollToAnchor(window.location.hash.substring(1), true);
+  // scroll to anchor unless the browser has already scrolled somewhere during page load
+  if (!document.querySelector(':target')) {
+    scrollToAnchor(window.location.hash?.substring(1));
+  }
 }

From 196c8772a80e81c210be6fad09698f2ced4363d4 Mon Sep 17 00:00:00 2001
From: Lunny Xiao <xiaolunwen@gmail.com>
Date: Fri, 22 Mar 2024 20:53:52 +0800
Subject: [PATCH 48/74] Use db.ListOptionsAll instead of
 db.ListOptions{ListAll: true} (#29995)

(cherry picked from commit f8ab9dafb7a173a35e9308f8f784735b0f822439)

Conflicts:
	routers/web/repo/fork.go
	trivial context conflict, the file does not exist in Forgejo
---
 .../indexer/issues/internal/tests/tests.go    | 48 +++++++------------
 modules/indexer/issues/util.go                |  4 +-
 routers/web/admin/users.go                    |  8 +---
 routers/web/repo/commit.go                    |  2 +-
 routers/web/repo/compare.go                   | 12 ++---
 routers/web/repo/pull.go                      |  6 +--
 routers/web/repo/release.go                   |  2 +-
 routers/web/repo/repo.go                      |  8 +---
 routers/web/repo/view.go                      |  2 +-
 services/actions/commit_status.go             |  2 +-
 services/pull/commit_status.go                |  2 +-
 services/pull/pull.go                         |  2 +-
 services/pull/review.go                       | 14 ++----
 services/repository/adopt.go                  |  6 +--
 14 files changed, 41 insertions(+), 77 deletions(-)

diff --git a/modules/indexer/issues/internal/tests/tests.go b/modules/indexer/issues/internal/tests/tests.go
index 91aafd589c..2209377c2f 100644
--- a/modules/indexer/issues/internal/tests/tests.go
+++ b/modules/indexer/issues/internal/tests/tests.go
@@ -515,10 +515,8 @@ var cases = []*testIndexerCase{
 	{
 		Name: "SortByCreatedDesc",
 		SearchOptions: &internal.SearchOptions{
-			Paginator: &db.ListOptions{
-				ListAll: true,
-			},
-			SortBy: internal.SortByCreatedDesc,
+			Paginator: &db.ListOptionsAll,
+			SortBy:    internal.SortByCreatedDesc,
 		},
 		Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) {
 			assert.Equal(t, len(data), len(result.Hits))
@@ -533,10 +531,8 @@ var cases = []*testIndexerCase{
 	{
 		Name: "SortByUpdatedDesc",
 		SearchOptions: &internal.SearchOptions{
-			Paginator: &db.ListOptions{
-				ListAll: true,
-			},
-			SortBy: internal.SortByUpdatedDesc,
+			Paginator: &db.ListOptionsAll,
+			SortBy:    internal.SortByUpdatedDesc,
 		},
 		Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) {
 			assert.Equal(t, len(data), len(result.Hits))
@@ -551,10 +547,8 @@ var cases = []*testIndexerCase{
 	{
 		Name: "SortByCommentsDesc",
 		SearchOptions: &internal.SearchOptions{
-			Paginator: &db.ListOptions{
-				ListAll: true,
-			},
-			SortBy: internal.SortByCommentsDesc,
+			Paginator: &db.ListOptionsAll,
+			SortBy:    internal.SortByCommentsDesc,
 		},
 		Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) {
 			assert.Equal(t, len(data), len(result.Hits))
@@ -569,10 +563,8 @@ var cases = []*testIndexerCase{
 	{
 		Name: "SortByDeadlineDesc",
 		SearchOptions: &internal.SearchOptions{
-			Paginator: &db.ListOptions{
-				ListAll: true,
-			},
-			SortBy: internal.SortByDeadlineDesc,
+			Paginator: &db.ListOptionsAll,
+			SortBy:    internal.SortByDeadlineDesc,
 		},
 		Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) {
 			assert.Equal(t, len(data), len(result.Hits))
@@ -587,10 +579,8 @@ var cases = []*testIndexerCase{
 	{
 		Name: "SortByCreatedAsc",
 		SearchOptions: &internal.SearchOptions{
-			Paginator: &db.ListOptions{
-				ListAll: true,
-			},
-			SortBy: internal.SortByCreatedAsc,
+			Paginator: &db.ListOptionsAll,
+			SortBy:    internal.SortByCreatedAsc,
 		},
 		Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) {
 			assert.Equal(t, len(data), len(result.Hits))
@@ -605,10 +595,8 @@ var cases = []*testIndexerCase{
 	{
 		Name: "SortByUpdatedAsc",
 		SearchOptions: &internal.SearchOptions{
-			Paginator: &db.ListOptions{
-				ListAll: true,
-			},
-			SortBy: internal.SortByUpdatedAsc,
+			Paginator: &db.ListOptionsAll,
+			SortBy:    internal.SortByUpdatedAsc,
 		},
 		Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) {
 			assert.Equal(t, len(data), len(result.Hits))
@@ -623,10 +611,8 @@ var cases = []*testIndexerCase{
 	{
 		Name: "SortByCommentsAsc",
 		SearchOptions: &internal.SearchOptions{
-			Paginator: &db.ListOptions{
-				ListAll: true,
-			},
-			SortBy: internal.SortByCommentsAsc,
+			Paginator: &db.ListOptionsAll,
+			SortBy:    internal.SortByCommentsAsc,
 		},
 		Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) {
 			assert.Equal(t, len(data), len(result.Hits))
@@ -641,10 +627,8 @@ var cases = []*testIndexerCase{
 	{
 		Name: "SortByDeadlineAsc",
 		SearchOptions: &internal.SearchOptions{
-			Paginator: &db.ListOptions{
-				ListAll: true,
-			},
-			SortBy: internal.SortByDeadlineAsc,
+			Paginator: &db.ListOptionsAll,
+			SortBy:    internal.SortByDeadlineAsc,
 		},
 		Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) {
 			assert.Equal(t, len(data), len(result.Hits))
diff --git a/modules/indexer/issues/util.go b/modules/indexer/issues/util.go
index 510b4060b2..9861c808dc 100644
--- a/modules/indexer/issues/util.go
+++ b/modules/indexer/issues/util.go
@@ -61,9 +61,7 @@ func getIssueIndexerData(ctx context.Context, issueID int64) (*internal.IndexerD
 	)
 	{
 		reviews, err := issue_model.FindReviews(ctx, issue_model.FindReviewOptions{
-			ListOptions: db.ListOptions{
-				ListAll: true,
-			},
+			ListOptions:  db.ListOptionsAll,
 			IssueID:      issueID,
 			OfficialOnly: false,
 		})
diff --git a/routers/web/admin/users.go b/routers/web/admin/users.go
index 6dfcfc3d9a..b93668c5a2 100644
--- a/routers/web/admin/users.go
+++ b/routers/web/admin/users.go
@@ -275,9 +275,7 @@ func ViewUser(ctx *context.Context) {
 	}
 
 	repos, count, err := repo_model.SearchRepository(ctx, &repo_model.SearchRepoOptions{
-		ListOptions: db.ListOptions{
-			ListAll: true,
-		},
+		ListOptions: db.ListOptionsAll,
 		OwnerID:     u.ID,
 		OrderBy:     db.SearchOrderByAlphabetically,
 		Private:     true,
@@ -300,9 +298,7 @@ func ViewUser(ctx *context.Context) {
 	ctx.Data["EmailsTotal"] = len(emails)
 
 	orgs, err := db.Find[org_model.Organization](ctx, org_model.FindOrgOptions{
-		ListOptions: db.ListOptions{
-			ListAll: true,
-		},
+		ListOptions:    db.ListOptionsAll,
 		UserID:         u.ID,
 		IncludePrivate: true,
 	})
diff --git a/routers/web/repo/commit.go b/routers/web/repo/commit.go
index 0c585a93b8..718454e063 100644
--- a/routers/web/repo/commit.go
+++ b/routers/web/repo/commit.go
@@ -367,7 +367,7 @@ func Diff(ctx *context.Context) {
 	ctx.Data["Commit"] = commit
 	ctx.Data["Diff"] = diff
 
-	statuses, _, err := git_model.GetLatestCommitStatus(ctx, ctx.Repo.Repository.ID, commitID, db.ListOptions{ListAll: true})
+	statuses, _, err := git_model.GetLatestCommitStatus(ctx, ctx.Repo.Repository.ID, commitID, db.ListOptionsAll)
 	if err != nil {
 		log.Error("GetLatestCommitStatus: %v", err)
 	}
diff --git a/routers/web/repo/compare.go b/routers/web/repo/compare.go
index 7b5243e6b7..cfb0e859bd 100644
--- a/routers/web/repo/compare.go
+++ b/routers/web/repo/compare.go
@@ -697,10 +697,8 @@ func getBranchesAndTagsForRepo(ctx gocontext.Context, repo *repo_model.Repositor
 	defer gitRepo.Close()
 
 	branches, err = git_model.FindBranchNames(ctx, git_model.FindBranchOptions{
-		RepoID: repo.ID,
-		ListOptions: db.ListOptions{
-			ListAll: true,
-		},
+		RepoID:          repo.ID,
+		ListOptions:     db.ListOptionsAll,
 		IsDeletedBranch: optional.Some(false),
 	})
 	if err != nil {
@@ -754,10 +752,8 @@ func CompareDiff(ctx *context.Context) {
 	}
 
 	headBranches, err := git_model.FindBranchNames(ctx, git_model.FindBranchOptions{
-		RepoID: ci.HeadRepo.ID,
-		ListOptions: db.ListOptions{
-			ListAll: true,
-		},
+		RepoID:          ci.HeadRepo.ID,
+		ListOptions:     db.ListOptionsAll,
 		IsDeletedBranch: optional.Some(false),
 	})
 	if err != nil {
diff --git a/routers/web/repo/pull.go b/routers/web/repo/pull.go
index 8727f3d1e3..c6c6142534 100644
--- a/routers/web/repo/pull.go
+++ b/routers/web/repo/pull.go
@@ -505,7 +505,7 @@ func PrepareMergedViewPullInfo(ctx *context.Context, issue *issues_model.Issue)
 
 	if len(compareInfo.Commits) != 0 {
 		sha := compareInfo.Commits[0].ID.String()
-		commitStatuses, _, err := git_model.GetLatestCommitStatus(ctx, ctx.Repo.Repository.ID, sha, db.ListOptions{ListAll: true})
+		commitStatuses, _, err := git_model.GetLatestCommitStatus(ctx, ctx.Repo.Repository.ID, sha, db.ListOptionsAll)
 		if err != nil {
 			ctx.ServerError("GetLatestCommitStatus", err)
 			return nil
@@ -567,7 +567,7 @@ func PrepareViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *git.C
 			ctx.ServerError(fmt.Sprintf("GetRefCommitID(%s)", pull.GetGitRefName()), err)
 			return nil
 		}
-		commitStatuses, _, err := git_model.GetLatestCommitStatus(ctx, repo.ID, sha, db.ListOptions{ListAll: true})
+		commitStatuses, _, err := git_model.GetLatestCommitStatus(ctx, repo.ID, sha, db.ListOptionsAll)
 		if err != nil {
 			ctx.ServerError("GetLatestCommitStatus", err)
 			return nil
@@ -659,7 +659,7 @@ func PrepareViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *git.C
 		return nil
 	}
 
-	commitStatuses, _, err := git_model.GetLatestCommitStatus(ctx, repo.ID, sha, db.ListOptions{ListAll: true})
+	commitStatuses, _, err := git_model.GetLatestCommitStatus(ctx, repo.ID, sha, db.ListOptionsAll)
 	if err != nil {
 		ctx.ServerError("GetLatestCommitStatus", err)
 		return nil
diff --git a/routers/web/repo/release.go b/routers/web/repo/release.go
index 95447a8f54..38bb1305fb 100644
--- a/routers/web/repo/release.go
+++ b/routers/web/repo/release.go
@@ -136,7 +136,7 @@ func getReleaseInfos(ctx *context.Context, opts *repo_model.FindReleasesOptions)
 		}
 
 		if canReadActions {
-			statuses, _, err := git_model.GetLatestCommitStatus(ctx, r.Repo.ID, r.Sha1, db.ListOptions{ListAll: true})
+			statuses, _, err := git_model.GetLatestCommitStatus(ctx, r.Repo.ID, r.Sha1, db.ListOptionsAll)
 			if err != nil {
 				return nil, err
 			}
diff --git a/routers/web/repo/repo.go b/routers/web/repo/repo.go
index bda78b0edc..b2458a561a 100644
--- a/routers/web/repo/repo.go
+++ b/routers/web/repo/repo.go
@@ -684,9 +684,7 @@ func GetBranchesList(ctx *context.Context) {
 	branchOpts := git_model.FindBranchOptions{
 		RepoID:          ctx.Repo.Repository.ID,
 		IsDeletedBranch: optional.Some(false),
-		ListOptions: db.ListOptions{
-			ListAll: true,
-		},
+		ListOptions:     db.ListOptionsAll,
 	}
 	branches, err := git_model.FindBranchNames(ctx, branchOpts)
 	if err != nil {
@@ -719,9 +717,7 @@ func PrepareBranchList(ctx *context.Context) {
 	branchOpts := git_model.FindBranchOptions{
 		RepoID:          ctx.Repo.Repository.ID,
 		IsDeletedBranch: optional.Some(false),
-		ListOptions: db.ListOptions{
-			ListAll: true,
-		},
+		ListOptions:     db.ListOptionsAll,
 	}
 	brs, err := git_model.FindBranchNames(ctx, branchOpts)
 	if err != nil {
diff --git a/routers/web/repo/view.go b/routers/web/repo/view.go
index 8ddfd92aa1..e4d7179f63 100644
--- a/routers/web/repo/view.go
+++ b/routers/web/repo/view.go
@@ -364,7 +364,7 @@ func loadLatestCommitData(ctx *context.Context, latestCommit *git.Commit) bool {
 		ctx.Data["LatestCommitVerification"] = verification
 		ctx.Data["LatestCommitUser"] = user_model.ValidateCommitWithEmail(ctx, latestCommit)
 
-		statuses, _, err := git_model.GetLatestCommitStatus(ctx, ctx.Repo.Repository.ID, latestCommit.ID.String(), db.ListOptions{ListAll: true})
+		statuses, _, err := git_model.GetLatestCommitStatus(ctx, ctx.Repo.Repository.ID, latestCommit.ID.String(), db.ListOptionsAll)
 		if err != nil {
 			log.Error("GetLatestCommitStatus: %v", err)
 		}
diff --git a/services/actions/commit_status.go b/services/actions/commit_status.go
index edd1fd1568..4236553927 100644
--- a/services/actions/commit_status.go
+++ b/services/actions/commit_status.go
@@ -79,7 +79,7 @@ func createCommitStatus(ctx context.Context, job *actions_model.ActionRunJob) er
 	}
 	ctxname := fmt.Sprintf("%s / %s (%s)", runName, job.Name, event)
 	state := toCommitStatus(job.Status)
-	if statuses, _, err := git_model.GetLatestCommitStatus(ctx, repo.ID, sha, db.ListOptions{ListAll: true}); err == nil {
+	if statuses, _, err := git_model.GetLatestCommitStatus(ctx, repo.ID, sha, db.ListOptionsAll); err == nil {
 		for _, v := range statuses {
 			if v.Context == ctxname {
 				if v.State == state {
diff --git a/services/pull/commit_status.go b/services/pull/commit_status.go
index cb63123463..0d4763a838 100644
--- a/services/pull/commit_status.go
+++ b/services/pull/commit_status.go
@@ -153,7 +153,7 @@ func GetPullRequestCommitStatusState(ctx context.Context, pr *issues_model.PullR
 		return "", fmt.Errorf("LoadBaseRepo: %w", err)
 	}
 
-	commitStatuses, _, err := git_model.GetLatestCommitStatus(ctx, pr.BaseRepo.ID, sha, db.ListOptions{ListAll: true})
+	commitStatuses, _, err := git_model.GetLatestCommitStatus(ctx, pr.BaseRepo.ID, sha, db.ListOptionsAll)
 	if err != nil {
 		return "", fmt.Errorf("GetLatestCommitStatus: %w", err)
 	}
diff --git a/services/pull/pull.go b/services/pull/pull.go
index 29a49fb4dc..23a75e2924 100644
--- a/services/pull/pull.go
+++ b/services/pull/pull.go
@@ -893,7 +893,7 @@ func getAllCommitStatus(ctx context.Context, gitRepo *git.Repository, pr *issues
 		return nil, nil, shaErr
 	}
 
-	statuses, _, err = git_model.GetLatestCommitStatus(ctx, pr.BaseRepo.ID, sha, db.ListOptions{ListAll: true})
+	statuses, _, err = git_model.GetLatestCommitStatus(ctx, pr.BaseRepo.ID, sha, db.ListOptionsAll)
 	lastStatus = git_model.CalcCommitStatus(statuses)
 	return statuses, lastStatus, err
 }
diff --git a/services/pull/review.go b/services/pull/review.go
index faac885273..7d698a14f6 100644
--- a/services/pull/review.go
+++ b/services/pull/review.go
@@ -52,9 +52,7 @@ func InvalidateCodeComments(ctx context.Context, prs issues_model.PullRequestLis
 	issueIDs := prs.GetIssueIDs()
 
 	codeComments, err := db.Find[issues_model.Comment](ctx, issues_model.FindCommentsOptions{
-		ListOptions: db.ListOptions{
-			ListAll: true,
-		},
+		ListOptions: db.ListOptionsAll,
 		Type:        issues_model.CommentTypeCode,
 		Invalidated: optional.Some(false),
 		IssueIDs:    issueIDs,
@@ -322,12 +320,10 @@ func SubmitReview(ctx context.Context, doer *user_model.User, gitRepo *git.Repos
 // DismissApprovalReviews dismiss all approval reviews because of new commits
 func DismissApprovalReviews(ctx context.Context, doer *user_model.User, pull *issues_model.PullRequest) error {
 	reviews, err := issues_model.FindReviews(ctx, issues_model.FindReviewOptions{
-		ListOptions: db.ListOptions{
-			ListAll: true,
-		},
-		IssueID:   pull.IssueID,
-		Type:      issues_model.ReviewTypeApprove,
-		Dismissed: optional.Some(false),
+		ListOptions: db.ListOptionsAll,
+		IssueID:     pull.IssueID,
+		Type:        issues_model.ReviewTypeApprove,
+		Dismissed:   optional.Some(false),
 	})
 	if err != nil {
 		return err
diff --git a/services/repository/adopt.go b/services/repository/adopt.go
index 0ac3c774b7..b337eac38a 100644
--- a/services/repository/adopt.go
+++ b/services/repository/adopt.go
@@ -144,10 +144,8 @@ func adoptRepository(ctx context.Context, repoPath string, u *user_model.User, r
 	}
 
 	branches, _ := git_model.FindBranchNames(ctx, git_model.FindBranchOptions{
-		RepoID: repo.ID,
-		ListOptions: db.ListOptions{
-			ListAll: true,
-		},
+		RepoID:          repo.ID,
+		ListOptions:     db.ListOptionsAll,
 		IsDeletedBranch: optional.Some(false),
 	})
 

From 44bc7b995478f6c6e2f5a177ad304d0044776341 Mon Sep 17 00:00:00 2001
From: Lunny Xiao <xiaolunwen@gmail.com>
Date: Fri, 22 Mar 2024 21:17:39 +0800
Subject: [PATCH 49/74] Add border radius for wiki pages (#29937)

Before

<img width="1312" alt="image"
src="https://github.com/go-gitea/gitea/assets/81045/26a6dec2-9fea-4c0c-b6fb-290eab12a55a">

After

<img width="1298" alt="image"
src="https://github.com/go-gitea/gitea/assets/81045/01f7a714-eae9-4729-918f-3b4795094d0b">

(cherry picked from commit 0c55506b407731546c6bacd1442a785db68f55a7)
---
 web_src/css/repo/wiki.css | 1 +
 1 file changed, 1 insertion(+)

diff --git a/web_src/css/repo/wiki.css b/web_src/css/repo/wiki.css
index bb6f364557..ba502d3216 100644
--- a/web_src/css/repo/wiki.css
+++ b/web_src/css/repo/wiki.css
@@ -21,6 +21,7 @@
 
 .repository.wiki .wiki-content-parts .markup {
   border: 1px solid var(--color-secondary);
+  border-radius: var(--border-radius);
   padding: 1em;
   margin-top: 1em;
   font-size: 1em;

From 2befd9a3cf0efeb40948609e19ae7d496181bff9 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Fri, 22 Mar 2024 14:45:10 +0100
Subject: [PATCH 50/74] Replace 10 more gt- classes with tw- (#29945)

Likely the biggest change of the tailwind refactors. Only thing of note
is that `tw-flex-1` resolves to `flex: 1 1 0%` while our `gt-f1` was
`flex: 1 1 0`, I don't think it will make any difference. Commands I've
ran:

```sh
perl -p -i -e 's#gt-vm#tw-align-middle#g' web_src/js/**/* templates/**/* models/**/*
perl -p -i -e 's#gt-fw#tw-flex-wrap#g' web_src/js/**/* templates/**/* models/**/*
perl -p -i -e 's#gt-f1#tw-flex-1#g' web_src/js/**/* templates/**/* models/**/*
perl -p -i -e 's#gt-fc#tw-flex-col#g' web_src/js/**/* templates/**/* models/**/*
perl -p -i -e 's#gt-sb#tw-justify-between#g' web_src/js/**/* templates/**/* models/**/*
perl -p -i -e 's#gt-je#tw-justify-end#g' web_src/js/**/* templates/**/* models/**/*
perl -p -i -e 's#gt-jc#tw-justify-center#g' web_src/js/**/* templates/**/* models/**/*
perl -p -i -e 's#gt-ac#tw-content-center#g' web_src/js/**/* templates/**/* models/**/* tests/**/*
perl -p -i -e 's#gt-df#tw-flex#g' web_src/js/**/* templates/**/* models/**/* tests/**/*
perl -p -i -e 's#gt-dib#tw-inline-block#g' web_src/js/**/* templates/**/* models/**/* tests/**/*

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
(cherry picked from commit f88ad5424f381bf2a45fd863b551c5a72891bb68)

Conflicts:
	templates/repo/diff/conversation.tmpl
	templates/repo/header.tmpl
	templates/repo/issue/filter_actions.tmpl
	templates/repo/issue/filter_list.tmpl
	templates/repo/issue/view_content/conversation.tmpl
	templates/repo/release/list.tmpl
	templates/repo/wiki/view.tmpl
	web_src/js/components/DashboardRepoList.vue
	discard the proposed changes and prefer Forgejo. A followup
	commit will apply the same commands.
---
 .../contributing/guidelines-frontend.en-us.md |  2 +-
 .../contributing/guidelines-frontend.zh-cn.md |  2 +-
 models/avatars/avatar.go                      |  2 +-
 templates/admin/emails/list.tmpl              |  4 +-
 templates/admin/notice.tmpl                   |  2 +-
 templates/admin/org/list.tmpl                 |  4 +-
 templates/admin/queue_manage.tmpl             |  2 +-
 templates/admin/repo/unadopted.tmpl           |  4 +-
 templates/admin/stacktrace-row.tmpl           |  8 ++--
 templates/admin/stacktrace.tmpl               |  4 +-
 templates/admin/user/list.tmpl                |  2 +-
 templates/admin/user/view.tmpl                |  4 +-
 templates/base/head_navbar.tmpl               |  8 ++--
 templates/devtest/fomantic-modal.tmpl         |  2 +-
 templates/devtest/tmplerr.tmpl                |  2 +-
 templates/explore/search.tmpl                 |  4 +-
 templates/explore/user_list.tmpl              |  2 +-
 templates/org/header.tmpl                     |  6 +--
 templates/org/home.tmpl                       | 12 ++---
 templates/org/member/members.tmpl             |  2 +-
 templates/org/settings/labels.tmpl            |  4 +-
 templates/org/team/members.tmpl               |  6 +--
 templates/org/team/new.tmpl                   |  6 +--
 templates/org/team/repositories.tmpl          |  8 ++--
 templates/package/view.tmpl                   |  4 +-
 templates/projects/list.tmpl                  |  2 +-
 templates/projects/view.tmpl                  |  2 +-
 templates/repo/actions/list.tmpl              |  2 +-
 templates/repo/actions/runs_list.tmpl         |  2 +-
 templates/repo/actions/status.tmpl            |  2 +-
 templates/repo/blame.tmpl                     |  6 +--
 templates/repo/branch/list.tmpl               | 10 ++---
 templates/repo/branch_dropdown.tmpl           |  4 +-
 .../code/recently_pushed_new_branches.tmpl    |  4 +-
 .../repo/commit_load_branches_and_tags.tmpl   |  8 ++--
 templates/repo/commit_page.tmpl               | 16 +++----
 templates/repo/commit_statuses.tmpl           |  4 +-
 templates/repo/commits.tmpl                   |  2 +-
 templates/repo/commits_list_small.tmpl        |  2 +-
 templates/repo/commits_table.tmpl             |  4 +-
 templates/repo/diff/blob_excerpt.tmpl         |  4 +-
 templates/repo/diff/box.tmpl                  | 16 +++----
 templates/repo/diff/comments.tmpl             |  6 +--
 templates/repo/diff/new_review.tmpl           | 10 ++---
 templates/repo/diff/section_split.tmpl        |  2 +-
 templates/repo/diff/section_unified.tmpl      |  2 +-
 templates/repo/find/files.tmpl                |  4 +-
 templates/repo/forks.tmpl                     |  2 +-
 templates/repo/graph/commits.tmpl             |  8 ++--
 templates/repo/home.tmpl                      | 14 +++---
 templates/repo/icon.tmpl                      |  2 +-
 templates/repo/issue/card.tmpl                | 16 +++----
 templates/repo/issue/label_precolors.tmpl     |  4 +-
 templates/repo/issue/labels/label_list.tmpl   |  4 +-
 templates/repo/issue/milestone_issues.tmpl    | 10 ++---
 templates/repo/issue/milestones.tmpl          |  2 +-
 templates/repo/issue/new_form.tmpl            |  2 +-
 templates/repo/issue/view_content.tmpl        |  6 +--
 .../repo/issue/view_content/attachments.tmpl  |  6 +--
 .../repo/issue/view_content/comments.tmpl     | 14 +++---
 templates/repo/issue/view_content/pull.tmpl   |  6 +--
 .../repo/issue/view_content/sidebar.tmpl      | 44 +++++++++----------
 .../view_content/update_branch_by_merge.tmpl  |  2 +-
 templates/repo/issue/view_title.tmpl          |  2 +-
 templates/repo/migrate/migrate.tmpl           |  2 +-
 templates/repo/projects/view.tmpl             |  2 +-
 templates/repo/pulls/fork.tmpl                |  2 +-
 templates/repo/pulls/tab_menu.tmpl            |  2 +-
 templates/repo/release/new.tmpl               |  6 +--
 templates/repo/release_tag_header.tmpl        |  4 +-
 templates/repo/settings/branches.tmpl         |  6 +--
 templates/repo/settings/collaboration.tmpl    |  6 +--
 templates/repo/settings/githooks.tmpl         |  2 +-
 templates/repo/settings/options.tmpl          |  8 ++--
 templates/repo/settings/tags.tmpl             |  2 +-
 .../repo/settings/webhook/base_list.tmpl      |  2 +-
 templates/repo/settings/webhook/history.tmpl  |  2 +-
 templates/repo/tag/list.tmpl                  |  8 ++--
 templates/repo/view_file.tmpl                 |  6 +--
 templates/repo/wiki/new.tmpl                  |  2 +-
 templates/repo/wiki/pages.tmpl                |  2 +-
 templates/shared/actions/runner_edit.tmpl     |  8 ++--
 templates/shared/search/code/results.tmpl     |  8 ++--
 templates/shared/searchbottom.tmpl            |  4 +-
 templates/shared/secrets/add_list.tmpl        |  2 +-
 templates/shared/user/org_profile_avatar.tmpl |  2 +-
 templates/shared/user/profile_big_avatar.tmpl |  6 +--
 templates/shared/variables/variable_list.tmpl |  2 +-
 templates/status/500.tmpl                     |  4 +-
 templates/user/auth/signin_inner.tmpl         |  6 +--
 templates/user/auth/signup_inner.tmpl         |  6 +--
 templates/user/auth/webauthn.tmpl             |  2 +-
 templates/user/dashboard/feeds.tmpl           |  2 +-
 templates/user/dashboard/milestones.tmpl      |  2 +-
 .../user/notification/notification_div.tmpl   | 12 ++---
 .../notification_subscriptions.tmpl           |  6 +--
 templates/user/settings/account.tmpl          |  2 +-
 .../settings/applications_oauth2_list.tmpl    |  2 +-
 templates/user/settings/repos.tmpl            |  4 +-
 templates/user/settings/security/openid.tmpl  |  2 +-
 templates/webhook/new.tmpl                    |  2 +-
 tests/integration/release_test.go             |  2 +-
 web_src/css/actions.css                       |  4 --
 web_src/css/helpers.css                       | 10 -----
 web_src/js/components/ActionRunStatus.vue     |  2 +-
 web_src/js/components/DiffCommitSelector.vue  |  2 +-
 web_src/js/components/DiffFileList.vue        |  4 +-
 web_src/js/components/DiffFileTreeItem.vue    |  2 +-
 .../js/components/PullRequestMergeForm.vue    |  2 +-
 .../js/components/RepoBranchTagSelector.vue   |  4 +-
 web_src/js/components/RepoCodeFrequency.vue   |  4 +-
 web_src/js/components/RepoContributors.vue    | 10 ++---
 web_src/js/components/RepoRecentCommits.vue   |  4 +-
 web_src/js/features/repo-issue-content.js     |  2 +-
 web_src/js/features/repo-issue-list.js        |  2 +-
 115 files changed, 276 insertions(+), 290 deletions(-)

diff --git a/docs/content/contributing/guidelines-frontend.en-us.md b/docs/content/contributing/guidelines-frontend.en-us.md
index 2637780718..b19101dd47 100644
--- a/docs/content/contributing/guidelines-frontend.en-us.md
+++ b/docs/content/contributing/guidelines-frontend.en-us.md
@@ -47,7 +47,7 @@ We recommend [Google HTML/CSS Style Guide](https://google.github.io/styleguide/h
 9. Avoid unnecessary `!important` in CSS, add comments to explain why it's necessary if it can't be avoided.
 10. Avoid mixing different events in one event listener, prefer to use individual event listeners for every event.
 11. Custom event names are recommended to use `ce-` prefix.
-12. Prefer using Tailwind CSS which is available via `tw-` prefix, e.g. `tw-relative`. Gitea's helper CSS classes use `gt-` prefix (`gt-df`), while Gitea's own private framework-level CSS classes use `g-` prefix (`g-modal-confirm`).
+12. Prefer using Tailwind CSS which is available via `tw-` prefix, e.g. `tw-relative`. Gitea's helper CSS classes use `gt-` prefix (`gt-mono`), while Gitea's own private framework-level CSS classes use `g-` prefix (`g-modal-confirm`).
 13. Avoid inline scripts & styles as much as possible, it's recommended to put JS code into JS files and use CSS classes. If inline scripts & styles are unavoidable, explain the reason why it can't be avoided.
 
 ### Accessibility / ARIA
diff --git a/docs/content/contributing/guidelines-frontend.zh-cn.md b/docs/content/contributing/guidelines-frontend.zh-cn.md
index ace0d97f49..961a331ac2 100644
--- a/docs/content/contributing/guidelines-frontend.zh-cn.md
+++ b/docs/content/contributing/guidelines-frontend.zh-cn.md
@@ -47,7 +47,7 @@ HTML 页面由[Go HTML Template](https://pkg.go.dev/html/template)渲染。
 9. 避免在 CSS 中使用不必要的`!important`,如果无法避免,添加注释解释为什么需要它。
 10. 避免在一个事件监听器中混合不同的事件,优先为每个事件使用独立的事件监听器。
 11. 推荐使用自定义事件名称前缀`ce-`。
-12. 建议使用 Tailwind CSS,它可以通过 `tw-` 前缀获得,例如 `tw-relative`. Gitea 自身的助手类 CSS 使用 `gt-` 前缀(`gt-df`),Gitea 自身的私有框架级 CSS 类使用 `g-` 前缀(`g-modal-confirm`)。
+12. 建议使用 Tailwind CSS,它可以通过 `tw-` 前缀获得,例如 `tw-relative`. Gitea 自身的助手类 CSS 使用 `gt-` 前缀(`gt-mono`),Gitea 自身的私有框架级 CSS 类使用 `g-` 前缀(`g-modal-confirm`)。
 13. 尽量避免内联脚本和样式,建议将JS代码放入JS文件中并使用CSS类。如果内联脚本和样式不可避免,请解释无法避免的原因。
 
 ### 可访问性 / ARIA
diff --git a/models/avatars/avatar.go b/models/avatars/avatar.go
index bbe16483bf..9c56e0f9a0 100644
--- a/models/avatars/avatar.go
+++ b/models/avatars/avatar.go
@@ -24,7 +24,7 @@ import (
 
 const (
 	// DefaultAvatarClass is the default class of a rendered avatar
-	DefaultAvatarClass = "ui avatar gt-vm"
+	DefaultAvatarClass = "ui avatar tw-align-middle"
 	// DefaultAvatarPixelSize is the default size in pixels of a rendered avatar
 	DefaultAvatarPixelSize = 28
 )
diff --git a/templates/admin/emails/list.tmpl b/templates/admin/emails/list.tmpl
index 660df55999..0b5249fbd9 100644
--- a/templates/admin/emails/list.tmpl
+++ b/templates/admin/emails/list.tmpl
@@ -4,8 +4,8 @@
 			{{ctx.Locale.Tr "admin.emails.email_manage_panel"}} ({{ctx.Locale.Tr "admin.total" .Total}})
 		</h4>
 		<div class="ui attached segment">
-			<div class="ui secondary filter menu gt-ac gt-mx-0">
-				<form class="ui form ignore-dirty gt-f1">
+			<div class="ui secondary filter menu tw-content-center gt-mx-0">
+				<form class="ui form ignore-dirty tw-flex-1">
 					{{template "shared/search/combo" dict "Value" .Keyword}}
 				</form>
 				<!-- Sort -->
diff --git a/templates/admin/notice.tmpl b/templates/admin/notice.tmpl
index f7d77eab1d..c788ebc602 100644
--- a/templates/admin/notice.tmpl
+++ b/templates/admin/notice.tmpl
@@ -17,7 +17,7 @@
 			<tbody>
 				{{range .Notices}}
 					<tr>
-						<td><div class="ui checkbox gt-df" data-id="{{.ID}}"><input type="checkbox"></div></td>
+						<td><div class="ui checkbox tw-flex" data-id="{{.ID}}"><input type="checkbox"></div></td>
 						<td>{{.ID}}</td>
 						<td>{{ctx.Locale.Tr .TrStr}}</td>
 						<td class="view-detail auto-ellipsis" style="width: 80%;"><span class="notice-description">{{.Description}}</span></td>
diff --git a/templates/admin/org/list.tmpl b/templates/admin/org/list.tmpl
index 4609d1b8b4..abd43d297e 100644
--- a/templates/admin/org/list.tmpl
+++ b/templates/admin/org/list.tmpl
@@ -7,8 +7,8 @@
 			</div>
 		</h4>
 		<div class="ui attached segment">
-			<div class="ui secondary filter menu gt-ac gt-mx-0">
-				<form class="ui form ignore-dirty gt-f1">
+			<div class="ui secondary filter menu tw-content-center gt-mx-0">
+				<form class="ui form ignore-dirty tw-flex-1">
 					{{template "shared/search/combo" dict "Value" .Keyword "Placeholder" (ctx.Locale.Tr "search.org_kind")}}
 				</form>
 				<!-- Sort -->
diff --git a/templates/admin/queue_manage.tmpl b/templates/admin/queue_manage.tmpl
index 80214d1021..dd1682a000 100644
--- a/templates/admin/queue_manage.tmpl
+++ b/templates/admin/queue_manage.tmpl
@@ -30,7 +30,7 @@
 								-
 							{{else}}
 								{{$sum}}
-								<form action="{{$.Link}}/remove-all-items" method="post" class="gt-dib gt-ml-4">
+								<form action="{{$.Link}}/remove-all-items" method="post" class="tw-inline-block gt-ml-4">
 									{{$.CsrfTokenHtml}}
 									<button class="ui tiny basic red button">{{ctx.Locale.Tr "admin.monitor.queue.settings.remove_all_items"}}</button>
 								</form>
diff --git a/templates/admin/repo/unadopted.tmpl b/templates/admin/repo/unadopted.tmpl
index c65cfd9db4..eb8188de14 100644
--- a/templates/admin/repo/unadopted.tmpl
+++ b/templates/admin/repo/unadopted.tmpl
@@ -20,8 +20,8 @@
 				{{if .Dirs}}
 					<div class="ui aligned divided list">
 						{{range $dirI, $dir := .Dirs}}
-							<div class="item gt-df gt-ac">
-								<span class="gt-f1"> {{svg "octicon-file-directory-fill"}} {{$dir}}</span>
+							<div class="item tw-flex tw-content-center">
+								<span class="tw-flex-1"> {{svg "octicon-file-directory-fill"}} {{$dir}}</span>
 								<div>
 									<button class="ui button primary show-modal gt-p-3" data-modal="#adopt-unadopted-modal-{{$dirI}}">{{svg "octicon-plus"}} {{ctx.Locale.Tr "repo.adopt_preexisting_label"}}</button>
 									<div class="ui g-modal-confirm modal" id="adopt-unadopted-modal-{{$dirI}}">
diff --git a/templates/admin/stacktrace-row.tmpl b/templates/admin/stacktrace-row.tmpl
index ffb8bf812f..fdce81eda7 100644
--- a/templates/admin/stacktrace-row.tmpl
+++ b/templates/admin/stacktrace-row.tmpl
@@ -1,5 +1,5 @@
 <div class="item">
-	<div class="gt-df gt-ac">
+	<div class="tw-flex tw-content-center">
 		<div class="icon gt-ml-3 gt-mr-3">
 			{{if eq .Process.Type "request"}}
 				{{svg "octicon-globe" 16}}
@@ -11,7 +11,7 @@
 				{{svg "octicon-code" 16}}
 			{{end}}
 		</div>
-		<div class="content gt-f1">
+		<div class="content tw-flex-1">
 			<div class="header">{{.Process.Description}}</div>
 			<div class="description">{{if ne .Process.Type "none"}}{{TimeSince .Process.Start ctx.Locale}}{{end}}</div>
 		</div>
@@ -40,9 +40,9 @@
 						</summary>
 						<div class="list">
 							{{range .Entry}}
-								<div class="item gt-df gt-ac">
+								<div class="item tw-flex tw-content-center">
 									<span class="icon gt-mr-4">{{svg "octicon-dot-fill" 16}}</span>
-									<div class="content gt-f1">
+									<div class="content tw-flex-1">
 										<div class="header"><code>{{.Function}}</code></div>
 										<div class="description"><code>{{.File}}:{{.Line}}</code></div>
 									</div>
diff --git a/templates/admin/stacktrace.tmpl b/templates/admin/stacktrace.tmpl
index 950aa0ea86..3c13c1e9dd 100644
--- a/templates/admin/stacktrace.tmpl
+++ b/templates/admin/stacktrace.tmpl
@@ -1,8 +1,8 @@
 {{template "admin/layout_head" (dict "ctxData" . "pageClass" "admin monitor")}}
 <div class="admin-setting-content">
 
-	<div class="gt-df gt-ac">
-		<div class="gt-f1">
+	<div class="tw-flex tw-content-center">
+		<div class="tw-flex-1">
 			<div class="ui compact small menu">
 				<a class="{{if eq .ShowGoroutineList "process"}}active {{end}}item" href="?show=process">{{ctx.Locale.Tr "admin.monitor.process"}}</a>
 				<a class="{{if eq .ShowGoroutineList "stacktrace"}}active {{end}}item" href="?show=stacktrace">{{ctx.Locale.Tr "admin.monitor.stacktrace"}}</a>
diff --git a/templates/admin/user/list.tmpl b/templates/admin/user/list.tmpl
index 11c2fa5940..dc557e8598 100644
--- a/templates/admin/user/list.tmpl
+++ b/templates/admin/user/list.tmpl
@@ -103,7 +103,7 @@
 								<td><span>{{ctx.Locale.Tr "admin.users.never_login"}}</span></td>
 							{{end}}
 							<td>
-								<div class="gt-df gt-gap-3">
+								<div class="tw-flex gt-gap-3">
 									<a href="{{$.Link}}/{{.ID}}" data-tooltip-content="{{ctx.Locale.Tr "admin.users.details"}}">{{svg "octicon-person"}}</a>
 									<a href="{{$.Link}}/{{.ID}}/edit" data-tooltip-content="{{ctx.Locale.Tr "edit"}}">{{svg "octicon-pencil"}}</a>
 								</div>
diff --git a/templates/admin/user/view.tmpl b/templates/admin/user/view.tmpl
index fd3017607c..21943a8382 100644
--- a/templates/admin/user/view.tmpl
+++ b/templates/admin/user/view.tmpl
@@ -2,7 +2,7 @@
 
 <div class="admin-setting-content">
 	<div class="admin-responsive-columns">
-		<div class="gt-f1">
+		<div class="tw-flex-1">
 			<h4 class="ui top attached header">
 				{{.Title}}
 				<div class="ui right">
@@ -13,7 +13,7 @@
 				{{template "admin/user/view_details" .}}
 			</div>
 		</div>
-		<div class="gt-f1">
+		<div class="tw-flex-1">
 			<h4 class="ui top attached header">
 				{{ctx.Locale.Tr "admin.emails"}}
 				<div class="ui right">
diff --git a/templates/base/head_navbar.tmpl b/templates/base/head_navbar.tmpl
index e755775985..03ece45fe2 100644
--- a/templates/base/head_navbar.tmpl
+++ b/templates/base/head_navbar.tmpl
@@ -56,7 +56,7 @@
 	<div class="navbar-right ui secondary menu">
 		{{if and .IsSigned .MustChangePassword}}
 			<div class="ui dropdown jump item" data-tooltip-content="{{ctx.Locale.Tr "user_profile_and_more"}}">
-				<span class="text gt-df gt-ac">
+				<span class="text tw-flex tw-content-center">
 					{{ctx.AvatarUtils.Avatar .SignedUser 24 "gt-mr-2"}}
 					<span class="mobile-only gt-ml-3">{{.SignedUser.Name}}</span>
 					<span class="not-mobile">{{svg "octicon-triangle-down"}}</span>
@@ -83,8 +83,8 @@
 				<span class="mobile-only gt-ml-3">{{ctx.Locale.Tr "active_stopwatch"}}</span>
 			</a>
 			<div class="active-stopwatch-popup item tippy-target gt-p-3">
-				<div class="gt-df gt-ac">
-					<a class="stopwatch-link gt-df gt-ac" href="{{.ActiveStopwatch.IssueLink}}">
+				<div class="tw-flex tw-content-center">
+					<a class="stopwatch-link tw-flex tw-content-center" href="{{.ActiveStopwatch.IssueLink}}">
 						{{svg "octicon-issue-opened" 16 "gt-mr-3"}}
 						<span class="stopwatch-issue">{{.ActiveStopwatch.RepoSlug}}#{{.ActiveStopwatch.IssueIndex}}</span>
 						<span class="ui primary label stopwatch-time gt-my-0 gt-mx-4" data-seconds="{{.ActiveStopwatch.Seconds}}">
@@ -142,7 +142,7 @@
 			</div><!-- end dropdown menu create new -->
 
 			<div class="ui dropdown jump item gt-mx-0 gt-pr-3" data-tooltip-content="{{ctx.Locale.Tr "user_profile_and_more"}}">
-				<span class="text gt-df gt-ac">
+				<span class="text tw-flex tw-content-center">
 					{{ctx.AvatarUtils.Avatar .SignedUser 24 "gt-mr-2"}}
 					<span class="mobile-only gt-ml-3">{{.SignedUser.Name}}</span>
 					<span class="not-mobile">{{svg "octicon-triangle-down"}}</span>
diff --git a/templates/devtest/fomantic-modal.tmpl b/templates/devtest/fomantic-modal.tmpl
index 0b4199a197..5cd36721a7 100644
--- a/templates/devtest/fomantic-modal.tmpl
+++ b/templates/devtest/fomantic-modal.tmpl
@@ -73,7 +73,7 @@
 		{{template "base/modal_actions_confirm" (dict "ModalButtonDangerText" "I know and must do  this is dangerous operation")}}
 	</div>
 
-	<div class="modal-buttons flex-text-block gt-fw"></div>
+	<div class="modal-buttons flex-text-block tw-flex-wrap"></div>
 	<script type="module">
 		for (const el of $('.ui.modal')) {
 			const $btn = $('<button>').text(`${el.id}`).on('click', () => {
diff --git a/templates/devtest/tmplerr.tmpl b/templates/devtest/tmplerr.tmpl
index 2fe3f1effd..09cf05fc1f 100644
--- a/templates/devtest/tmplerr.tmpl
+++ b/templates/devtest/tmplerr.tmpl
@@ -1,6 +1,6 @@
 {{template "base/head" .}}
 <div class="page-content devtest">
-	<div class="gt-df">
+	<div class="tw-flex">
 		<div style="width: 80%; ">
 			hello hello hello hello hello hello hello hello hello hello
 		</div>
diff --git a/templates/explore/search.tmpl b/templates/explore/search.tmpl
index c1d114125e..505fc64548 100644
--- a/templates/explore/search.tmpl
+++ b/templates/explore/search.tmpl
@@ -1,5 +1,5 @@
-<div class="ui small secondary filter menu gt-ac gt-mx-0">
-	<form class="ui form ignore-dirty gt-f1">
+<div class="ui small secondary filter menu tw-content-center gt-mx-0">
+	<form class="ui form ignore-dirty tw-flex-1">
 		{{if .PageIsExploreUsers}}
 			{{template "shared/search/combo" dict "Value" .Keyword "Placeholder" (ctx.Locale.Tr "search.user_kind")}}
 		{{else}}
diff --git a/templates/explore/user_list.tmpl b/templates/explore/user_list.tmpl
index fb86fbbea2..e49ca1d069 100644
--- a/templates/explore/user_list.tmpl
+++ b/templates/explore/user_list.tmpl
@@ -1,6 +1,6 @@
 <div class="flex-list">
 	{{range .Users}}
-		<div class="flex-item gt-ac">
+		<div class="flex-item tw-content-center">
 			<div class="flex-item-leading">
 				{{ctx.AvatarUtils.Avatar . 48}}
 			</div>
diff --git a/templates/org/header.tmpl b/templates/org/header.tmpl
index 1a55101c2e..c8a0ad3ab0 100644
--- a/templates/org/header.tmpl
+++ b/templates/org/header.tmpl
@@ -1,13 +1,13 @@
-<div class="ui container gt-df">
+<div class="ui container tw-flex">
 	{{ctx.AvatarUtils.Avatar .Org 100 "org-avatar"}}
-	<div id="org-info" class="gt-df gt-fc">
+	<div id="org-info" class="tw-flex tw-flex-col">
 		<div class="ui header">
 			{{.Org.DisplayName}}
 			<span class="org-visibility">
 				{{if .Org.Visibility.IsLimited}}<span class="ui large basic horizontal label">{{ctx.Locale.Tr "org.settings.visibility.limited_shortname"}}</span>{{end}}
 				{{if .Org.Visibility.IsPrivate}}<span class="ui large basic horizontal label">{{ctx.Locale.Tr "org.settings.visibility.private_shortname"}}</span>{{end}}
 			</span>
-			<span class="gt-df gt-ac gt-gap-2 tw-ml-auto gt-font-16 tw-whitespace-nowrap">
+			<span class="tw-flex tw-content-center gt-gap-2 tw-ml-auto gt-font-16 tw-whitespace-nowrap">
 				{{if .EnableFeed}}
 					<a class="ui basic label button gt-mr-0" href="{{.Org.HomeLink}}.rss" data-tooltip-content="{{ctx.Locale.Tr "rss_feed"}}">
 						{{svg "octicon-rss" 24}}
diff --git a/templates/org/home.tmpl b/templates/org/home.tmpl
index d0a39c04fc..0ea0a280fd 100644
--- a/templates/org/home.tmpl
+++ b/templates/org/home.tmpl
@@ -30,9 +30,9 @@
 					<div class="divider"></div>
 				{{end}}
 				{{if .NumMembers}}
-					<h4 class="ui top attached header gt-df">
-						<strong class="gt-f1">{{ctx.Locale.Tr "org.members"}}</strong>
-						<a class="text grey gt-df gt-ac" href="{{.OrgLink}}/members"><span>{{.NumMembers}}</span> {{svg "octicon-chevron-right"}}</a>
+					<h4 class="ui top attached header tw-flex">
+						<strong class="tw-flex-1">{{ctx.Locale.Tr "org.members"}}</strong>
+						<a class="text grey tw-flex tw-content-center" href="{{.OrgLink}}/members"><span>{{.NumMembers}}</span> {{svg "octicon-chevron-right"}}</a>
 					</h4>
 					<div class="ui attached segment members">
 						{{$isMember := .IsOrganizationMember}}
@@ -44,9 +44,9 @@
 					</div>
 				{{end}}
 				{{if .IsOrganizationMember}}
-					<div class="ui top attached header gt-df">
-						<strong class="gt-f1">{{ctx.Locale.Tr "org.teams"}}</strong>
-						<a class="text grey gt-df gt-ac" href="{{.OrgLink}}/teams"><span>{{.Org.NumTeams}}</span> {{svg "octicon-chevron-right"}}</a>
+					<div class="ui top attached header tw-flex">
+						<strong class="tw-flex-1">{{ctx.Locale.Tr "org.teams"}}</strong>
+						<a class="text grey tw-flex tw-content-center" href="{{.OrgLink}}/teams"><span>{{.Org.NumTeams}}</span> {{svg "octicon-chevron-right"}}</a>
 					</div>
 					<div class="ui attached table segment teams">
 						{{range .Teams}}
diff --git a/templates/org/member/members.tmpl b/templates/org/member/members.tmpl
index 54f84450eb..cb9e60da29 100644
--- a/templates/org/member/members.tmpl
+++ b/templates/org/member/members.tmpl
@@ -7,7 +7,7 @@
 		<div class="flex-list">
 			{{range .Members}}
 				{{$isPublic := index $.MembersIsPublicMember .ID}}
-				<div class="flex-item {{if $.PublicOnly}}gt-ac{{end}}">
+				<div class="flex-item {{if $.PublicOnly}}tw-content-center{{end}}">
 					<div class="flex-item-leading">
 						<a href="{{.HomeLink}}">{{ctx.AvatarUtils.Avatar . 48}}</a>
 					</div>
diff --git a/templates/org/settings/labels.tmpl b/templates/org/settings/labels.tmpl
index 8eb7b4584e..19a7d5355e 100644
--- a/templates/org/settings/labels.tmpl
+++ b/templates/org/settings/labels.tmpl
@@ -1,7 +1,7 @@
 {{template "org/settings/layout_head" (dict "ctxData" . "pageClass" "organization settings labels")}}
 				<div class="org-setting-content">
-					<div class="gt-df gt-ac">
-						<div class="gt-f1">
+					<div class="tw-flex tw-content-center">
+						<div class="tw-flex-1">
 							{{ctx.Locale.Tr "org.settings.labels_desc"}}
 						</div>
 						<button class="ui small primary new-label button">{{ctx.Locale.Tr "repo.issues.new_label"}}</button>
diff --git a/templates/org/team/members.tmpl b/templates/org/team/members.tmpl
index 02220a917a..d86aeb7ce4 100644
--- a/templates/org/team/members.tmpl
+++ b/templates/org/team/members.tmpl
@@ -9,7 +9,7 @@
 				{{template "org/team/navbar" .}}
 				{{if .IsOrganizationOwner}}
 					<div class="ui attached segment">
-						<form class="ui form ignore-dirty gt-df gt-fw gt-gap-3" action="{{$.OrgLink}}/teams/{{$.Team.LowerName | PathEscape}}/action/add" method="post">
+						<form class="ui form ignore-dirty tw-flex tw-flex-wrap gt-gap-3" action="{{$.OrgLink}}/teams/{{$.Team.LowerName | PathEscape}}/action/add" method="post">
 							{{.CsrfTokenHtml}}
 							<input type="hidden" name="uid" value="{{.SignedUser.ID}}">
 							<div id="search-user-box" class="ui search gt-mr-3"{{if .IsEmailInviteEnabled}} data-allow-email="true" data-allow-email-description="{{ctx.Locale.Tr "org.teams.invite_team_member" $.Team.Name}}"{{end}}>
@@ -24,7 +24,7 @@
 				<div class="ui attached segment">
 					<div class="flex-list">
 						{{range .Team.Members}}
-							<div class="flex-item gt-ac">
+							<div class="flex-item tw-content-center">
 								<div class="flex-item-leading">
 									<a href="{{.HomeLink}}">{{ctx.AvatarUtils.Avatar . 32}}</a>
 								</div>
@@ -56,7 +56,7 @@
 				<div class="ui attached segment">
 					<div class="flex-list">
 						{{range .Invites}}
-							<div class="flex-item gt-ac">
+							<div class="flex-item tw-content-center">
 								<div class="flex-item-main">
 									{{.Email}}
 								</div>
diff --git a/templates/org/team/new.tmpl b/templates/org/team/new.tmpl
index 50ef53b91b..d1e0dbe382 100644
--- a/templates/org/team/new.tmpl
+++ b/templates/org/team/new.tmpl
@@ -78,11 +78,11 @@
 										<tr>
 											<th>{{ctx.Locale.Tr "units.unit"}}</th>
 											<th class="center aligned">{{ctx.Locale.Tr "org.teams.none_access"}}
-											<span class="gt-vm" data-tooltip-content="{{ctx.Locale.Tr "org.teams.none_access_helper"}}">{{svg "octicon-question" 16 "gt-ml-2"}}</span></th>
+											<span class="tw-align-middle" data-tooltip-content="{{ctx.Locale.Tr "org.teams.none_access_helper"}}">{{svg "octicon-question" 16 "gt-ml-2"}}</span></th>
 											<th class="center aligned">{{ctx.Locale.Tr "org.teams.read_access"}}
-											<span class="gt-vm" data-tooltip-content="{{ctx.Locale.Tr "org.teams.read_access_helper"}}">{{svg "octicon-question" 16 "gt-ml-2"}}</span></th>
+											<span class="tw-align-middle" data-tooltip-content="{{ctx.Locale.Tr "org.teams.read_access_helper"}}">{{svg "octicon-question" 16 "gt-ml-2"}}</span></th>
 											<th class="center aligned">{{ctx.Locale.Tr "org.teams.write_access"}}
-											<span class="gt-vm" data-tooltip-content="{{ctx.Locale.Tr "org.teams.write_access_helper"}}">{{svg "octicon-question" 16 "gt-ml-2"}}</span></th>
+											<span class="tw-align-middle" data-tooltip-content="{{ctx.Locale.Tr "org.teams.write_access_helper"}}">{{svg "octicon-question" 16 "gt-ml-2"}}</span></th>
 										</tr>
 									</thead>
 									<tbody>
diff --git a/templates/org/team/repositories.tmpl b/templates/org/team/repositories.tmpl
index bd38cda6d1..9efe8f9f09 100644
--- a/templates/org/team/repositories.tmpl
+++ b/templates/org/team/repositories.tmpl
@@ -9,8 +9,8 @@
 				{{template "org/team/navbar" .}}
 				{{$canAddRemove := and $.IsOrganizationOwner (not $.Team.IncludesAllRepositories)}}
 				{{if $canAddRemove}}
-					<div class="ui attached segment gt-df gt-fw gt-gap-3">
-						<form class="ui form ignore-dirty gt-f1 gt-df" action="{{$.OrgLink}}/teams/{{$.Team.LowerName | PathEscape}}/action/repo/add" method="post">
+					<div class="ui attached segment tw-flex tw-flex-wrap gt-gap-3">
+						<form class="ui form ignore-dirty tw-flex-1 tw-flex" action="{{$.OrgLink}}/teams/{{$.Team.LowerName | PathEscape}}/action/repo/add" method="post">
 							{{.CsrfTokenHtml}}
 							<div id="search-repo-box" data-uid="{{.Org.ID}}" class="ui search">
 								<div class="ui input">
@@ -19,7 +19,7 @@
 							</div>
 							<button class="ui primary button gt-ml-3">{{ctx.Locale.Tr "add"}}</button>
 						</form>
-						<div class="gt-dib">
+						<div class="tw-inline-block">
 							<button class="ui primary button link-action" data-modal-confirm="{{ctx.Locale.Tr "org.teams.add_all_repos_desc"}}" data-url="{{$.OrgLink}}/teams/{{$.Team.LowerName | PathEscape}}/action/repo/addall">{{ctx.Locale.Tr "add_all"}}</button>
 							<button class="ui red button link-action" data-modal-confirm="{{ctx.Locale.Tr "org.teams.remove_all_repos_desc"}}" data-url="{{$.OrgLink}}/teams/{{$.Team.LowerName | PathEscape}}/action/repo/removeall">{{ctx.Locale.Tr "remove_all"}}</button>
 						</div>
@@ -28,7 +28,7 @@
 				<div class="ui attached segment">
 					<div class="flex-list">
 						{{range .Team.Repos}}
-							<div class="flex-item gt-ac">
+							<div class="flex-item tw-content-center">
 								<div class="flex-item-leading">
 									{{template "repo/icon" .}}
 								</div>
diff --git a/templates/package/view.tmpl b/templates/package/view.tmpl
index 54af71126f..e81a714895 100644
--- a/templates/package/view.tmpl
+++ b/templates/package/view.tmpl
@@ -90,8 +90,8 @@
 				<a class="tw-float-right" href="{{$.PackageDescriptor.PackageWebLink}}/versions">{{ctx.Locale.Tr "packages.versions.view_all"}}</a>
 				<div class="ui relaxed list">
 				{{range .LatestVersions}}
-					<div class="item gt-df">
-						<a class="gt-f1 gt-ellipsis" title="{{.Version}}" href="{{$.PackageDescriptor.PackageWebLink}}/{{PathEscape .LowerVersion}}">{{.Version}}</a>
+					<div class="item tw-flex">
+						<a class="tw-flex-1 gt-ellipsis" title="{{.Version}}" href="{{$.PackageDescriptor.PackageWebLink}}/{{PathEscape .LowerVersion}}">{{.Version}}</a>
 						<span class="text small">{{DateTime "short" .CreatedUnix}}</span>
 					</div>
 				{{end}}
diff --git a/templates/projects/list.tmpl b/templates/projects/list.tmpl
index d87e7e0663..f33f9180bb 100644
--- a/templates/projects/list.tmpl
+++ b/templates/projects/list.tmpl
@@ -1,5 +1,5 @@
 {{if and $.CanWriteProjects (not $.Repository.IsArchived)}}
-	<div class="gt-df gt-sb gt-mb-4">
+	<div class="tw-flex tw-justify-between gt-mb-4">
 		<div class="small-menu-items ui compact tiny menu list-header-toggle">
 			<a class="item{{if not .IsShowClosed}} active{{end}}" href="?state=open&q={{$.Keyword}}">
 				{{svg "octicon-project-symlink" 16 "gt-mr-3"}}
diff --git a/templates/projects/view.tmpl b/templates/projects/view.tmpl
index a6e84024bc..d36ecdfc85 100644
--- a/templates/projects/view.tmpl
+++ b/templates/projects/view.tmpl
@@ -1,7 +1,7 @@
 {{$canWriteProject := and .CanWriteProjects (or (not .Repository) (not .Repository.IsArchived))}}
 
 <div class="ui container">
-	<div class="gt-df gt-sb gt-ac gt-mb-4">
+	<div class="tw-flex tw-justify-between tw-content-center gt-mb-4">
 		<h2 class="gt-mb-0">{{.Project.Title}}</h2>
 		{{if $canWriteProject}}
 			<div class="ui compact mini menu">
diff --git a/templates/repo/actions/list.tmpl b/templates/repo/actions/list.tmpl
index 55c0494566..f4215829ba 100644
--- a/templates/repo/actions/list.tmpl
+++ b/templates/repo/actions/list.tmpl
@@ -25,7 +25,7 @@
 				</div>
 			</div>
 			<div class="twelve wide column content">
-				<div class="ui secondary filter menu gt-je gt-df gt-ac">
+				<div class="ui secondary filter menu tw-justify-end tw-flex tw-content-center">
 					<!-- Actor -->
 					<div class="ui{{if not .Actors}} disabled{{end}} dropdown jump item">
 						<span class="text">{{ctx.Locale.Tr "actions.runs.actor"}}</span>
diff --git a/templates/repo/actions/runs_list.tmpl b/templates/repo/actions/runs_list.tmpl
index 580fb08a9e..b898837a26 100644
--- a/templates/repo/actions/runs_list.tmpl
+++ b/templates/repo/actions/runs_list.tmpl
@@ -6,7 +6,7 @@
 	</div>
 	{{end}}
 	{{range .Runs}}
-		<div class="flex-item gt-ac">
+		<div class="flex-item tw-content-center">
 			<div class="flex-item-leading">
 				{{template "repo/actions/status" (dict "status" .Status.String)}}
 			</div>
diff --git a/templates/repo/actions/status.tmpl b/templates/repo/actions/status.tmpl
index 5016570142..e42eafe8f6 100644
--- a/templates/repo/actions/status.tmpl
+++ b/templates/repo/actions/status.tmpl
@@ -12,7 +12,7 @@
 {{- $className = .className -}}
 {{- end -}}
 
-<span class="gt-df gt-ac" data-tooltip-content="{{ctx.Locale.Tr (printf "actions.status.%s" .status)}}">
+<span class="tw-flex tw-content-center" data-tooltip-content="{{ctx.Locale.Tr (printf "actions.status.%s" .status)}}">
 {{if eq .status "success"}}
 	{{svg "octicon-check-circle-fill" $size (printf "text green %s" $className)}}
 {{else if eq .status "skipped"}}
diff --git a/templates/repo/blame.tmpl b/templates/repo/blame.tmpl
index 6e0d0d1a5e..4d4f2069ee 100644
--- a/templates/repo/blame.tmpl
+++ b/templates/repo/blame.tmpl
@@ -11,11 +11,11 @@
 	{{end}}
 {{end}}
 <div class="{{TabSizeClass .Editorconfig .FileName}} non-diff-file-content">
-	<h4 class="file-header ui top attached header gt-df gt-ac gt-sb gt-fw">
-		<div class="file-header-left gt-df gt-ac gt-py-3 gt-pr-4">
+	<h4 class="file-header ui top attached header tw-flex tw-content-center tw-justify-between tw-flex-wrap">
+		<div class="file-header-left tw-flex tw-content-center gt-py-3 gt-pr-4">
 			{{template "repo/file_info" .}}
 		</div>
-		<div class="file-header-right file-actions gt-df gt-ac gt-fw">
+		<div class="file-header-right file-actions tw-flex tw-content-center tw-flex-wrap">
 			<div class="ui buttons">
 				<a class="ui tiny button" href="{{$.RawFileLink}}">{{ctx.Locale.Tr "repo.file_raw"}}</a>
 				{{if not .IsViewCommit}}
diff --git a/templates/repo/branch/list.tmpl b/templates/repo/branch/list.tmpl
index 48c14cf343..21121c4f09 100644
--- a/templates/repo/branch/list.tmpl
+++ b/templates/repo/branch/list.tmpl
@@ -25,7 +25,7 @@
 									<button class="btn interact-fg gt-px-2" data-clipboard-text="{{.DefaultBranchBranch.DBBranch.Name}}">{{svg "octicon-copy" 14}}</button>
 									{{template "repo/commit_statuses" dict "Status" (index $.CommitStatus .DefaultBranchBranch.DBBranch.CommitID) "Statuses" (index $.CommitStatuses .DefaultBranchBranch.DBBranch.CommitID)}}
 								</div>
-								<p class="info gt-df gt-ac gt-my-2">{{svg "octicon-git-commit" 16 "gt-mr-2"}}<a href="{{.RepoLink}}/commit/{{PathEscape .DefaultBranchBranch.DBBranch.CommitID}}">{{ShortSha .DefaultBranchBranch.DBBranch.CommitID}}</a> · <span class="commit-message">{{RenderCommitMessage $.Context .DefaultBranchBranch.DBBranch.CommitMessage (.Repository.ComposeMetas ctx)}}</span> · {{ctx.Locale.Tr "org.repo_updated"}} {{TimeSince .DefaultBranchBranch.DBBranch.CommitTime.AsTime ctx.Locale}}{{if .DefaultBranchBranch.DBBranch.Pusher}} &nbsp;{{template "shared/user/avatarlink" dict "user" .DefaultBranchBranch.DBBranch.Pusher}}{{template "shared/user/namelink" .DefaultBranchBranch.DBBranch.Pusher}}{{end}}</p>
+								<p class="info tw-flex tw-content-center gt-my-2">{{svg "octicon-git-commit" 16 "gt-mr-2"}}<a href="{{.RepoLink}}/commit/{{PathEscape .DefaultBranchBranch.DBBranch.CommitID}}">{{ShortSha .DefaultBranchBranch.DBBranch.CommitID}}</a> · <span class="commit-message">{{RenderCommitMessage $.Context .DefaultBranchBranch.DBBranch.CommitMessage (.Repository.ComposeMetas ctx)}}</span> · {{ctx.Locale.Tr "org.repo_updated"}} {{TimeSince .DefaultBranchBranch.DBBranch.CommitTime.AsTime ctx.Locale}}{{if .DefaultBranchBranch.DBBranch.Pusher}} &nbsp;{{template "shared/user/avatarlink" dict "user" .DefaultBranchBranch.DBBranch.Pusher}}{{template "shared/user/namelink" .DefaultBranchBranch.DBBranch.Pusher}}{{end}}</p>
 							</td>
 							<td class="right aligned middle aligned overflow-visible">
 								{{if and $.IsWriter (not $.Repository.IsArchived) (not .IsDeleted)}}
@@ -67,8 +67,8 @@
 			</div>
 		{{end}}
 
-		<h4 class="ui top attached header gt-df gt-ac gt-sb">
-			<div class="gt-df gt-ac">
+		<h4 class="ui top attached header tw-flex tw-content-center tw-justify-between">
+			<div class="tw-flex tw-content-center">
 				{{ctx.Locale.Tr "repo.branches"}}
 			</div>
 		</h4>
@@ -98,7 +98,7 @@
 									<button class="btn interact-fg gt-px-2" data-clipboard-text="{{.DBBranch.Name}}">{{svg "octicon-copy" 14}}</button>
 									{{template "repo/commit_statuses" dict "Status" (index $.CommitStatus .DBBranch.CommitID) "Statuses" (index $.CommitStatuses .DBBranch.CommitID)}}
 								</div>
-								<p class="info gt-df gt-ac gt-my-2">{{svg "octicon-git-commit" 16 "gt-mr-2"}}<a href="{{$.RepoLink}}/commit/{{PathEscape .DBBranch.CommitID}}">{{ShortSha .DBBranch.CommitID}}</a> · <span class="commit-message">{{RenderCommitMessage $.Context .DBBranch.CommitMessage ($.Repository.ComposeMetas ctx)}}</span> · {{ctx.Locale.Tr "org.repo_updated"}} {{TimeSince .DBBranch.CommitTime.AsTime ctx.Locale}}{{if .DBBranch.Pusher}} &nbsp;{{template "shared/user/avatarlink" dict "user" .DBBranch.Pusher}} &nbsp;{{template "shared/user/namelink" .DBBranch.Pusher}}{{end}}</p>
+								<p class="info tw-flex tw-content-center gt-my-2">{{svg "octicon-git-commit" 16 "gt-mr-2"}}<a href="{{$.RepoLink}}/commit/{{PathEscape .DBBranch.CommitID}}">{{ShortSha .DBBranch.CommitID}}</a> · <span class="commit-message">{{RenderCommitMessage $.Context .DBBranch.CommitMessage ($.Repository.ComposeMetas ctx)}}</span> · {{ctx.Locale.Tr "org.repo_updated"}} {{TimeSince .DBBranch.CommitTime.AsTime ctx.Locale}}{{if .DBBranch.Pusher}} &nbsp;{{template "shared/user/avatarlink" dict "user" .DBBranch.Pusher}} &nbsp;{{template "shared/user/namelink" .DBBranch.Pusher}}{{end}}</p>
 							{{end}}
 							</td>
 							<td class="two wide ui">
@@ -134,7 +134,7 @@
 									</a>
 									{{end}}
 								{{else}}
-									<a href="{{.LatestPullRequest.Issue.Link}}" class="gt-vm ref-issue">{{if not .LatestPullRequest.IsSameRepo}}{{.LatestPullRequest.BaseRepo.FullName}}{{end}}#{{.LatestPullRequest.Issue.Index}}</a>
+									<a href="{{.LatestPullRequest.Issue.Link}}" class="tw-align-middle ref-issue">{{if not .LatestPullRequest.IsSameRepo}}{{.LatestPullRequest.BaseRepo.FullName}}{{end}}#{{.LatestPullRequest.Issue.Index}}</a>
 									{{if .LatestPullRequest.HasMerged}}
 										<a href="{{.LatestPullRequest.Issue.Link}}" class="ui purple large label">{{svg "octicon-git-merge" 16 "gt-mr-2"}}{{ctx.Locale.Tr "repo.pulls.merged"}}</a>
 									{{else if .LatestPullRequest.Issue.IsClosed}}
diff --git a/templates/repo/branch_dropdown.tmpl b/templates/repo/branch_dropdown.tmpl
index 8a5cdc7cc7..19da1b6474 100644
--- a/templates/repo/branch_dropdown.tmpl
+++ b/templates/repo/branch_dropdown.tmpl
@@ -70,8 +70,8 @@
 <div class="js-branch-tag-selector {{if .ContainerClasses}}{{.ContainerClasses}}{{end}}">
 	{{/* show dummy elements before Vue componment is mounted, this code must match the code in BranchTagSelector.vue */}}
 	<div class="ui dropdown custom">
-		<button class="branch-dropdown-button gt-ellipsis ui basic small compact button gt-df gt-m-0">
-			<span class="text gt-df gt-ac gt-mr-2">
+		<button class="branch-dropdown-button gt-ellipsis ui basic small compact button tw-flex gt-m-0">
+			<span class="text tw-flex tw-content-center gt-mr-2">
 				{{if .release}}
 					{{ctx.Locale.Tr "repo.release.compare"}}
 				{{else}}
diff --git a/templates/repo/code/recently_pushed_new_branches.tmpl b/templates/repo/code/recently_pushed_new_branches.tmpl
index eac9d0e243..7e138c41e9 100644
--- a/templates/repo/code/recently_pushed_new_branches.tmpl
+++ b/templates/repo/code/recently_pushed_new_branches.tmpl
@@ -1,6 +1,6 @@
 {{range .RecentlyPushedNewBranches}}
-	<div class="ui positive message gt-df gt-ac">
-		<div class="gt-f1">
+	<div class="ui positive message tw-flex tw-content-center">
+		<div class="tw-flex-1">
 			{{$timeSince := TimeSince .CommitTime.AsTime ctx.Locale}}
 			{{$repo := .GetRepo $.Context}}
 			{{$name := .Name}}
diff --git a/templates/repo/commit_load_branches_and_tags.tmpl b/templates/repo/commit_load_branches_and_tags.tmpl
index 883230ac29..49f7323845 100644
--- a/templates/repo/commit_load_branches_and_tags.tmpl
+++ b/templates/repo/commit_load_branches_and_tags.tmpl
@@ -7,13 +7,13 @@
 	<div class="branch-and-tag-detail gt-hidden">
 		<div class="divider"></div>
 		<div>{{ctx.Locale.Tr "repo.commit.contained_in"}}</div>
-		<div class="gt-df gt-mt-3">
+		<div class="tw-flex gt-mt-3">
 			<div class="gt-p-2">{{svg "octicon-git-branch"}}</div>
-			<div class="branch-area flex-text-block gt-fw gt-f1"></div>
+			<div class="branch-area flex-text-block tw-flex-wrap tw-flex-1"></div>
 		</div>
-		<div class="gt-df gt-mt-3">
+		<div class="tw-flex gt-mt-3">
 			<div class="gt-p-2">{{svg "octicon-tag"}}</div>
-			<div class="tag-area flex-text-block gt-fw gt-f1"></div>
+			<div class="tag-area flex-text-block tw-flex-wrap tw-flex-1"></div>
 		</div>
 	</div>
 </div>
diff --git a/templates/repo/commit_page.tmpl b/templates/repo/commit_page.tmpl
index 80af73ce48..1670781e24 100644
--- a/templates/repo/commit_page.tmpl
+++ b/templates/repo/commit_page.tmpl
@@ -18,8 +18,8 @@
 			{{end}}
 		{{end}}
 		<div class="ui top attached header clearing segment tw-relative commit-header {{$class}}">
-			<div class="gt-df gt-mb-4 gt-fw">
-				<h3 class="gt-mb-0 gt-f1"><span class="commit-summary" title="{{.Commit.Summary}}">{{RenderCommitMessage $.Context .Commit.Message ($.Repository.ComposeMetas ctx)}}</span>{{template "repo/commit_statuses" dict "Status" .CommitStatus "Statuses" .CommitStatuses}}</h3>
+			<div class="tw-flex gt-mb-4 tw-flex-wrap">
+				<h3 class="gt-mb-0 tw-flex-1"><span class="commit-summary" title="{{.Commit.Summary}}">{{RenderCommitMessage $.Context .Commit.Message ($.Repository.ComposeMetas ctx)}}</span>{{template "repo/commit_statuses" dict "Status" .CommitStatus "Statuses" .CommitStatuses}}</h3>
 				{{if not $.PageIsWiki}}
 					<div>
 						<a class="ui primary tiny button" href="{{.SourcePath}}">
@@ -139,8 +139,8 @@
 			{{end}}
 			{{template "repo/commit_load_branches_and_tags" .}}
 		</div>
-		<div class="ui attached segment gt-df gt-ac gt-sb gt-py-2 commit-header-row gt-fw {{$class}}">
-				<div class="gt-df gt-ac author">
+		<div class="ui attached segment tw-flex tw-content-center tw-justify-between gt-py-2 commit-header-row tw-flex-wrap {{$class}}">
+				<div class="tw-flex tw-content-center author">
 					{{if .Author}}
 						{{ctx.AvatarUtils.Avatar .Author 28 "gt-mr-3"}}
 						{{if .Author.FullName}}
@@ -164,7 +164,7 @@
 						{{end}}
 					{{end}}
 				</div>
-				<div class="ui horizontal list gt-df gt-ac">
+				<div class="ui horizontal list tw-flex tw-content-center">
 					{{if .Parents}}
 						<div class="item">
 							<span>{{ctx.Locale.Tr "repo.diff.parent"}}</span>
@@ -184,8 +184,8 @@
 				</div>
 		</div>
 		{{if .Commit.Signature}}
-			<div class="ui bottom attached message tw-text-left gt-df gt-ac gt-sb commit-header-row gt-fw gt-mb-0 {{$class}}">
-				<div class="gt-df gt-ac">
+			<div class="ui bottom attached message tw-text-left tw-flex tw-content-center tw-justify-between commit-header-row tw-flex-wrap gt-mb-0 {{$class}}">
+				<div class="tw-flex tw-content-center">
 					{{if .Verification.Verified}}
 						{{if ne .Verification.SigningUser.ID 0}}
 							{{svg "gitea-lock" 16 "gt-mr-3"}}
@@ -209,7 +209,7 @@
 						<span class="ui text">{{ctx.Locale.Tr .Verification.Reason}}</span>
 					{{end}}
 				</div>
-				<div class="gt-df gt-ac">
+				<div class="tw-flex tw-content-center">
 					{{if .Verification.Verified}}
 						{{if ne .Verification.SigningUser.ID 0}}
 							{{svg "octicon-verified" 16 "gt-mr-3"}}
diff --git a/templates/repo/commit_statuses.tmpl b/templates/repo/commit_statuses.tmpl
index b035e74c2f..f451ac06a1 100644
--- a/templates/repo/commit_statuses.tmpl
+++ b/templates/repo/commit_statuses.tmpl
@@ -1,10 +1,10 @@
 {{if .Statuses}}
 	{{if and (eq (len .Statuses) 1) .Status.TargetURL}}
-		<a class="gt-vm {{.AdditionalClasses}} tw-no-underline" data-tippy="commit-statuses" href="{{.Status.TargetURL}}">
+		<a class="tw-align-middle {{.AdditionalClasses}} tw-no-underline" data-tippy="commit-statuses" href="{{.Status.TargetURL}}">
 			{{template "repo/commit_status" .Status}}
 		</a>
 	{{else}}
-		<span class="gt-vm {{.AdditionalClasses}}" data-tippy="commit-statuses" tabindex="0">
+		<span class="tw-align-middle {{.AdditionalClasses}}" data-tippy="commit-statuses" tabindex="0">
 			{{template "repo/commit_status" .Status}}
 		</span>
 	{{end}}
diff --git a/templates/repo/commits.tmpl b/templates/repo/commits.tmpl
index 7b3b27af1d..29afc350f6 100644
--- a/templates/repo/commits.tmpl
+++ b/templates/repo/commits.tmpl
@@ -4,7 +4,7 @@
 	<div class="ui container">
 		{{template "repo/sub_menu" .}}
 		<div class="repo-button-row">
-			<div class="gt-df gt-ac">
+			<div class="tw-flex tw-content-center">
 				{{template "repo/branch_dropdown" dict "root" . "ContainerClasses" "gt-mr-2"}}
 				<a href="{{.RepoLink}}/graph" class="ui basic small compact button">
 					{{svg "octicon-git-branch"}}
diff --git a/templates/repo/commits_list_small.tmpl b/templates/repo/commits_list_small.tmpl
index 86e6b7225e..53834f7acb 100644
--- a/templates/repo/commits_list_small.tmpl
+++ b/templates/repo/commits_list_small.tmpl
@@ -13,7 +13,7 @@
 
 		{{$commitLink:= printf "%s/commit/%s" $.comment.Issue.PullRequest.BaseRepo.Link (PathEscape .ID.String)}}
 
-		<span class="shabox gt-df gt-ac tw-float-right">
+		<span class="shabox tw-flex tw-content-center tw-float-right">
 			{{template "repo/commit_statuses" dict "Status" .Status "Statuses" .Statuses}}
 			{{$class := "ui sha label"}}
 			{{if .Signature}}
diff --git a/templates/repo/commits_table.tmpl b/templates/repo/commits_table.tmpl
index 221ee8d99b..330130ac0d 100644
--- a/templates/repo/commits_table.tmpl
+++ b/templates/repo/commits_table.tmpl
@@ -1,5 +1,5 @@
-<h4 class="ui top attached header commits-table gt-df gt-ac gt-sb">
-	<div class="commits-table-left gt-df gt-ac">
+<h4 class="ui top attached header commits-table tw-flex tw-content-center tw-justify-between">
+	<div class="commits-table-left tw-flex tw-content-center">
 		{{if or .PageIsCommits (gt .CommitCount 0)}}
 			{{.CommitCount}} {{ctx.Locale.Tr "repo.commits.commits"}}
 		{{else if .IsNothingToCompare}}
diff --git a/templates/repo/diff/blob_excerpt.tmpl b/templates/repo/diff/blob_excerpt.tmpl
index 353f6db705..201bff805a 100644
--- a/templates/repo/diff/blob_excerpt.tmpl
+++ b/templates/repo/diff/blob_excerpt.tmpl
@@ -3,7 +3,7 @@
 	<tr class="{{.GetHTMLDiffLineType}}-code nl-{{$k}} ol-{{$k}}">
 		{{if eq .GetType 4}}
 			<td class="lines-num lines-num-old" data-line-num="{{if $line.LeftIdx}}{{$line.LeftIdx}}{{end}}">
-				<div class="gt-df">
+				<div class="tw-flex">
 				{{if or (eq $line.GetExpandDirection 3) (eq $line.GetExpandDirection 5)}}
 					<button class="code-expander-button" hx-target="closest tr" hx-get="{{$.RepoLink}}/blob_excerpt/{{PathEscape $.AfterCommitID}}?{{$line.GetBlobExcerptQuery}}&style=split&direction=down&wiki={{$.PageIsWiki}}&anchor={{$.Anchor}}">
 						{{svg "octicon-fold-down"}}
@@ -49,7 +49,7 @@
 	<tr class="{{.GetHTMLDiffLineType}}-code nl-{{$k}} ol-{{$k}}">
 		{{if eq .GetType 4}}
 			<td colspan="2" class="lines-num">
-				<div class="gt-df">
+				<div class="tw-flex">
 					{{if or (eq $line.GetExpandDirection 3) (eq $line.GetExpandDirection 5)}}
 						<button class="code-expander-button" hx-target="closest tr" hx-get="{{$.RepoLink}}/blob_excerpt/{{PathEscape $.AfterCommitID}}?data-query={{$line.GetBlobExcerptQuery}}&style=unified&direction=down&wiki={{$.PageIsWiki}}&anchor={{$.Anchor}}">
 							{{svg "octicon-fold-down"}}
diff --git a/templates/repo/diff/box.tmpl b/templates/repo/diff/box.tmpl
index 3daac1c2ce..fc6a5e0c3c 100644
--- a/templates/repo/diff/box.tmpl
+++ b/templates/repo/diff/box.tmpl
@@ -1,7 +1,7 @@
 {{$showFileTree := (and (not .DiffNotAvailable) (gt .Diff.NumFiles 1))}}
 <div>
 	<div class="diff-detail-box diff-box">
-		<div class="gt-df gt-ac gt-fw gt-gap-3 gt-ml-1">
+		<div class="tw-flex tw-content-center tw-flex-wrap gt-gap-3 gt-ml-1">
 			{{if $showFileTree}}
 				<button class="diff-toggle-file-tree-button not-mobile btn interact-fg" data-show-text="{{ctx.Locale.Tr "repo.diff.show_file_tree"}}" data-hide-text="{{ctx.Locale.Tr "repo.diff.hide_file_tree"}}">
 					{{/* the icon meaning is reversed here, "octicon-sidebar-collapse" means show the file tree */}}
@@ -18,14 +18,14 @@
 				</script>
 			{{end}}
 			{{if not .DiffNotAvailable}}
-				<div class="diff-detail-stats gt-df gt-ac gt-fw">
+				<div class="diff-detail-stats tw-flex tw-content-center tw-flex-wrap">
 					{{svg "octicon-diff" 16 "gt-mr-2"}}{{ctx.Locale.Tr "repo.diff.stats_desc" .Diff.NumFiles .Diff.TotalAddition .Diff.TotalDeletion}}
 				</div>
 			{{end}}
 		</div>
 		<div class="diff-detail-actions">
 			{{if and .PageIsPullFiles $.SignedUserID (not .IsArchived) (not .DiffNotAvailable)}}
-				<div class="not-mobile gt-df gt-ac gt-fc tw-whitespace-nowrap gt-mr-2">
+				<div class="not-mobile tw-flex tw-content-center tw-flex-col tw-whitespace-nowrap gt-mr-2">
 					<label for="viewed-files-summary" id="viewed-files-summary-label" data-text-changed-template="{{ctx.Locale.Tr "repo.pulls.viewed_files_label"}}">
 						{{ctx.Locale.Tr "repo.pulls.viewed_files_label" .Diff.NumViewedFiles .Diff.NumFiles}}
 					</label>
@@ -110,8 +110,8 @@
 					{{$isExpandable := or (gt $file.Addition 0) (gt $file.Deletion 0) $file.IsBin}}
 					{{$isReviewFile := and $.IsSigned $.PageIsPullFiles (not $.IsArchived) $.IsShowingAllCommits}}
 					<div class="diff-file-box diff-box file-content {{TabSizeClass $.Editorconfig $file.Name}} gt-mt-0" id="diff-{{$file.NameHash}}" data-old-filename="{{$file.OldName}}" data-new-filename="{{$file.Name}}" {{if or ($file.ShouldBeHidden) (not $isExpandable)}}data-folded="true"{{end}}>
-						<h4 class="diff-file-header sticky-2nd-row ui top attached normal header gt-df gt-ac gt-sb gt-fw">
-							<div class="diff-file-name gt-df gt-ac gt-gap-2 gt-fw">
+						<h4 class="diff-file-header sticky-2nd-row ui top attached normal header tw-flex tw-content-center tw-justify-between tw-flex-wrap">
+							<div class="diff-file-name tw-flex tw-content-center gt-gap-2 tw-flex-wrap">
 								<button class="fold-file btn interact-bg gt-p-2{{if not $isExpandable}} tw-invisible{{end}}">
 									{{if $file.ShouldBeHidden}}
 										{{svg "octicon-chevron-right" 18}}
@@ -119,7 +119,7 @@
 										{{svg "octicon-chevron-down" 18}}
 									{{end}}
 								</button>
-								<div class="gt-font-semibold gt-df gt-ac gt-mono">
+								<div class="gt-font-semibold tw-flex tw-content-center gt-mono">
 									{{if $file.IsBin}}
 										<span class="gt-ml-1 gt-mr-3">
 											{{ctx.Locale.Tr "repo.diff.bin"}}
@@ -144,7 +144,7 @@
 									<span class="gt-ml-4 gt-mono">{{ctx.Locale.Tr ($file.ModeTranslationKey $file.Mode)}}</span>
 								{{end}}
 							</div>
-							<div class="diff-file-header-actions gt-df gt-ac gt-gap-2 gt-fw">
+							<div class="diff-file-header-actions tw-flex tw-content-center gt-gap-2 tw-flex-wrap">
 								{{if $showFileViewToggle}}
 									<div class="ui compact icon buttons">
 										<button class="ui tiny basic button file-view-toggle" data-toggle-selector="#diff-source-{{$file.NameHash}}" data-tooltip-content="{{ctx.Locale.Tr "repo.file_view_source"}}">{{svg "octicon-code"}}</button>
@@ -221,7 +221,7 @@
 
 				{{if .Diff.IsIncomplete}}
 					<div class="diff-file-box diff-box file-content gt-mt-3" id="diff-incomplete">
-						<h4 class="ui top attached normal header gt-df gt-ac gt-sb">
+						<h4 class="ui top attached normal header tw-flex tw-content-center tw-justify-between">
 							{{ctx.Locale.Tr "repo.diff.too_many_files"}}
 							<a class="ui basic tiny button" id="diff-show-more-files" data-href="?skip-to={{.Diff.End}}&file-only=true">{{ctx.Locale.Tr "repo.diff.show_more"}}</a>
 						</h4>
diff --git a/templates/repo/diff/comments.tmpl b/templates/repo/diff/comments.tmpl
index 465ed2b911..6747328bb7 100644
--- a/templates/repo/diff/comments.tmpl
+++ b/templates/repo/diff/comments.tmpl
@@ -8,8 +8,8 @@
 		{{template "shared/user/avatarlink" dict "user" .Poster}}
 	{{end}}
 	<div class="content comment-container">
-		<div class="ui top attached header comment-header gt-df gt-ac gt-sb">
-			<div class="comment-header-left gt-df gt-ac">
+		<div class="ui top attached header comment-header tw-flex tw-content-center tw-justify-between">
+			<div class="comment-header-left tw-flex tw-content-center">
 				{{if .OriginalAuthor}}
 					<span class="text black gt-font-semibold gt-mr-2">
 						{{svg (MigrationIcon $.root.Repository.GetOriginalURLHostname)}}
@@ -30,7 +30,7 @@
 					</span>
 				{{end}}
 			</div>
-			<div class="comment-header-right actions gt-df gt-ac">
+			<div class="comment-header-right actions tw-flex tw-content-center">
 				{{if .Invalidated}}
 					{{$referenceUrl := printf "%s#%s" $.root.Issue.Link .HashTag}}
 					<a href="{{AppSubUrl}}{{$referenceUrl}}" class="ui label basic small" data-tooltip-content="{{ctx.Locale.Tr "repo.issues.review.outdated_description"}}">
diff --git a/templates/repo/diff/new_review.tmpl b/templates/repo/diff/new_review.tmpl
index ae7182c930..9d1eef712d 100644
--- a/templates/repo/diff/new_review.tmpl
+++ b/templates/repo/diff/new_review.tmpl
@@ -1,5 +1,5 @@
 <div id="review-box">
-	<button class="ui tiny primary button gt-pr-2 gt-df js-btn-review {{if not $.IsShowingAllCommits}}disabled{{end}}" {{if not $.IsShowingAllCommits}}data-tooltip-content="{{ctx.Locale.Tr "repo.pulls.review_only_possible_for_full_diff"}}"{{end}}>
+	<button class="ui tiny primary button gt-pr-2 tw-flex js-btn-review {{if not $.IsShowingAllCommits}}disabled{{end}}" {{if not $.IsShowingAllCommits}}data-tooltip-content="{{ctx.Locale.Tr "repo.pulls.review_only_possible_for_full_diff"}}"{{end}}>
 		{{ctx.Locale.Tr "repo.diff.review"}}
 		<span class="ui small label review-comments-counter" data-pending-comment-number="{{.PendingCodeCommentNumber}}">{{.PendingCodeCommentNumber}}</span>
 		{{svg "octicon-triangle-down" 14 "dropdown icon"}}
@@ -10,8 +10,8 @@
 			<form class="ui form form-fetch-action" action="{{.Link}}/reviews/submit" method="post">
 				{{.CsrfTokenHtml}}
 				<input type="hidden" name="commit_id" value="{{.AfterCommitID}}">
-				<div class="field gt-df gt-ac">
-					<div class="gt-f1">{{ctx.Locale.Tr "repo.diff.review.header"}}</div>
+				<div class="field tw-flex tw-content-center">
+					<div class="tw-flex-1">{{ctx.Locale.Tr "repo.diff.review.header"}}</div>
 					<a class="muted close">{{svg "octicon-x" 16}}</a>
 				</div>
 				<div class="field">
@@ -31,7 +31,7 @@
 				<div class="divider"></div>
 				{{$showSelfTooltip := (and $.IsSigned ($.Issue.IsPoster $.SignedUser.ID))}}
 				{{if $showSelfTooltip}}
-					<span class="gt-dib" data-tooltip-content="{{ctx.Locale.Tr "repo.diff.review.self_approve"}}">
+					<span class="tw-inline-block" data-tooltip-content="{{ctx.Locale.Tr "repo.diff.review.self_approve"}}">
 						<button type="submit" name="type" value="approve" disabled class="ui submit primary tiny button btn-submit">{{ctx.Locale.Tr "repo.diff.review.approve"}}</button>
 					</span>
 				{{else}}
@@ -39,7 +39,7 @@
 				{{end}}
 				<button type="submit" name="type" value="comment" class="ui submit tiny basic button btn-submit">{{ctx.Locale.Tr "repo.diff.review.comment"}}</button>
 				{{if $showSelfTooltip}}
-					<span class="gt-dib" data-tooltip-content="{{ctx.Locale.Tr "repo.diff.review.self_reject"}}">
+					<span class="tw-inline-block" data-tooltip-content="{{ctx.Locale.Tr "repo.diff.review.self_reject"}}">
 						<button type="submit" name="type" value="reject" disabled class="ui submit red tiny button btn-submit">{{ctx.Locale.Tr "repo.diff.review.reject"}}</button>
 					</span>
 				{{else}}
diff --git a/templates/repo/diff/section_split.tmpl b/templates/repo/diff/section_split.tmpl
index 00e23f5c1c..0030bbef8e 100644
--- a/templates/repo/diff/section_split.tmpl
+++ b/templates/repo/diff/section_split.tmpl
@@ -16,7 +16,7 @@
 			<tr class="{{.GetHTMLDiffLineType}}-code nl-{{$k}} ol-{{$k}}" data-line-type="{{.GetHTMLDiffLineType}}">
 				{{if eq .GetType 4}}
 					<td class="lines-num lines-num-old">
-						<div class="gt-df">
+						<div class="tw-flex">
 						{{if or (eq $line.GetExpandDirection 3) (eq $line.GetExpandDirection 5)}}
 							<button class="code-expander-button" hx-target="closest tr" hx-get="{{$.root.RepoLink}}/blob_excerpt/{{PathEscape $.root.AfterCommitID}}?{{$line.GetBlobExcerptQuery}}&style=split&direction=down&wiki={{$.root.PageIsWiki}}&anchor=diff-{{$file.NameHash}}K{{$line.SectionInfo.RightIdx}}">
 								{{svg "octicon-fold-down"}}
diff --git a/templates/repo/diff/section_unified.tmpl b/templates/repo/diff/section_unified.tmpl
index e3249c26d5..112924d1a7 100644
--- a/templates/repo/diff/section_unified.tmpl
+++ b/templates/repo/diff/section_unified.tmpl
@@ -12,7 +12,7 @@
 			{{if eq .GetType 4}}
 				{{if $.root.AfterCommitID}}
 					<td colspan="2" class="lines-num">
-						<div class="gt-df">
+						<div class="tw-flex">
 							{{if or (eq $line.GetExpandDirection 3) (eq $line.GetExpandDirection 5)}}
 								<button class="code-expander-button" hx-target="closest tr" hx-get="{{$.root.RepoLink}}/blob_excerpt/{{PathEscape $.root.AfterCommitID}}?{{$line.GetBlobExcerptQuery}}&style=unified&direction=down&wiki={{$.root.PageIsWiki}}&anchor=diff-{{$file.NameHash}}K{{$line.SectionInfo.RightIdx}}">
 									{{svg "octicon-fold-down"}}
diff --git a/templates/repo/find/files.tmpl b/templates/repo/find/files.tmpl
index eac6ec2011..de2c34a158 100644
--- a/templates/repo/find/files.tmpl
+++ b/templates/repo/find/files.tmpl
@@ -2,10 +2,10 @@
 <div role="main" aria-label="{{.Title}}" class="page-content repository">
 	{{template "repo/header" .}}
 	<div class="ui container">
-		<div class="gt-df gt-ac">
+		<div class="tw-flex tw-content-center">
 			<a href="{{$.RepoLink}}">{{.RepoName}}</a>
 			<span class="gt-mx-3">/</span>
-			<div class="ui input gt-f1">
+			<div class="ui input tw-flex-1">
 				<input id="repo-file-find-input" type="text" autofocus data-url-data-link="{{.DataLink}}" data-url-tree-link="{{.TreeLink}}">
 			</div>
 		</div>
diff --git a/templates/repo/forks.tmpl b/templates/repo/forks.tmpl
index b27b55c131..0a4b369cdb 100644
--- a/templates/repo/forks.tmpl
+++ b/templates/repo/forks.tmpl
@@ -6,7 +6,7 @@
 			{{ctx.Locale.Tr "repo.forks"}}
 		</h2>
 		{{range .Forks}}
-			<div class="gt-df gt-ac gt-py-3">
+			<div class="tw-flex tw-content-center gt-py-3">
 				<span class="gt-mr-2">{{ctx.AvatarUtils.Avatar .Owner}}</span>
 				<a href="{{.Owner.HomeLink}}">{{.Owner.Name}}</a> / <a href="{{.Link}}">{{.Name}}</a>
 			</div>
diff --git a/templates/repo/graph/commits.tmpl b/templates/repo/graph/commits.tmpl
index 61ef1fe10d..fc7cf925ab 100644
--- a/templates/repo/graph/commits.tmpl
+++ b/templates/repo/graph/commits.tmpl
@@ -28,10 +28,10 @@
 							{{- end -}}
 						</a>
 					</span>
-					<span class="message gt-dib gt-ellipsis gt-mr-3">
+					<span class="message tw-inline-block gt-ellipsis gt-mr-3">
 						<span>{{RenderCommitMessage $.Context $commit.Subject ($.Repository.ComposeMetas ctx)}}</span>
 					</span>
-					<span class="commit-refs gt-df gt-ac gt-mr-2">
+					<span class="commit-refs tw-flex tw-content-center gt-mr-2">
 						{{range $commit.Refs}}
 							{{$refGroup := .RefGroup}}
 							{{if eq $refGroup "pull"}}
@@ -58,7 +58,7 @@
 							{{end}}
 						{{end}}
 					</span>
-					<span class="author gt-df gt-ac gt-mr-3">
+					<span class="author tw-flex tw-content-center gt-mr-3">
 						{{$userName := $commit.Commit.Author.Name}}
 						{{if $commit.User}}
 							{{if $commit.User.FullName}}
@@ -71,7 +71,7 @@
 							{{$userName}}
 						{{end}}
 					</span>
-					<span class="time gt-df gt-ac">{{DateTime "full" $commit.Date}}</span>
+					<span class="time tw-flex tw-content-center">{{DateTime "full" $commit.Date}}</span>
 				{{end}}
 			</li>
 		{{end}}
diff --git a/templates/repo/home.tmpl b/templates/repo/home.tmpl
index 2c037fd3d9..05524e79a3 100644
--- a/templates/repo/home.tmpl
+++ b/templates/repo/home.tmpl
@@ -27,19 +27,19 @@
 				</form>
 			</div>
 		</div>
-		<div class="gt-df gt-ac gt-fw gt-gap-2" id="repo-topics">
+		<div class="tw-flex tw-content-center tw-flex-wrap gt-gap-2" id="repo-topics">
 			{{range .Topics}}<a class="ui repo-topic large label topic gt-m-0" href="{{AppSubUrl}}/explore/repos?q={{.Name}}&topic=1">{{.Name}}</a>{{end}}
 			{{if and .Permission.IsAdmin (not .Repository.IsArchived)}}<button id="manage_topic" class="btn interact-fg gt-font-12">{{ctx.Locale.Tr "repo.topic.manage_topics"}}</button>{{end}}
 		</div>
 		{{end}}
 		{{if and .Permission.IsAdmin (not .Repository.IsArchived)}}
-		<div class="ui form gt-hidden gt-df gt-fc gt-mt-4" id="topic_edit">
-			<div class="field gt-f1 gt-mb-2">
-				<div class="ui fluid multiple search selection dropdown gt-fw" data-text-count-prompt="{{ctx.Locale.Tr "repo.topic.count_prompt"}}" data-text-format-prompt="{{ctx.Locale.Tr "repo.topic.format_prompt"}}">
+		<div class="ui form gt-hidden tw-flex tw-flex-col gt-mt-4" id="topic_edit">
+			<div class="field tw-flex-1 gt-mb-2">
+				<div class="ui fluid multiple search selection dropdown tw-flex-wrap" data-text-count-prompt="{{ctx.Locale.Tr "repo.topic.count_prompt"}}" data-text-format-prompt="{{ctx.Locale.Tr "repo.topic.format_prompt"}}">
 					<input type="hidden" name="topics" value="{{range $i, $v := .Topics}}{{.Name}}{{if Eval $i "+" 1 "<" (len $.Topics)}},{{end}}{{end}}">
 					{{range .Topics}}
 						{{/* keey the same layout as Fomantic UI generated labels */}}
-						<a class="ui label transition visible tw-cursor-default gt-dib" data-value="{{.Name}}">{{.Name}}{{svg "octicon-x" 16 "delete icon"}}</a>
+						<a class="ui label transition visible tw-cursor-default tw-inline-block" data-value="{{.Name}}">{{.Name}}{{svg "octicon-x" 16 "delete icon"}}</a>
 					{{end}}
 					<div class="text"></div>
 				</div>
@@ -69,7 +69,7 @@
 		{{end}}
 		{{template "repo/sub_menu" .}}
 		<div class="repo-button-row">
-			<div class="gt-df gt-ac gt-fw gt-gap-y-3">
+			<div class="tw-flex tw-content-center tw-flex-wrap gt-gap-y-3">
 				{{template "repo/branch_dropdown" dict "root" . "ContainerClasses" "gt-mr-2"}}
 				{{if and .CanCompareOrPull .IsViewBranch (not .Repository.IsArchived)}}
 					{{$cmpBranch := ""}}
@@ -129,7 +129,7 @@
 					</span>
 				{{end}}
 			</div>
-			<div class="gt-df gt-ac">
+			<div class="tw-flex tw-content-center">
 				<!-- Only show clone panel in repository home page -->
 				{{if eq $n 0}}
 					<div class="clone-panel ui action tiny input">
diff --git a/templates/repo/icon.tmpl b/templates/repo/icon.tmpl
index a001f81891..e5e0bd68e7 100644
--- a/templates/repo/icon.tmpl
+++ b/templates/repo/icon.tmpl
@@ -1,6 +1,6 @@
 {{$avatarLink := (.RelAvatarLink ctx)}}
 {{if $avatarLink}}
-	<img class="ui avatar gt-vm" src="{{$avatarLink}}" width="24" height="24" alt="{{.FullName}}">
+	<img class="ui avatar tw-align-middle" src="{{$avatarLink}}" width="24" height="24" alt="{{.FullName}}">
 {{else if $.IsMirror}}
 	{{svg "octicon-mirror" 24}}
 {{else if $.IsFork}}
diff --git a/templates/repo/issue/card.tmpl b/templates/repo/issue/card.tmpl
index ff635c736a..3e1af71b21 100644
--- a/templates/repo/issue/card.tmpl
+++ b/templates/repo/issue/card.tmpl
@@ -7,13 +7,13 @@
 		</div>
 	{{end}}
 	<div class="content gt-p-0 tw-w-full">
-		<div class="gt-df tw-items-start">
+		<div class="tw-flex tw-items-start">
 			<div class="issue-card-icon">
 				{{template "shared/issueicon" .}}
 			</div>
 			<a class="issue-card-title muted issue-title" href="{{.Link}}">{{.Title | RenderEmoji ctx | RenderCodeBlock}}</a>
 			{{if and $.isPinnedIssueCard $.Page.IsRepoAdmin}}
-				<a role="button" class="issue-card-unpin muted gt-df gt-ac" data-tooltip-content={{ctx.Locale.Tr "repo.issues.unpin_issue"}} data-issue-id="{{.ID}}" data-unpin-url="{{$.Page.Link}}/unpin/{{.Index}}">
+				<a role="button" class="issue-card-unpin muted tw-flex tw-content-center" data-tooltip-content={{ctx.Locale.Tr "repo.issues.unpin_issue"}} data-issue-id="{{.ID}}" data-unpin-url="{{$.Page.Link}}/unpin/{{.Index}}">
 					{{svg "octicon-x" 16}}
 				</a>
 			{{end}}
@@ -34,8 +34,8 @@
 		{{if .MilestoneID}}
 		<div class="meta gt-my-2">
 			<a class="milestone" href="{{.Repo.Link}}/milestone/{{.MilestoneID}}">
-				{{svg "octicon-milestone" 16 "gt-mr-2 gt-vm"}}
-				<span class="gt-vm">{{.Milestone.Name}}</span>
+				{{svg "octicon-milestone" 16 "gt-mr-2 tw-align-middle"}}
+				<span class="tw-align-middle">{{.Milestone.Name}}</span>
 			</a>
 		</div>
 		{{end}}
@@ -43,8 +43,8 @@
 		{{range index $.Page.LinkedPRs .ID}}
 		<div class="meta gt-my-2">
 			<a href="{{$.Issue.Repo.Link}}/pulls/{{.Index}}">
-				<span class="gt-m-0 text {{if .PullRequest.HasMerged}}purple{{else if .IsClosed}}red{{else}}green{{end}}">{{svg "octicon-git-merge" 16 "gt-mr-2 gt-vm"}}</span>
-				<span class="gt-vm">{{.Title}} <span class="text light grey">#{{.Index}}</span></span>
+				<span class="gt-m-0 text {{if .PullRequest.HasMerged}}purple{{else if .IsClosed}}red{{else}}green{{end}}">{{svg "octicon-git-merge" 16 "gt-mr-2 tw-align-middle"}}</span>
+				<span class="tw-align-middle">{{.Title}} <span class="text light grey">#{{.Index}}</span></span>
 			</a>
 		</div>
 		{{end}}
@@ -52,8 +52,8 @@
 		{{$tasks := .GetTasks}}
 		{{if gt $tasks 0}}
 			<div class="meta gt-my-2">
-				{{svg "octicon-checklist" 16 "gt-mr-2 gt-vm"}}
-				<span class="gt-vm">{{.GetTasksDone}} / {{$tasks}}</span>
+				{{svg "octicon-checklist" 16 "gt-mr-2 tw-align-middle"}}
+				<span class="tw-align-middle">{{.GetTasksDone}} / {{$tasks}}</span>
 			</div>
 		{{end}}
 	</div>
diff --git a/templates/repo/issue/label_precolors.tmpl b/templates/repo/issue/label_precolors.tmpl
index 146119b978..80007662c0 100644
--- a/templates/repo/issue/label_precolors.tmpl
+++ b/templates/repo/issue/label_precolors.tmpl
@@ -1,5 +1,5 @@
 <div class="precolors">
-	<div class="gt-df">
+	<div class="tw-flex">
 		<a class="color" style="background-color:#e11d21" data-color-hex="#e11d21"></a>
 		<a class="color" style="background-color:#eb6420" data-color-hex="#eb6420"></a>
 		<a class="color" style="background-color:#fbca04" data-color-hex="#fbca04"></a>
@@ -9,7 +9,7 @@
 		<a class="color" style="background-color:#0052cc" data-color-hex="#0052cc"></a>
 		<a class="color" style="background-color:#5319e7" data-color-hex="#5319e7"></a>
 	</div>
-	<div class="gt-df">
+	<div class="tw-flex">
 		<a class="color" style="background-color:#f6c6c7" data-color-hex="#f6c6c7"></a>
 		<a class="color" style="background-color:#fad8c7" data-color-hex="#fad8c7"></a>
 		<a class="color" style="background-color:#fef2c0" data-color-hex="#fef2c0"></a>
diff --git a/templates/repo/issue/labels/label_list.tmpl b/templates/repo/issue/labels/label_list.tmpl
index fbfc885880..cc244af17e 100644
--- a/templates/repo/issue/labels/label_list.tmpl
+++ b/templates/repo/issue/labels/label_list.tmpl
@@ -42,9 +42,9 @@
 					<a class="open-issues" href="{{$.RepoLink}}/issues?labels={{.ID}}">{{svg "octicon-issue-opened"}} {{ctx.Locale.Tr "repo.issues.label_open_issues" .NumOpenIssues}}</a>
 				{{end}}
 			</div>
-			<div class="label-operation gt-df">
+			<div class="label-operation tw-flex">
 				{{template "repo/issue/labels/label_archived" .}}
-				<div class="gt-df tw-ml-auto">
+				<div class="tw-flex tw-ml-auto">
 					{{if and (not $.PageIsOrgSettingsLabels) (not $.Repository.IsArchived) (or $.CanWriteIssues $.CanWritePulls)}}
 						<a class="edit-label-button" href="#" data-id="{{.ID}}" data-title="{{.Name}}" {{if .Exclusive}}data-exclusive{{end}} {{if gt .ArchivedUnix 0}}data-is-archived{{end}} data-num-issues="{{.NumIssues}}" data-description="{{.Description}}" data-color={{.Color}}>{{svg "octicon-pencil"}} {{ctx.Locale.Tr "repo.issues.label_edit"}}</a>
 						<a class="delete-button" href="#" data-url="{{$.Link}}/delete" data-id="{{.ID}}">{{svg "octicon-trash"}} {{ctx.Locale.Tr "repo.issues.label_delete"}}</a>
diff --git a/templates/repo/issue/milestone_issues.tmpl b/templates/repo/issue/milestone_issues.tmpl
index 507c3ce37a..e029bf6031 100644
--- a/templates/repo/issue/milestone_issues.tmpl
+++ b/templates/repo/issue/milestone_issues.tmpl
@@ -3,10 +3,10 @@
 	{{template "repo/header" .}}
 	<div class="ui container">
 		{{template "base/alert" .}}
-		<div class="gt-df">
+		<div class="tw-flex">
 			<h1 class="gt-mb-3">{{.Milestone.Name}}</h1>
 			{{if not .Repository.IsArchived}}
-				<div class="text right gt-f1">
+				<div class="text right tw-flex-1">
 					{{if or .CanWriteIssues .CanWritePulls}}
 						{{if .Milestone.IsClosed}}
 							<a class="ui primary basic button link-action" href data-url="{{$.RepoLink}}/milestones/{{.MilestoneID}}/open">{{ctx.Locale.Tr "repo.milestones.open"}}
@@ -26,10 +26,10 @@
 				{{.Milestone.RenderedContent}}
 		</div>
 		{{end}}
-		<div class="gt-df gt-fc gt-gap-3">
+		<div class="tw-flex tw-flex-col gt-gap-3">
 			<progress class="milestone-progress-big" value="{{.Milestone.Completeness}}" max="100"></progress>
-			<div class="gt-df gt-gap-4">
-				<div classs="gt-df gt-ac">
+			<div class="tw-flex gt-gap-4">
+				<div classs="tw-flex tw-content-center">
 					{{$closedDate:= TimeSinceUnix .Milestone.ClosedDateUnix ctx.Locale}}
 					{{if .IsClosed}}
 						{{svg "octicon-clock"}} {{ctx.Locale.Tr "repo.milestones.closed" $closedDate}}
diff --git a/templates/repo/issue/milestones.tmpl b/templates/repo/issue/milestones.tmpl
index 363ba7e3a2..af7dd70193 100644
--- a/templates/repo/issue/milestones.tmpl
+++ b/templates/repo/issue/milestones.tmpl
@@ -23,7 +23,7 @@
 							{{svg "octicon-milestone" 16}}
 							<a class="muted" href="{{$.RepoLink}}/milestone/{{.ID}}">{{.Name}}</a>
 						</h3>
-						<div class="gt-df gt-ac">
+						<div class="tw-flex tw-content-center">
 							<span class="gt-mr-3">{{.Completeness}}%</span>
 							<progress value="{{.Completeness}}" max="100"></progress>
 						</div>
diff --git a/templates/repo/issue/new_form.tmpl b/templates/repo/issue/new_form.tmpl
index b2b9e308f5..ba1e19bf07 100644
--- a/templates/repo/issue/new_form.tmpl
+++ b/templates/repo/issue/new_form.tmpl
@@ -171,7 +171,7 @@
 				<div class="selected">
 				{{range .Assignees}}
 					<a class="item gt-p-2 muted gt-hidden" id="assignee_{{.ID}}" href="{{$.RepoLink}}/issues?assignee={{.ID}}">
-						{{ctx.AvatarUtils.Avatar . 28 "gt-mr-3 gt-vm"}}{{.GetDisplayName}}
+						{{ctx.AvatarUtils.Avatar . 28 "gt-mr-3 tw-align-middle"}}{{.GetDisplayName}}
 					</a>
 				{{end}}
 				</div>
diff --git a/templates/repo/issue/view_content.tmpl b/templates/repo/issue/view_content.tmpl
index 7e6816613d..e7d25ed8a3 100644
--- a/templates/repo/issue/view_content.tmpl
+++ b/templates/repo/issue/view_content.tmpl
@@ -20,8 +20,8 @@
 				</a>
 				{{end}}
 				<div class="content comment-container">
-					<div class="ui top attached header comment-header gt-df gt-ac gt-sb" role="heading" aria-level="3">
-						<div class="comment-header-left gt-df gt-ac">
+					<div class="ui top attached header comment-header tw-flex tw-content-center tw-justify-between" role="heading" aria-level="3">
+						<div class="comment-header-left tw-flex tw-content-center">
 							{{if .Issue.OriginalAuthor}}
 								<span class="text black gt-font-semibold">
 									{{svg (MigrationIcon .Repository.GetOriginalURLHostname)}}
@@ -43,7 +43,7 @@
 								</span>
 							{{end}}
 						</div>
-						<div class="comment-header-right actions gt-df gt-ac">
+						<div class="comment-header-right actions tw-flex tw-content-center">
 							{{template "repo/issue/view_content/show_role" dict "ShowRole" .Issue.ShowRole "IgnorePoster" true}}
 							{{if not $.Repository.IsArchived}}
 								{{template "repo/issue/view_content/add_reaction" dict "ctxData" $ "ActionURL" (printf "%s/issues/%d/reactions" $.RepoLink .Issue.Index)}}
diff --git a/templates/repo/issue/view_content/attachments.tmpl b/templates/repo/issue/view_content/attachments.tmpl
index 2c3a47d670..151131366f 100644
--- a/templates/repo/issue/view_content/attachments.tmpl
+++ b/templates/repo/issue/view_content/attachments.tmpl
@@ -4,8 +4,8 @@
 	{{end}}
 	{{$hasThumbnails := false}}
 	{{- range .Attachments -}}
-		<div class="gt-df">
-			<div class="gt-f1 gt-p-3">
+		<div class="tw-flex">
+			<div class="tw-flex-1 gt-p-3">
 				<a target="_blank" rel="noopener noreferrer" href="{{.DownloadURL}}" title="{{ctx.Locale.Tr "repo.issues.attachment.open_tab" .Name}}">
 					{{if FilenameIsImage .Name}}
 						{{if not (StringUtils.Contains (StringUtils.ToString $.RenderedContent) .UUID)}}
@@ -18,7 +18,7 @@
 					<span><strong>{{.Name}}</strong></span>
 				</a>
 			</div>
-			<div class="gt-p-3 gt-df gt-ac">
+			<div class="gt-p-3 tw-flex tw-content-center">
 				<span class="ui text grey">{{.Size | FileSize}}</span>
 			</div>
 		</div>
diff --git a/templates/repo/issue/view_content/comments.tmpl b/templates/repo/issue/view_content/comments.tmpl
index 6e585f0280..b593eb8b4a 100644
--- a/templates/repo/issue/view_content/comments.tmpl
+++ b/templates/repo/issue/view_content/comments.tmpl
@@ -25,8 +25,8 @@
 				</a>
 			{{end}}
 				<div class="content comment-container">
-					<div class="ui top attached header comment-header gt-df gt-ac gt-sb" role="heading" aria-level="3">
-						<div class="comment-header-left gt-df gt-ac">
+					<div class="ui top attached header comment-header tw-flex tw-content-center tw-justify-between" role="heading" aria-level="3">
+						<div class="comment-header-left tw-flex tw-content-center">
 							{{if .OriginalAuthor}}
 								<span class="text black gt-font-semibold gt-mr-2">
 									{{svg (MigrationIcon $.Repository.GetOriginalURLHostname)}}
@@ -50,7 +50,7 @@
 								</span>
 							{{end}}
 						</div>
-						<div class="comment-header-right actions gt-df gt-ac">
+						<div class="comment-header-right actions tw-flex tw-content-center">
 							{{template "repo/issue/view_content/show_role" dict "ShowRole" .ShowRole}}
 							{{if not $.Repository.IsArchived}}
 								{{template "repo/issue/view_content/add_reaction" dict "ctxData" $ "ActionURL" (printf "%s/comments/%d/reactions" $.RepoLink .ID)}}
@@ -403,8 +403,8 @@
 				{{if or .Content .Attachments}}
 				<div class="timeline-item comment">
 					<div class="content comment-container">
-						<div class="ui top attached header comment-header gt-df gt-ac gt-sb">
-							<div class="comment-header-left gt-df gt-ac">
+						<div class="ui top attached header comment-header tw-flex tw-content-center tw-justify-between">
+							<div class="comment-header-left tw-flex tw-content-center">
 								{{if gt .Poster.ID 0}}
 									<a class="inline-timeline-avatar" href="{{.Poster.HomeLink}}">
 										{{ctx.AvatarUtils.Avatar .Poster 24}}
@@ -425,7 +425,7 @@
 									{{ctx.Locale.Tr "repo.issues.review.left_comment"}}
 								</span>
 							</div>
-							<div class="comment-header-right actions gt-df gt-ac">
+							<div class="comment-header-right actions tw-flex tw-content-center">
 								{{template "repo/issue/view_content/show_role" dict "ShowRole" .ShowRole}}
 								{{if not $.Repository.IsArchived}}
 									{{template "repo/issue/view_content/add_reaction" dict "ctxData" $ "ActionURL" (printf "%s/comments/%d/reactions" $.RepoLink .ID)}}
@@ -622,7 +622,7 @@
 				{{if .Content}}
 					<div class="timeline-item comment">
 						<div class="content">
-							<div class="ui top attached header comment-header-left gt-df gt-ac arrow-top">
+							<div class="ui top attached header comment-header-left tw-flex tw-content-center arrow-top">
 								{{if gt .Poster.ID 0}}
 									<a class="inline-timeline-avatar" href="{{.Poster.HomeLink}}">
 										{{ctx.AvatarUtils.Avatar .Poster 24}}
diff --git a/templates/repo/issue/view_content/pull.tmpl b/templates/repo/issue/view_content/pull.tmpl
index 23257b5a65..19f1d3f91d 100644
--- a/templates/repo/issue/view_content/pull.tmpl
+++ b/templates/repo/issue/view_content/pull.tmpl
@@ -33,7 +33,7 @@
 		<div class="ui attached merge-section segment {{if not $.LatestCommitStatus}}no-header{{end}} flex-items-block">
 			{{if .Issue.PullRequest.HasMerged}}
 				{{if .IsPullBranchDeletable}}
-					<div class="item item-section text gt-f1">
+					<div class="item item-section text tw-flex-1">
 						<div class="item-section-left">
 							<h3 class="gt-mb-3">
 								{{ctx.Locale.Tr "repo.pulls.merged_success"}}
@@ -48,7 +48,7 @@
 					</div>
 				{{end}}
 			{{else if .Issue.IsClosed}}
-				<div class="item item-section text gt-f1">
+				<div class="item item-section text tw-flex-1">
 					<div class="item-section-left">
 						<h3 class="gt-mb-3">{{ctx.Locale.Tr "repo.pulls.closed"}}</h3>
 						<div class="merge-section-info">
@@ -82,7 +82,7 @@
 				</div>
 			{{else if .IsPullWorkInProgress}}
 				<div class="item toggle-wip" data-title="{{.Issue.Title}}" data-wip-prefix="{{.WorkInProgressPrefix}}" data-update-url="{{.Issue.Link}}/title">
-					<div class="item-section-left flex-text-inline gt-f1">
+					<div class="item-section-left flex-text-inline tw-flex-1">
 						{{svg "octicon-x"}}
 						{{ctx.Locale.Tr "repo.pulls.cannot_merge_work_in_progress"}}
 					</div>
diff --git a/templates/repo/issue/view_content/sidebar.tmpl b/templates/repo/issue/view_content/sidebar.tmpl
index 9dd49fd7e7..3e3ca41922 100644
--- a/templates/repo/issue/view_content/sidebar.tmpl
+++ b/templates/repo/issue/view_content/sidebar.tmpl
@@ -3,7 +3,7 @@
 	{{if .Issue.IsPull}}
 		<input id="reviewer_id" name="reviewer_id" type="hidden" value="{{.reviewer_id}}">
 		<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">
+			<a class="text tw-flex tw-content-center muted">
 				<strong>{{ctx.Locale.Tr "repo.issues.review.reviewers"}}</strong>
 				{{if and .CanChooseReviewer (not .Repository.IsArchived)}}
 					{{svg "octicon-gear" 16 "gt-ml-2"}}
@@ -50,17 +50,17 @@
 			<span class="no-select item {{if or .OriginalReviews .PullReviewers}}gt-hidden{{end}}">{{ctx.Locale.Tr "repo.issues.new.no_reviewers"}}</span>
 			<div class="selected">
 				{{range .PullReviewers}}
-					<div class="item gt-df gt-ac gt-py-3">
-						<div class="gt-df gt-ac gt-f1">
+					<div class="item tw-flex tw-content-center gt-py-3">
+						<div class="tw-flex tw-content-center tw-flex-1">
 							{{if .User}}
 								<a class="muted sidebar-item-link" href="{{.User.HomeLink}}">{{ctx.AvatarUtils.Avatar .User 20 "gt-mr-3"}}{{.User.GetDisplayName}}</a>
 							{{else if .Team}}
 								<span class="text">{{svg "octicon-people" 20 "gt-mr-3"}}{{$.Issue.Repo.OwnerName}}/{{.Team.Name}}</span>
 							{{end}}
 						</div>
-						<div class="gt-df gt-ac gt-gap-3">
+						<div class="tw-flex tw-content-center gt-gap-3">
 							{{if (and $.Permission.IsAdmin (or (eq .Review.Type 1) (eq .Review.Type 3)) (not $.Issue.IsClosed))}}
-								<a href="#" class="ui muted icon gt-df gt-ac show-modal" data-tooltip-content="{{ctx.Locale.Tr "repo.issues.dismiss_review"}}" data-modal="#dismiss-review-modal-{{.Review.ID}}">
+								<a href="#" class="ui muted icon tw-flex tw-content-center show-modal" data-tooltip-content="{{ctx.Locale.Tr "repo.issues.dismiss_review"}}" data-modal="#dismiss-review-modal-{{.Review.ID}}">
 									{{svg "octicon-x" 20}}
 								</a>
 								<div class="ui small modal" id="dismiss-review-modal-{{.Review.ID}}">
@@ -99,14 +99,14 @@
 					</div>
 				{{end}}
 				{{range .OriginalReviews}}
-					<div class="item gt-df gt-ac gt-py-3">
-						<div class="gt-df gt-ac gt-f1">
+					<div class="item tw-flex tw-content-center gt-py-3">
+						<div class="tw-flex tw-content-center tw-flex-1">
 							<a class="muted" href="{{$.Repository.OriginalURL}}" data-tooltip-content="{{ctx.Locale.Tr "repo.migrated_from_fake" $.Repository.GetOriginalURLHostname}}">
 								{{svg (MigrationIcon $.Repository.GetOriginalURLHostname) 20 "gt-mr-3"}}
 								{{.OriginalAuthor}}
 							</a>
 						</div>
-						<div class="gt-df gt-ac gt-gap-3">
+						<div class="tw-flex tw-content-center gt-gap-3">
 							{{svg (printf "octicon-%s" .Type.Icon) 16 (printf "text %s" (.HTMLTypeColorName))}}
 						</div>
 					</div>
@@ -264,7 +264,7 @@
 
 	{{if .Participants}}
 		<span class="text"><strong>{{ctx.Locale.Tr "repo.issues.num_participants" .NumParticipants}}</strong></span>
-		<div class="ui list gt-df gt-fw">
+		<div class="ui list tw-flex tw-flex-wrap">
 			{{range .Participants}}
 				<a {{if gt .ID 0}}href="{{.HomeLink}}"{{end}} data-tooltip-content="{{.GetDisplayName}}">
 					{{ctx.AvatarUtils.Avatar . 28 "gt-my-1 gt-mr-2"}}
@@ -368,7 +368,7 @@
 		</div>
 		{{if ne .Issue.DeadlineUnix 0}}
 			<p>
-				<div class="gt-df gt-sb gt-ac">
+				<div class="tw-flex tw-justify-between tw-content-center">
 					<div class="due-date {{if .Issue.IsOverdue}}text red{{end}}" {{if .Issue.IsOverdue}}data-tooltip-content="{{ctx.Locale.Tr "repo.issues.due_date_overdue"}}"{{end}}>
 						{{svg "octicon-calendar" 16 "gt-mr-3"}}
 						{{DateTime "long" .Issue.DeadlineUnix.FormatDate}}
@@ -424,8 +424,8 @@
 				</span>
 				<div class="ui relaxed divided list">
 					{{range .BlockingDependencies}}
-						<div class="item dependency{{if .Issue.IsClosed}} is-closed{{end}} gt-df gt-ac gt-sb">
-							<div class="item-left gt-df gt-jc gt-fc gt-f1 gt-ellipsis">
+						<div class="item dependency{{if .Issue.IsClosed}} is-closed{{end}} tw-flex tw-content-center tw-justify-between">
+							<div class="item-left tw-flex tw-justify-center tw-flex-col tw-flex-1 gt-ellipsis">
 								<a class="title muted" href="{{.Issue.Link}}" data-tooltip-content="#{{.Issue.Index}} {{.Issue.Title | RenderEmoji $.Context}}">
 									#{{.Issue.Index}} {{.Issue.Title | RenderEmoji $.Context}}
 								</a>
@@ -433,7 +433,7 @@
 									{{.Repository.OwnerName}}/{{.Repository.Name}}
 								</div>
 							</div>
-							<div class="item-right gt-df gt-ac gt-m-2">
+							<div class="item-right tw-flex tw-content-center gt-m-2">
 								{{if and $.CanCreateIssueDependencies (not $.Repository.IsArchived)}}
 									<a class="delete-dependency-button ci muted" data-id="{{.Issue.ID}}" data-type="blocking" data-tooltip-content="{{ctx.Locale.Tr "repo.issues.dependency.remove_info"}}">
 										{{svg "octicon-trash" 16}}
@@ -443,7 +443,7 @@
 						</div>
 					{{end}}
 					{{if .BlockingDependenciesNotPermitted}}
-						<div class="item gt-df gt-ac gt-sb gt-ellipsis">
+						<div class="item tw-flex tw-content-center tw-justify-between gt-ellipsis">
 							<span>{{ctx.Locale.TrN (len .BlockingDependenciesNotPermitted) "repo.issues.dependency.no_permission_1" "repo.issues.dependency.no_permission_n" (len .BlockingDependenciesNotPermitted)}}</span>
 						</div>
 					{{end}}
@@ -456,8 +456,8 @@
 				</span>
 				<div class="ui relaxed divided list">
 					{{range .BlockedByDependencies}}
-						<div class="item dependency{{if .Issue.IsClosed}} is-closed{{end}} gt-df gt-ac gt-sb">
-							<div class="item-left gt-df gt-jc gt-fc gt-f1 gt-ellipsis">
+						<div class="item dependency{{if .Issue.IsClosed}} is-closed{{end}} tw-flex tw-content-center tw-justify-between">
+							<div class="item-left tw-flex tw-justify-center tw-flex-col tw-flex-1 gt-ellipsis">
 								<a class="title muted" href="{{.Issue.Link}}" data-tooltip-content="#{{.Issue.Index}} {{.Issue.Title | RenderEmoji $.Context}}">
 									#{{.Issue.Index}} {{.Issue.Title | RenderEmoji $.Context}}
 								</a>
@@ -465,7 +465,7 @@
 									{{.Repository.OwnerName}}/{{.Repository.Name}}
 								</div>
 							</div>
-							<div class="item-right gt-df gt-ac gt-m-2">
+							<div class="item-right tw-flex tw-content-center gt-m-2">
 								{{if and $.CanCreateIssueDependencies (not $.Repository.IsArchived)}}
 									<a class="delete-dependency-button ci muted" data-id="{{.Issue.ID}}" data-type="blockedBy" data-tooltip-content="{{ctx.Locale.Tr "repo.issues.dependency.remove_info"}}">
 										{{svg "octicon-trash" 16}}
@@ -476,8 +476,8 @@
 					{{end}}
 					{{if $.CanCreateIssueDependencies}}
 						{{range .BlockedByDependenciesNotPermitted}}
-							<div class="item dependency{{if .Issue.IsClosed}} is-closed{{end}} gt-df gt-ac gt-sb">
-								<div class="item-left gt-df gt-jc gt-fc gt-f1 gt-ellipsis">
+							<div class="item dependency{{if .Issue.IsClosed}} is-closed{{end}} tw-flex tw-content-center tw-justify-between">
+								<div class="item-left tw-flex tw-justify-center tw-flex-col tw-flex-1 gt-ellipsis">
 									<div class="gt-ellipsis">
 										<span data-tooltip-content="{{ctx.Locale.Tr "repo.issues.dependency.no_permission.can_remove"}}">{{svg "octicon-lock" 16}}</span>
 										<span class="title" data-tooltip-content="#{{.Issue.Index}} {{.Issue.Title | RenderEmoji $.Context}}">
@@ -488,7 +488,7 @@
 										{{.Repository.OwnerName}}/{{.Repository.Name}}
 									</div>
 								</div>
-								<div class="item-right gt-df gt-ac gt-m-2">
+								<div class="item-right tw-flex tw-content-center gt-m-2">
 									{{if and $.CanCreateIssueDependencies (not $.Repository.IsArchived)}}
 										<a class="delete-dependency-button ci muted" data-id="{{.Issue.ID}}" data-type="blocking" data-tooltip-content="{{ctx.Locale.Tr "repo.issues.dependency.remove_info"}}">
 											{{svg "octicon-trash" 16}}
@@ -498,7 +498,7 @@
 							</div>
 						{{end}}
 					{{else if .BlockedByDependenciesNotPermitted}}
-						<div class="item gt-df gt-ac gt-sb gt-ellipsis">
+						<div class="item tw-flex tw-content-center tw-justify-between gt-ellipsis">
 							<span>{{ctx.Locale.TrN (len .BlockedByDependenciesNotPermitted) "repo.issues.dependency.no_permission_1" "repo.issues.dependency.no_permission_n" (len .BlockedByDependenciesNotPermitted)}}</span>
 						</div>
 					{{end}}
@@ -555,7 +555,7 @@
 	<div class="divider"></div>
 	<div class="ui equal width compact grid">
 		{{$issueReferenceLink := printf "%s#%d" .Issue.Repo.FullName .Issue.Index}}
-		<div class="row gt-ac" data-tooltip-content="{{$issueReferenceLink}}">
+		<div class="row tw-content-center" data-tooltip-content="{{$issueReferenceLink}}">
 			<span class="text column truncate">{{ctx.Locale.Tr "repo.issues.reference_link" $issueReferenceLink}}</span>
 			<button class="ui two wide button column gt-p-3" data-clipboard-text="{{$issueReferenceLink}}">{{svg "octicon-copy" 14}}</button>
 		</div>
diff --git a/templates/repo/issue/view_content/update_branch_by_merge.tmpl b/templates/repo/issue/view_content/update_branch_by_merge.tmpl
index 4dbefefe00..adce052dee 100644
--- a/templates/repo/issue/view_content/update_branch_by_merge.tmpl
+++ b/templates/repo/issue/view_content/update_branch_by_merge.tmpl
@@ -7,7 +7,7 @@
 		</div>
 		<div class="item-section-right">
 			{{if and $.UpdateAllowed $.UpdateByRebaseAllowed}}
-				<div class="gt-dib">
+				<div class="tw-inline-block">
 					<div class="ui buttons update-button">
 						<button class="ui button" data-do="{{$.Link}}/update" data-redirect="{{$.Link}}">
 							<span class="button-text">
diff --git a/templates/repo/issue/view_title.tmpl b/templates/repo/issue/view_title.tmpl
index 764a8b01ab..f65a4ee085 100644
--- a/templates/repo/issue/view_title.tmpl
+++ b/templates/repo/issue/view_title.tmpl
@@ -8,7 +8,7 @@
 		<h1 class="gt-word-break">
 			<span id="issue-title">{{RenderIssueTitle $.Context .Issue.Title ($.Repository.ComposeMetas ctx) | RenderCodeBlock}} <span class="index">#{{.Issue.Index}}</span>
 </span>
-			<div id="edit-title-input" class="ui input gt-f1 gt-hidden">
+			<div id="edit-title-input" class="ui input tw-flex-1 gt-hidden">
 				<input value="{{.Issue.Title}}" maxlength="255" autocomplete="off">
 			</div>
 		</h1>
diff --git a/templates/repo/migrate/migrate.tmpl b/templates/repo/migrate/migrate.tmpl
index 8ba567ee6b..c0336b9b97 100644
--- a/templates/repo/migrate/migrate.tmpl
+++ b/templates/repo/migrate/migrate.tmpl
@@ -5,7 +5,7 @@
 			{{template "repo/migrate/helper" .}}
 			<div class="ui cards migrate-entries">
 				{{range .Services}}
-					<a class="ui card migrate-entry gt-df gt-ac" href="{{AppSubUrl}}/repo/migrate?service_type={{.}}&org={{$.Org}}&mirror={{$.Mirror}}">
+					<a class="ui card migrate-entry tw-flex tw-content-center" href="{{AppSubUrl}}/repo/migrate?service_type={{.}}&org={{$.Org}}&mirror={{$.Mirror}}">
 						{{if eq .Name "github"}}
 							{{svg "octicon-mark-github" 184 "gt-p-4"}}
 						{{else if eq .Name "gitlab"}}
diff --git a/templates/repo/projects/view.tmpl b/templates/repo/projects/view.tmpl
index 377a7ff79f..b227ce4439 100644
--- a/templates/repo/projects/view.tmpl
+++ b/templates/repo/projects/view.tmpl
@@ -2,7 +2,7 @@
 <div role="main" aria-label="{{.Title}}" class="page-content repository projects view-project">
 	{{template "repo/header" .}}
 	<div class="ui container padded">
-		<div class="gt-df gt-sb gt-ac gt-mb-4">
+		<div class="tw-flex tw-justify-between tw-content-center gt-mb-4">
 			{{template "repo/issue/navbar" .}}
 			<a class="ui small primary button" href="{{.RepoLink}}/issues/new/choose?project={{.Project.ID}}">{{ctx.Locale.Tr "repo.issues.new"}}</a>
 		</div>
diff --git a/templates/repo/pulls/fork.tmpl b/templates/repo/pulls/fork.tmpl
index f0907f409b..2cf0a85fc8 100644
--- a/templates/repo/pulls/fork.tmpl
+++ b/templates/repo/pulls/fork.tmpl
@@ -37,7 +37,7 @@
 
 					<div class="inline field">
 						<label>{{ctx.Locale.Tr "repo.fork_from"}}</label>
-						<a href="{{.ForkRepo.Link}}" class="gt-dib">{{.ForkRepo.FullName}}</a>
+						<a href="{{.ForkRepo.Link}}" class="tw-inline-block">{{.ForkRepo.FullName}}</a>
 					</div>
 					<div class="inline required field {{if .Err_RepoName}}error{{end}}">
 						<label for="repo_name">{{ctx.Locale.Tr "repo.repo_name"}}</label>
diff --git a/templates/repo/pulls/tab_menu.tmpl b/templates/repo/pulls/tab_menu.tmpl
index 340b1bb397..fb00acde32 100644
--- a/templates/repo/pulls/tab_menu.tmpl
+++ b/templates/repo/pulls/tab_menu.tmpl
@@ -15,7 +15,7 @@
 			{{ctx.Locale.Tr "repo.pulls.tab_files"}}
 			<span class="ui small label">{{if .NumFiles}}{{.NumFiles}}{{else}}-{{end}}</span>
 		</a>
-		<span class="item tw-ml-auto gt-pr-0 gt-font-bold gt-df gt-ac gt-gap-3">
+		<span class="item tw-ml-auto gt-pr-0 gt-font-bold tw-flex tw-content-center gt-gap-3">
 			<span><span class="text green">{{if .Diff.TotalAddition}}+{{.Diff.TotalAddition}}{{end}}</span> <span class="text red">{{if .Diff.TotalDeletion}}-{{.Diff.TotalDeletion}}{{end}}</span></span>
 			<span class="diff-stats-bar">
 				<div class="diff-stats-add-bar" style="width: {{Eval 100 "*" .Diff.TotalAddition "/" "(" .Diff.TotalAddition "+" .Diff.TotalDeletion "+" 0.0 ")"}}%"></div>
diff --git a/templates/repo/release/new.tmpl b/templates/repo/release/new.tmpl
index 30e783167c..fd6338a701 100644
--- a/templates/repo/release/new.tmpl
+++ b/templates/repo/release/new.tmpl
@@ -21,7 +21,7 @@
 					{{else}}
 						<input id="tag-name" name="tag_name" value="{{.tag_name}}" aria-label="{{ctx.Locale.Tr "repo.release.tag_name"}}" placeholder="{{ctx.Locale.Tr "repo.release.tag_name"}}" autofocus required maxlength="255">
 						<input id="tag-name-editor" type="hidden" data-existing-tags="{{JsonUtils.EncodeToString .Tags}}" data-tag-helper="{{ctx.Locale.Tr "repo.release.tag_helper"}}" data-tag-helper-new="{{ctx.Locale.Tr "repo.release.tag_helper_new"}}" data-tag-helper-existing="{{ctx.Locale.Tr "repo.release.tag_helper_existing"}}">
-						<div id="tag-target-selector" class="gt-dib">
+						<div id="tag-target-selector" class="tw-inline-block">
 							<span class="at">@</span>
 							<div class="ui selection dropdown">
 								<input type="hidden" name="tag_target" value="{{.tag_target}}">
@@ -61,7 +61,7 @@
 				</div>
 				{{range .attachments}}
 					<div class="field flex-text-block" id="attachment-{{.ID}}">
-						<div class="flex-text-inline gt-f1">
+						<div class="flex-text-inline tw-flex-1">
 							<input name="attachment-edit-{{.UUID}}"  class="attachment_edit" required value="{{.Name}}">
 							<input name="attachment-del-{{.UUID}}" type="hidden" value="false">
 							<span class="ui text grey tw-whitespace-nowrap">{{.Size | FileSize}}</span>
@@ -101,7 +101,7 @@
 					</div>
 					<span class="help">{{ctx.Locale.Tr "repo.release.prerelease_helper"}}</span>
 					<div class="divider gt-mt-0"></div>
-					<div class="gt-df gt-je">
+					<div class="tw-flex tw-justify-end">
 						{{if .PageIsEditRelease}}
 							<a class="ui small button" href="{{.RepoLink}}/releases">
 								{{ctx.Locale.Tr "repo.release.cancel"}}
diff --git a/templates/repo/release_tag_header.tmpl b/templates/repo/release_tag_header.tmpl
index fe978a9680..1df1b5ba50 100644
--- a/templates/repo/release_tag_header.tmpl
+++ b/templates/repo/release_tag_header.tmpl
@@ -2,8 +2,8 @@
 {{$canReadCode := $.Permission.CanRead $.UnitTypeCode}}
 
 {{if $canReadReleases}}
-	<div class="gt-df">
-		<div class="gt-f1 gt-df gt-ac">
+	<div class="tw-flex">
+		<div class="tw-flex-1 tw-flex tw-content-center">
 			<h2 class="ui compact small menu header small-menu-items">
 				<a class="{{if and .PageIsReleaseList (not .PageIsSingleTag)}}active {{end}}item" href="{{.RepoLink}}/releases">{{ctx.Locale.PrettyNumber .NumReleases}} {{ctx.Locale.TrN .NumReleases "repo.release" "repo.releases"}}</a>
 				{{if $canReadCode}}
diff --git a/templates/repo/settings/branches.tmpl b/templates/repo/settings/branches.tmpl
index 73aff887f3..2610ae02fe 100644
--- a/templates/repo/settings/branches.tmpl
+++ b/templates/repo/settings/branches.tmpl
@@ -12,11 +12,11 @@
 				<p>
 					{{ctx.Locale.Tr "repo.settings.default_branch_desc"}}
 				</p>
-				<form class="gt-df" action="{{.Link}}" method="post">
+				<form class="tw-flex" action="{{.Link}}" method="post">
 					{{.CsrfTokenHtml}}
 					<input type="hidden" name="action" value="default_branch">
 					{{if not .Repository.IsEmpty}}
-						<div class="ui dropdown selection search gt-f1 gt-mr-3 tw-max-w-96">
+						<div class="ui dropdown selection search tw-flex-1 gt-mr-3 tw-max-w-96">
 							{{svg "octicon-triangle-down" 14 "dropdown icon"}}
 							<input type="hidden" name="branch" value="{{.Repository.DefaultBranch}}">
 							<div class="default text">{{.Repository.DefaultBranch}}</div>
@@ -41,7 +41,7 @@
 			<div class="ui attached segment">
 				<div class="flex-list">
 					{{range .ProtectedBranches}}
-						<div class="flex-item gt-ac">
+						<div class="flex-item tw-content-center">
 							<div class="flex-item-main">
 								<div class="flex-item-title">
 									<div class="ui basic primary label">{{.RuleName}}</div>
diff --git a/templates/repo/settings/collaboration.tmpl b/templates/repo/settings/collaboration.tmpl
index d7b5c96bab..8783de2544 100644
--- a/templates/repo/settings/collaboration.tmpl
+++ b/templates/repo/settings/collaboration.tmpl
@@ -7,7 +7,7 @@
 		<div class="ui attached segment">
 			<div class="flex-list">
 				{{range .Collaborators}}
-					<div class="flex-item gt-ac">
+					<div class="flex-item tw-content-center">
 						<div class="flex-item-leading">
 							<a href="{{.HomeLink}}">{{ctx.AvatarUtils.Avatar . 32}}</a>
 						</div>
@@ -41,7 +41,7 @@
 		<div class="ui bottom attached segment">
 			<form class="ui form" id="repo-collab-form" action="{{.Link}}" method="post">
 				{{.CsrfTokenHtml}}
-				<div id="search-user-box" class="ui search input gt-vm">
+				<div id="search-user-box" class="ui search input tw-align-middle">
 					<input class="prompt" name="collaborator" placeholder="{{ctx.Locale.Tr "search.team_kind"}}" autocomplete="off" autofocus required>
 				</div>
 				<button class="ui primary button">{{ctx.Locale.Tr "repo.settings.add_collaborator"}}</button>
@@ -89,7 +89,7 @@
 			{{if $allowedToChangeTeams}}
 				<form class="ui form" id="repo-collab-team-form" action="{{.Link}}/team" method="post">
 					{{.CsrfTokenHtml}}
-					<div id="search-team-box" class="ui search input gt-vm" data-org-name="{{.OrgName}}">
+					<div id="search-team-box" class="ui search input tw-align-middle" data-org-name="{{.OrgName}}">
 						<input class="prompt" name="team" placeholder="{{ctx.Locale.Tr "search.team_kind"}}" autocomplete="off" autofocus required>
 					</div>
 					<button class="ui primary button">{{ctx.Locale.Tr "repo.settings.add_team"}}</button>
diff --git a/templates/repo/settings/githooks.tmpl b/templates/repo/settings/githooks.tmpl
index 3fce29d545..3d15d097cc 100644
--- a/templates/repo/settings/githooks.tmpl
+++ b/templates/repo/settings/githooks.tmpl
@@ -11,7 +11,7 @@
 				{{range .Hooks}}
 					<div class="item truncated-item-container">
 						<span class="text {{if .IsActive}}green{{else}}grey{{end}} gt-mr-3">{{svg "octicon-dot-fill" 22}}</span>
-						<span class="text truncate gt-f1 gt-mr-3">{{.Name}}</span>
+						<span class="text truncate tw-flex-1 gt-mr-3">{{.Name}}</span>
 						<a class="muted tw-float-right gt-p-3" href="{{$.RepoLink}}/settings/hooks/git/{{.Name|PathEscape}}">
 							{{svg "octicon-pencil"}}
 						</a>
diff --git a/templates/repo/settings/options.tmpl b/templates/repo/settings/options.tmpl
index a2947701a0..4f505bdf02 100644
--- a/templates/repo/settings/options.tmpl
+++ b/templates/repo/settings/options.tmpl
@@ -132,7 +132,7 @@
 								<td>{{ctx.Locale.Tr "repo.settings.mirror_settings.direction.pull"}}</td>
 								<td>{{DateTime "full" .PullMirror.UpdatedUnix}}</td>
 								<td class="right aligned">
-									<form method="post" class="gt-dib">
+									<form method="post" class="tw-inline-block">
 										{{.CsrfTokenHtml}}
 										<input type="hidden" name="action" value="mirror-sync">
 										<button class="ui primary tiny button inline text-thin">{{ctx.Locale.Tr "repo.settings.sync_mirror"}}</button>
@@ -230,13 +230,13 @@
 									>
 										{{svg "octicon-pencil" 14}}
 									</button>
-									<form method="post" class="gt-dib">
+									<form method="post" class="tw-inline-block">
 										{{$.CsrfTokenHtml}}
 										<input type="hidden" name="action" value="push-mirror-sync">
 										<input type="hidden" name="push_mirror_id" value="{{.ID}}">
 										<button class="ui primary tiny button" data-tooltip-content="{{ctx.Locale.Tr "repo.settings.sync_mirror"}}">{{svg "octicon-sync" 14}}</button>
 									</form>
-									<form method="post" class="gt-dib">
+									<form method="post" class="tw-inline-block">
 										{{$.CsrfTokenHtml}}
 										<input type="hidden" name="action" value="push-mirror-remove">
 										<input type="hidden" name="push_mirror_id" value="{{.ID}}">
@@ -492,7 +492,7 @@
 					</div>
 				</div>
 				{{if not .Repository.IsMirror}}
-					<div class="flex-item gt-ac">
+					<div class="flex-item tw-content-center">
 						<div class="flex-item-main">
 							{{if .Repository.IsArchived}}
 								<div class="flex-item-title">{{ctx.Locale.Tr "repo.settings.unarchive.header"}}</div>
diff --git a/templates/repo/settings/tags.tmpl b/templates/repo/settings/tags.tmpl
index 4c196f0f99..c9efb7b67e 100644
--- a/templates/repo/settings/tags.tmpl
+++ b/templates/repo/settings/tags.tmpl
@@ -106,7 +106,7 @@
 										</td>
 										<td class="right aligned">
 											<a class="ui tiny primary button" href="{{$.RepoLink}}/settings/tags/{{.ID}}">{{ctx.Locale.Tr "edit"}}</a>
-											<form class="gt-dib" action="{{$.RepoLink}}/settings/tags/delete" method="post">
+											<form class="tw-inline-block" action="{{$.RepoLink}}/settings/tags/delete" method="post">
 												{{$.CsrfTokenHtml}}
 												<input type="hidden" name="id" value="{{.ID}}">
 												<button class="ui tiny red button">{{ctx.Locale.Tr "remove"}}</button>
diff --git a/templates/repo/settings/webhook/base_list.tmpl b/templates/repo/settings/webhook/base_list.tmpl
index e56929b70f..9abc03e40e 100644
--- a/templates/repo/settings/webhook/base_list.tmpl
+++ b/templates/repo/settings/webhook/base_list.tmpl
@@ -15,7 +15,7 @@
 		{{range .Webhooks}}
 			<div class="item truncated-item-container">
 				<span class="text {{if eq .LastStatus 1}}green{{else if eq .LastStatus 2}}red{{else}}grey{{end}} gt-mr-3">{{svg "octicon-dot-fill" 22}}</span>
-				<div class="text truncate gt-f1 gt-mr-3">
+				<div class="text truncate tw-flex-1 gt-mr-3">
 					<a title="{{.URL}}" href="{{$.BaseLink}}/{{.ID}}">{{.URL}}</a>
 				</div>
 				<a class="muted gt-p-3" href="{{$.BaseLink}}/{{.ID}}">{{svg "octicon-pencil"}}</a>
diff --git a/templates/repo/settings/webhook/history.tmpl b/templates/repo/settings/webhook/history.tmpl
index 9f7a7816ea..e2aee13941 100644
--- a/templates/repo/settings/webhook/history.tmpl
+++ b/templates/repo/settings/webhook/history.tmpl
@@ -17,7 +17,7 @@
 		<div class="ui list">
 			{{range .History}}
 				<div class="item">
-					<div class="flex-text-block gt-sb">
+					<div class="flex-text-block tw-justify-between">
 						<div class="flex-text-inline">
 							{{if .IsSucceed}}
 								<span class="text green">{{svg "octicon-check"}}</span>
diff --git a/templates/repo/tag/list.tmpl b/templates/repo/tag/list.tmpl
index 9f0676e395..0348334623 100644
--- a/templates/repo/tag/list.tmpl
+++ b/templates/repo/tag/list.tmpl
@@ -5,7 +5,7 @@
 		{{template "base/alert" .}}
 		{{template "repo/release_tag_header" .}}
 		<h4 class="ui top attached header">
-			<div class="five wide column gt-df gt-ac">
+			<div class="five wide column tw-flex tw-content-center">
 				{{svg "octicon-tag" 16 "gt-mr-2"}}{{ctx.Locale.Tr "repo.release.tags"}}
 			</div>
 		</h4>
@@ -18,12 +18,12 @@
 							<td class="tag">
 								<h3 class="release-tag-name gt-mb-3">
 									{{if $canReadReleases}}
-										<a class="gt-df gt-ac" href="{{$.RepoLink}}/releases/tag/{{.TagName | PathEscapeSegments}}" rel="nofollow">{{.TagName}}</a>
+										<a class="tw-flex tw-content-center" href="{{$.RepoLink}}/releases/tag/{{.TagName | PathEscapeSegments}}" rel="nofollow">{{.TagName}}</a>
 									{{else}}
-										<a class="gt-df gt-ac" href="{{$.RepoLink}}/src/tag/{{.TagName | PathEscapeSegments}}" rel="nofollow">{{.TagName}}</a>
+										<a class="tw-flex tw-content-center" href="{{$.RepoLink}}/src/tag/{{.TagName | PathEscapeSegments}}" rel="nofollow">{{.TagName}}</a>
 									{{end}}
 								</h3>
-								<div class="download gt-df gt-ac">
+								<div class="download tw-flex tw-content-center">
 									{{if $.Permission.CanRead $.UnitTypeCode}}
 										{{if .CreatedUnix}}
 											<span class="gt-mr-3">{{svg "octicon-clock" 16 "gt-mr-2"}}{{TimeSinceUnix .CreatedUnix ctx.Locale}}</span>
diff --git a/templates/repo/view_file.tmpl b/templates/repo/view_file.tmpl
index 9d116c7b46..c99b193ef0 100644
--- a/templates/repo/view_file.tmpl
+++ b/templates/repo/view_file.tmpl
@@ -25,8 +25,8 @@
 		</div>
 	{{end}}
 
-	<h4 class="file-header ui top attached header gt-df gt-ac gt-sb gt-fw">
-		<div class="file-header-left gt-df gt-ac gt-py-3 gt-pr-4">
+	<h4 class="file-header ui top attached header tw-flex tw-content-center tw-justify-between tw-flex-wrap">
+		<div class="file-header-left tw-flex tw-content-center gt-py-3 gt-pr-4">
 			{{if .ReadmeInList}}
 				{{svg "octicon-book" 16 "gt-mr-3"}}
 				<strong><a class="default-link muted" href="#readme">{{.FileName}}</a></strong>
@@ -34,7 +34,7 @@
 				{{template "repo/file_info" .}}
 			{{end}}
 		</div>
-		<div class="file-header-right file-actions gt-df gt-ac gt-fw">
+		<div class="file-header-right file-actions tw-flex tw-content-center tw-flex-wrap">
 			{{if .HasSourceRenderedToggle}}
 				<div class="ui compact icon buttons">
 					<a href="?display=source" class="ui mini basic button {{if .IsDisplayingSource}}active{{end}}" data-tooltip-content="{{ctx.Locale.Tr "repo.file_view_source"}}">{{svg "octicon-code" 15}}</a>
diff --git a/templates/repo/wiki/new.tmpl b/templates/repo/wiki/new.tmpl
index 118dc7c8f0..0e929f24eb 100644
--- a/templates/repo/wiki/new.tmpl
+++ b/templates/repo/wiki/new.tmpl
@@ -3,7 +3,7 @@
 	{{template "repo/header" .}}
 	<div class="ui container">
 		{{template "base/alert" .}}
-		<div class="ui header flex-text-block gt-sb">
+		<div class="ui header flex-text-block tw-justify-between">
 			{{ctx.Locale.Tr "repo.wiki.new_page"}}
 			{{if .PageIsWikiEdit}}
 				<a class="ui tiny primary button" href="{{.RepoLink}}/wiki?action=_new">{{ctx.Locale.Tr "repo.wiki.new_page_button"}}</a>
diff --git a/templates/repo/wiki/pages.tmpl b/templates/repo/wiki/pages.tmpl
index 22eb2619f9..646c6a3d36 100644
--- a/templates/repo/wiki/pages.tmpl
+++ b/templates/repo/wiki/pages.tmpl
@@ -2,7 +2,7 @@
 <div role="main" aria-label="{{.Title}}" class="page-content repository wiki pages">
 	{{template "repo/header" .}}
 	<div class="ui container">
-		<h2 class="ui header gt-df gt-ac gt-sb">
+		<h2 class="ui header tw-flex tw-content-center tw-justify-between">
 			<span>{{ctx.Locale.Tr "repo.wiki.pages"}}</span>
 			<span>
 				{{if and .CanWriteWiki (not .Repository.IsMirror)}}
diff --git a/templates/shared/actions/runner_edit.tmpl b/templates/shared/actions/runner_edit.tmpl
index fbc730b288..f8bbf23b62 100644
--- a/templates/shared/actions/runner_edit.tmpl
+++ b/templates/shared/actions/runner_edit.tmpl
@@ -7,15 +7,15 @@
 			{{template "base/disable_form_autofill"}}
 			{{.CsrfTokenHtml}}
 			<div class="runner-basic-info">
-				<div class="field gt-dib gt-mr-4">
+				<div class="field tw-inline-block gt-mr-4">
 					<label>{{ctx.Locale.Tr "actions.runners.status"}}</label>
 					<span class="ui {{if .Runner.IsOnline}}green{{else}}basic{{end}} label">{{.Runner.StatusLocaleName ctx.Locale}}</span>
 				</div>
-				<div class="field gt-dib gt-mr-4">
+				<div class="field tw-inline-block gt-mr-4">
 					<label>{{ctx.Locale.Tr "actions.runners.last_online"}}</label>
 					<span>{{if .Runner.LastOnline}}{{TimeSinceUnix .Runner.LastOnline ctx.Locale}}{{else}}{{ctx.Locale.Tr "never"}}{{end}}</span>
 				</div>
-				<div class="field gt-dib gt-mr-4">
+				<div class="field tw-inline-block gt-mr-4">
 					<label>{{ctx.Locale.Tr "actions.runners.labels"}}</label>
 					<span>
 						{{range .Runner.AgentLabels}}
@@ -23,7 +23,7 @@
 						{{end}}
 					</span>
 				</div>
-				<div class="field gt-dib gt-mr-4">
+				<div class="field tw-inline-block gt-mr-4">
 					<label>{{ctx.Locale.Tr "actions.runners.owner_type"}}</label>
 					<span data-tooltip-content="{{.Runner.BelongsToOwnerName}}">{{.Runner.BelongsToOwnerType.LocaleString ctx.Locale}}</span>
 				</div>
diff --git a/templates/shared/search/code/results.tmpl b/templates/shared/search/code/results.tmpl
index 11d7fbbd6b..a22820e788 100644
--- a/templates/shared/search/code/results.tmpl
+++ b/templates/shared/search/code/results.tmpl
@@ -1,4 +1,4 @@
-<div class="flex-text-block gt-fw">
+<div class="flex-text-block tw-flex-wrap">
 	{{range $term := .SearchResultLanguages}}
 	<a class="ui {{if eq $.Language $term.Language}}primary{{end}} basic label gt-m-0"
 		href="?q={{$.Keyword}}{{if ne $.Language $term.Language}}&l={{$term.Language}}{{end}}&fuzzy={{$.IsFuzzy}}">
@@ -12,9 +12,9 @@
 	{{range $result := .SearchResults}}
 		{{$repo := or $.Repo (index $.RepoMaps .RepoID)}}
 		<div class="diff-file-box diff-box file-content non-diff-file-content repo-search-result">
-			<h4 class="ui top attached normal header gt-df gt-fw">
+			<h4 class="ui top attached normal header tw-flex tw-flex-wrap">
 				{{if not $.Repo}}
-					<span class="file gt-f1">
+					<span class="file tw-flex-1">
 						<a rel="nofollow" href="{{$repo.Link}}">{{$repo.FullName}}</a>
 						{{if $repo.IsArchived}}
 							<span class="ui basic label">{{ctx.Locale.Tr "repo.desc.archived"}}</span>
@@ -22,7 +22,7 @@
 						- {{.Filename}}
 					</span>
 				{{else}}
-					<span class="file gt-f1">{{.Filename}}</span>
+					<span class="file tw-flex-1">{{.Filename}}</span>
 				{{end}}
 				<a role="button" class="ui basic tiny button" rel="nofollow" href="{{$repo.Link}}/src/{{if $.CodeIndexerDisabled}}branch{{else}}commit{{end}}/{{$result.CommitID | PathEscape}}/{{.Filename | PathEscapeSegments}}">{{ctx.Locale.Tr "repo.diff.view_file"}}</a>
 			</h4>
diff --git a/templates/shared/searchbottom.tmpl b/templates/shared/searchbottom.tmpl
index b123b497c7..b920e10bb2 100644
--- a/templates/shared/searchbottom.tmpl
+++ b/templates/shared/searchbottom.tmpl
@@ -1,5 +1,5 @@
-<div class="ui bottom attached table segment gt-df gt-ac gt-sb">
-		<div class="gt-df gt-ac gt-ml-4">
+<div class="ui bottom attached table segment tw-flex tw-content-center tw-justify-between">
+		<div class="tw-flex tw-content-center gt-ml-4">
 			{{if .result.Language}}
 					<i class="color-icon gt-mr-3" style="background-color: {{.result.Color}}"></i>{{.result.Language}}
 			{{end}}
diff --git a/templates/shared/secrets/add_list.tmpl b/templates/shared/secrets/add_list.tmpl
index 4fbd8ddcfd..ea36d0cec2 100644
--- a/templates/shared/secrets/add_list.tmpl
+++ b/templates/shared/secrets/add_list.tmpl
@@ -14,7 +14,7 @@
 	{{if .Secrets}}
 	<div class="flex-list">
 		{{range .Secrets}}
-		<div class="flex-item gt-ac">
+		<div class="flex-item tw-content-center">
 			<div class="flex-item-leading">
 				{{svg "octicon-key" 32}}
 			</div>
diff --git a/templates/shared/user/org_profile_avatar.tmpl b/templates/shared/user/org_profile_avatar.tmpl
index a8846b0abd..07e7b8aed5 100644
--- a/templates/shared/user/org_profile_avatar.tmpl
+++ b/templates/shared/user/org_profile_avatar.tmpl
@@ -2,7 +2,7 @@
 	<div class="ui container">
 		<div class="ui vertically grid head">
 			<div class="column">
-				<div class="ui header gt-df gt-ac gt-word-break">
+				<div class="ui header tw-flex tw-content-center gt-word-break">
 					{{ctx.AvatarUtils.Avatar . 100}}
 					<span class="text thin grey"><a href="{{.HomeLink}}">{{.DisplayName}}</a></span>
 					<span class="org-visibility">
diff --git a/templates/shared/user/profile_big_avatar.tmpl b/templates/shared/user/profile_big_avatar.tmpl
index 4e25a53737..b1c837d36c 100644
--- a/templates/shared/user/profile_big_avatar.tmpl
+++ b/templates/shared/user/profile_big_avatar.tmpl
@@ -1,5 +1,5 @@
 <div id="profile-avatar-card" class="ui card">
-	<div id="profile-avatar" class="content gt-df">
+	<div id="profile-avatar" class="content tw-flex">
 	{{if eq .SignedUserID .ContextUser.ID}}
 		<a class="image" href="{{AppSubUrl}}/user/settings" data-tooltip-content="{{ctx.Locale.Tr "user.change_avatar"}}">
 			{{/* the size doesn't take affect (and no need to take affect), image size(width) should be controlled by the parent container since this is not a flex layout*/}}
@@ -30,7 +30,7 @@
 			{{if .ContextUser.Location}}
 				<li>
 					{{svg "octicon-location"}}
-					<span class="gt-f1">{{.ContextUser.Location}}</span>
+					<span class="tw-flex-1">{{.ContextUser.Location}}</span>
 					{{if .ContextUserLocationMapURL}}
 						<a href="{{.ContextUserLocationMapURL}}" rel="nofollow noreferrer" data-tooltip-content="{{ctx.Locale.Tr "user.show_on_map"}}">
 							{{svg "octicon-link-external"}}
@@ -41,7 +41,7 @@
 			{{if (eq .SignedUserID .ContextUser.ID)}}
 				<li>
 					{{svg "octicon-mail"}}
-					<a class="gt-f1" href="mailto:{{.ContextUser.Email}}" rel="nofollow">{{.ContextUser.Email}}</a>
+					<a class="tw-flex-1" href="mailto:{{.ContextUser.Email}}" rel="nofollow">{{.ContextUser.Email}}</a>
 					<a href="{{AppSubUrl}}/user/settings#privacy-user-settings">
 						{{if .ShowUserEmail}}
 							<i data-tooltip-content="{{ctx.Locale.Tr "user.email_visibility.limited"}}">
diff --git a/templates/shared/variables/variable_list.tmpl b/templates/shared/variables/variable_list.tmpl
index 8e262d016c..dc8c7d7a80 100644
--- a/templates/shared/variables/variable_list.tmpl
+++ b/templates/shared/variables/variable_list.tmpl
@@ -16,7 +16,7 @@
 	{{if .Variables}}
 	<div class="flex-list">
 		{{range .Variables}}
-		<div class="flex-item gt-ac">
+		<div class="flex-item tw-content-center">
 			<div class="flex-item-leading">
 				{{svg "octicon-pencil" 32}}
 			</div>
diff --git a/templates/status/500.tmpl b/templates/status/500.tmpl
index 043ca7523b..a75c769167 100644
--- a/templates/status/500.tmpl
+++ b/templates/status/500.tmpl
@@ -17,8 +17,8 @@
 <body>
 	<div class="full height">
 		<nav class="ui secondary menu">
-			<div class="ui container gt-df">
-				<div class="item gt-f1">
+			<div class="ui container tw-flex">
+				<div class="item tw-flex-1">
 					<a href="{{AppSubUrl}}/" aria-label="{{ctx.Locale.Tr "home"}}">
 						<img width="30" height="30" src="{{AssetUrlPrefix}}/img/logo.svg" alt="{{ctx.Locale.Tr "logo"}}" aria-hidden="true">
 					</a>
diff --git a/templates/user/auth/signin_inner.tmpl b/templates/user/auth/signin_inner.tmpl
index d7d3649a4d..f8eb81423c 100644
--- a/templates/user/auth/signin_inner.tmpl
+++ b/templates/user/auth/signin_inner.tmpl
@@ -54,10 +54,10 @@
 		{{ctx.Locale.Tr "sign_in_or"}}
 	</div>
 	<div id="oauth2-login-navigator" class="gt-py-2">
-		<div class="gt-df gt-fc gt-jc">
-			<div id="oauth2-login-navigator-inner" class="gt-df gt-fc gt-fw gt-ac gt-gap-3">
+		<div class="tw-flex tw-flex-col tw-justify-center">
+			<div id="oauth2-login-navigator-inner" class="tw-flex tw-flex-col tw-flex-wrap tw-content-center gt-gap-3">
 				{{range $provider := .OAuth2Providers}}
-					<a class="{{$provider.Name}} ui button gt-df gt-ac gt-jc gt-py-3 tw-w-full oauth-login-link" href="{{AppSubUrl}}/user/oauth2/{{$provider.DisplayName}}">
+					<a class="{{$provider.Name}} ui button tw-flex tw-content-center tw-justify-center gt-py-3 tw-w-full oauth-login-link" href="{{AppSubUrl}}/user/oauth2/{{$provider.DisplayName}}">
 						{{$provider.IconHTML 28}}
 						{{ctx.Locale.Tr "sign_in_with_provider" $provider.DisplayName}}
 					</a>
diff --git a/templates/user/auth/signup_inner.tmpl b/templates/user/auth/signup_inner.tmpl
index cfd826a0ce..a911537996 100644
--- a/templates/user/auth/signup_inner.tmpl
+++ b/templates/user/auth/signup_inner.tmpl
@@ -59,10 +59,10 @@
 				{{ctx.Locale.Tr "sign_in_or"}}
 			</div>
 			<div id="oauth2-login-navigator" class="gt-py-2">
-				<div class="gt-df gt-fc gt-jc">
-					<div id="oauth2-login-navigator-inner" class="gt-df gt-fc gt-fw gt-ac gt-gap-3">
+				<div class="tw-flex tw-flex-col tw-justify-center">
+					<div id="oauth2-login-navigator-inner" class="tw-flex tw-flex-col tw-flex-wrap tw-content-center gt-gap-3">
 						{{range $provider := .OAuth2Providers}}
-							<a class="{{$provider.Name}} ui button gt-df gt-ac gt-jc gt-py-3 tw-w-full oauth-login-link" href="{{AppSubUrl}}/user/oauth2/{{$provider.DisplayName}}">
+							<a class="{{$provider.Name}} ui button tw-flex tw-content-center tw-justify-center gt-py-3 tw-w-full oauth-login-link" href="{{AppSubUrl}}/user/oauth2/{{$provider.DisplayName}}">
 								{{$provider.IconHTML 28}}
 								{{ctx.Locale.Tr "sign_in_with_provider" $provider.DisplayName}}
 							</a>
diff --git a/templates/user/auth/webauthn.tmpl b/templates/user/auth/webauthn.tmpl
index 722da02f54..375ebba9ae 100644
--- a/templates/user/auth/webauthn.tmpl
+++ b/templates/user/auth/webauthn.tmpl
@@ -10,7 +10,7 @@
 				{{template "base/alert" .}}
 				<p>{{ctx.Locale.Tr "webauthn_sign_in"}}</p>
 			</div>
-			<div class="ui attached segment gt-df gt-ac gt-jc gt-gap-2 gt-py-3">
+			<div class="ui attached segment tw-flex tw-content-center tw-justify-center gt-gap-2 gt-py-3">
 				<div class="is-loading" style="width: 40px; height: 40px"></div>
 				{{ctx.Locale.Tr "webauthn_press_button"}}
 			</div>
diff --git a/templates/user/dashboard/feeds.tmpl b/templates/user/dashboard/feeds.tmpl
index 0e7371ad83..382b0d4542 100644
--- a/templates/user/dashboard/feeds.tmpl
+++ b/templates/user/dashboard/feeds.tmpl
@@ -84,7 +84,7 @@
 					{{$push := ActionContent2Commits .}}
 					{{$repoLink := (.GetRepoLink ctx)}}
 					{{$repo := .Repo}}
-					<div class="gt-df gt-fc gt-gap-2">
+					<div class="tw-flex tw-flex-col gt-gap-2">
 						{{range $push.Commits}}
 							{{$commitLink := printf "%s/commit/%s" $repoLink .Sha1}}
 							<div class="flex-text-block">
diff --git a/templates/user/dashboard/milestones.tmpl b/templates/user/dashboard/milestones.tmpl
index 214081d423..05f2b30efb 100644
--- a/templates/user/dashboard/milestones.tmpl
+++ b/templates/user/dashboard/milestones.tmpl
@@ -79,7 +79,7 @@
 									{{svg "octicon-milestone" 16}}
 									<a class="muted" href="{{.Repo.Link}}/milestone/{{.ID}}">{{.Name}}</a>
 								</h3>
-								<div class="gt-df gt-ac">
+								<div class="tw-flex tw-content-center">
 									<span class="gt-mr-3">{{.Completeness}}%</span>
 									<progress value="{{.Completeness}}" max="100"></progress>
 								</div>
diff --git a/templates/user/notification/notification_div.tmpl b/templates/user/notification/notification_div.tmpl
index 431aca0975..9da9e16d93 100644
--- a/templates/user/notification/notification_div.tmpl
+++ b/templates/user/notification/notification_div.tmpl
@@ -1,7 +1,7 @@
 <div role="main" aria-label="{{.Title}}" class="page-content user notification" id="notification_div" data-sequence-number="{{.SequenceNumber}}">
 	<div class="ui container">
 		{{$notificationUnreadCount := call .NotificationUnreadCount}}
-		<div class="gt-df gt-ac gt-sb gt-mb-4">
+		<div class="tw-flex tw-content-center tw-justify-between gt-mb-4">
 			<div class="small-menu-items ui compact tiny menu">
 				<a class="{{if eq .Status 1}}active {{end}}item" href="{{AppSubUrl}}/notifications?q=unread">
 					{{ctx.Locale.Tr "notification.unread"}}
@@ -25,7 +25,7 @@
 		<div class="gt-p-0">
 			<div id="notification_table">
 				{{if not .Notifications}}
-					<div class="gt-df gt-ac gt-fc gt-p-4">
+					<div class="tw-flex tw-content-center tw-flex-col gt-p-4">
 						{{svg "octicon-inbox" 56 "gt-mb-4"}}
 						{{if eq .Status 1}}
 							{{ctx.Locale.Tr "notification.no_unread"}}
@@ -35,7 +35,7 @@
 					</div>
 				{{else}}
 					{{range $notification := .Notifications}}
-						<div class="notifications-item gt-df gt-ac gt-fw gt-gap-3 gt-p-3" id="notification_{{.ID}}" data-status="{{.Status}}">
+						<div class="notifications-item tw-flex tw-content-center tw-flex-wrap gt-gap-3 gt-p-3" id="notification_{{.ID}}" data-status="{{.Status}}">
 							<div class="notifications-icon gt-ml-3 gt-mr-2 tw-self-start gt-mt-2">
 								{{if .Issue}}
 									{{template "shared/issueicon" .Issue}}
@@ -43,7 +43,7 @@
 									{{svg "octicon-repo" 16 "text grey"}}
 								{{end}}
 							</div>
-							<a class="notifications-link gt-df gt-f1 gt-fc silenced" href="{{.Link ctx}}">
+							<a class="notifications-link tw-flex tw-flex-1 tw-flex-col silenced" href="{{.Link ctx}}">
 								<div class="notifications-top-row gt-font-13">
 									{{.Repository.FullName}} {{if .Issue}}<span class="text light-3">#{{.Issue.Index}}</span>{{end}}
 									{{if eq .Status 3}}
@@ -60,14 +60,14 @@
 									</span>
 								</div>
 							</a>
-							<div class="notifications-updated gt-ac gt-mr-3">
+							<div class="notifications-updated tw-content-center gt-mr-3">
 								{{if .Issue}}
 									{{TimeSinceUnix .Issue.UpdatedUnix ctx.Locale}}
 								{{else}}
 									{{TimeSinceUnix .UpdatedUnix ctx.Locale}}
 								{{end}}
 							</div>
-							<div class="notifications-buttons gt-ac gt-je gt-gap-2 gt-px-2">
+							<div class="notifications-buttons tw-content-center tw-justify-end gt-gap-2 gt-px-2">
 								{{if ne .Status 3}}
 									<form action="{{AppSubUrl}}/notifications/status" method="post">
 										{{$.CsrfTokenHtml}}
diff --git a/templates/user/notification/notification_subscriptions.tmpl b/templates/user/notification/notification_subscriptions.tmpl
index d39e628263..eb71621d92 100644
--- a/templates/user/notification/notification_subscriptions.tmpl
+++ b/templates/user/notification/notification_subscriptions.tmpl
@@ -11,8 +11,8 @@
 		</div>
 		<div class="ui bottom attached active tab segment">
 			{{if eq .Status 1}}
-				<div class="gt-df gt-sb">
-					<div class="gt-df">
+				<div class="tw-flex tw-justify-between">
+					<div class="tw-flex">
 						<div class="small-menu-items ui compact tiny menu">
 							<a class="{{if eq .State "all"}}active {{end}}item" href="?sort={{$.SortType}}&state=all&issueType={{$.IssueType}}&labels={{$.Labels}}">
 								{{ctx.Locale.Tr "all"}}
@@ -27,7 +27,7 @@
 							</a>
 						</div>
 					</div>
-					<div class="gt-df gt-sb">
+					<div class="tw-flex tw-justify-between">
 						<div class="ui right aligned secondary filter menu labels">
 							<!-- Type -->
 								<div class="ui dropdown type jump item">
diff --git a/templates/user/settings/account.tmpl b/templates/user/settings/account.tmpl
index 29441f97ee..6b3d3957cb 100644
--- a/templates/user/settings/account.tmpl
+++ b/templates/user/settings/account.tmpl
@@ -46,7 +46,7 @@
 					<form action="{{AppSubUrl}}/user/settings/account/email" class="ui form" method="post">
 						{{$.CsrfTokenHtml}}
 						<input name="_method" type="hidden" value="NOTIFICATION">
-						<div class="gt-df gt-fw gt-gap-3">
+						<div class="tw-flex tw-flex-wrap gt-gap-3">
 							<div class="ui selection dropdown">
 								<input name="preference" type="hidden" value="{{.EmailNotificationsPreference}}">
 								{{svg "octicon-triangle-down" 14 "dropdown icon"}}
diff --git a/templates/user/settings/applications_oauth2_list.tmpl b/templates/user/settings/applications_oauth2_list.tmpl
index bf6b28ec5f..1125a66d47 100644
--- a/templates/user/settings/applications_oauth2_list.tmpl
+++ b/templates/user/settings/applications_oauth2_list.tmpl
@@ -4,7 +4,7 @@
 			{{ctx.Locale.Tr "settings.oauth2_application_create_description"}}
 		</div>
 		{{range .Applications}}
-			<div class="flex-item gt-ac">
+			<div class="flex-item tw-content-center">
 				<div class="flex-item-leading">
 					{{svg "octicon-apps" 32}}
 				</div>
diff --git a/templates/user/settings/repos.tmpl b/templates/user/settings/repos.tmpl
index eeb2b6cbdd..41cdae2968 100644
--- a/templates/user/settings/repos.tmpl
+++ b/templates/user/settings/repos.tmpl
@@ -30,8 +30,8 @@
 											<span><a href="{{$repo.BaseRepo.Link}}">{{$repo.BaseRepo.OwnerName}}/{{$repo.BaseRepo.Name}}</a></span>
 										{{end}}
 									{{else}}
-										<span class="icon gt-dib gt-pt-3">{{svg "octicon-file-directory-fill"}}</span>
-										<span class="name gt-dib gt-pt-3">{{$.ContextUser.Name}}/{{$dir}}</span>
+										<span class="icon tw-inline-block gt-pt-3">{{svg "octicon-file-directory-fill"}}</span>
+										<span class="name tw-inline-block gt-pt-3">{{$.ContextUser.Name}}/{{$dir}}</span>
 										<div class="tw-float-right">
 											{{if $.allowAdopt}}
 												<button class="ui button primary show-modal gt-p-3" data-modal="#adopt-unadopted-modal-{{$dirI}}"><span class="icon">{{svg "octicon-plus"}}</span><span class="label">{{ctx.Locale.Tr "repo.adopt_preexisting_label"}}</span></button>
diff --git a/templates/user/settings/security/openid.tmpl b/templates/user/settings/security/openid.tmpl
index 0e9b4adcbe..63bc56ba9b 100644
--- a/templates/user/settings/security/openid.tmpl
+++ b/templates/user/settings/security/openid.tmpl
@@ -7,7 +7,7 @@
 			{{ctx.Locale.Tr "settings.openid_desc"}}
 		</div>
 		{{range .OpenIDs}}
-			<div class="flex-item gt-ac">
+			<div class="flex-item tw-content-center">
 				<div class="flex-item-leading">
 					{{svg "fontawesome-openid" 20}}
 				</div>
diff --git a/templates/webhook/new.tmpl b/templates/webhook/new.tmpl
index 63bd8363b4..b4126df341 100644
--- a/templates/webhook/new.tmpl
+++ b/templates/webhook/new.tmpl
@@ -1,7 +1,7 @@
 <h4 class="ui top attached header">
 	{{.CustomHeaderTitle}}
 	<div class="ui right type dropdown">
-		<div class="text gt-df gt-ac">
+		<div class="text tw-flex tw-content-center">
 			{{template "shared/webhook/icon" (dict "Size" 20 "HookType" .ctxData.HookType)}}
 			{{ctx.Locale.Tr (print "repo.settings.web_hook_name_" .ctxData.HookType)}}
 		</div>
diff --git a/tests/integration/release_test.go b/tests/integration/release_test.go
index 3ae2703afd..b1b08129bd 100644
--- a/tests/integration/release_test.go
+++ b/tests/integration/release_test.go
@@ -285,7 +285,7 @@ func TestViewTagsList(t *testing.T) {
 
 	tagNames := make([]string, 0, 5)
 	tags.Each(func(i int, s *goquery.Selection) {
-		tagNames = append(tagNames, s.Find(".tag a.gt-df.gt-ac").Text())
+		tagNames = append(tagNames, s.Find(".tag a.tw-flex.tw-content-center").Text())
 	})
 
 	assert.EqualValues(t, []string{"v1.0", "delete-tag", "v1.1"}, tagNames)
diff --git a/web_src/css/actions.css b/web_src/css/actions.css
index e353a013a7..e7b9a3855a 100644
--- a/web_src/css/actions.css
+++ b/web_src/css/actions.css
@@ -14,10 +14,6 @@
   color: var(--color-red-light);
 }
 
-.runner-container .runner-basic-info .gt-dib {
-  margin-right: 1em;
-}
-
 .runner-container .runner-new-text {
   color: var(--color-white);
 }
diff --git a/web_src/css/helpers.css b/web_src/css/helpers.css
index 6fc84d743c..7a328aeb20 100644
--- a/web_src/css/helpers.css
+++ b/web_src/css/helpers.css
@@ -2,16 +2,6 @@
 Gitea's tailwind-style CSS helper classes have `gt-` prefix.
 Gitea's private styles use `g-` prefix.
 */
-.gt-df { display: flex !important; }
-.gt-dib { display: inline-block !important; }
-.gt-ac { align-items: center !important; }
-.gt-jc { justify-content: center !important; }
-.gt-je { justify-content: flex-end !important; }
-.gt-sb { justify-content: space-between !important; }
-.gt-fc { flex-direction: column !important; }
-.gt-f1 { flex: 1 !important; }
-.gt-fw { flex-wrap: wrap !important; }
-.gt-vm { vertical-align: middle !important; }
 
 .gt-mono {
   font-family: var(--fonts-monospace) !important;
diff --git a/web_src/js/components/ActionRunStatus.vue b/web_src/js/components/ActionRunStatus.vue
index 51a7745431..08a47eded7 100644
--- a/web_src/js/components/ActionRunStatus.vue
+++ b/web_src/js/components/ActionRunStatus.vue
@@ -28,7 +28,7 @@ export default {
 };
 </script>
 <template>
-  <span class="gt-df gt-ac" :data-tooltip-content="localeStatus" v-if="status">
+  <span class="tw-flex tw-content-center" :data-tooltip-content="localeStatus" v-if="status">
     <SvgIcon name="octicon-check-circle-fill" class="text green" :size="size" :class-name="className" v-if="status === 'success'"/>
     <SvgIcon name="octicon-skip" class="text grey" :size="size" :class-name="className" v-else-if="status === 'skipped'"/>
     <SvgIcon name="octicon-clock" class="text yellow" :size="size" :class-name="className" v-else-if="status === 'waiting'"/>
diff --git a/web_src/js/components/DiffCommitSelector.vue b/web_src/js/components/DiffCommitSelector.vue
index b465127aca..e8ceffa3e8 100644
--- a/web_src/js/components/DiffCommitSelector.vue
+++ b/web_src/js/components/DiffCommitSelector.vue
@@ -240,7 +240,7 @@ export default {
           @click.meta.exact="commitClicked(commit.id, true)"
           @click.shift.exact.stop.prevent="commitClickedShift(commit)"
         >
-          <div class="gt-f1 gt-df gt-fc gt-gap-2">
+          <div class="tw-flex-1 tw-flex tw-flex-col gt-gap-2">
             <div class="gt-ellipsis commit-list-summary">
               {{ commit.summary }}
             </div>
diff --git a/web_src/js/components/DiffFileList.vue b/web_src/js/components/DiffFileList.vue
index 8bde61804f..2499d998a8 100644
--- a/web_src/js/components/DiffFileList.vue
+++ b/web_src/js/components/DiffFileList.vue
@@ -38,7 +38,7 @@ export default {
 <template>
   <ol class="diff-stats gt-m-0" ref="root" v-if="store.fileListIsVisible">
     <li v-for="file in store.files" :key="file.NameHash">
-      <div class="gt-font-semibold gt-df gt-ac pull-right">
+      <div class="gt-font-semibold tw-flex tw-content-center pull-right">
         <span v-if="file.IsBin" class="gt-ml-1 gt-mr-3">{{ store.binaryFileMessage }}</span>
         {{ file.IsBin ? '' : file.Addition + file.Deletion }}
         <span v-if="!file.IsBin" class="diff-stats-bar gt-mx-3" :data-tooltip-content="store.statisticsMessage.replace('%d', (file.Addition + file.Deletion)).replace('%d', file.Addition).replace('%d', file.Deletion)">
@@ -50,7 +50,7 @@ export default {
       <a class="file gt-mono" :href="'#diff-' + file.NameHash">{{ file.Name }}</a>
     </li>
     <li v-if="store.isIncomplete" class="gt-pt-2">
-      <span class="file gt-df gt-ac gt-sb">{{ store.tooManyFilesMessage }}
+      <span class="file tw-flex tw-content-center tw-justify-between">{{ store.tooManyFilesMessage }}
         <a :class="['ui', 'basic', 'tiny', 'button', store.isLoadingNewData ? 'disabled' : '']" @click.stop="loadMoreData">{{ store.showMoreMessage }}</a>
       </span>
     </li>
diff --git a/web_src/js/components/DiffFileTreeItem.vue b/web_src/js/components/DiffFileTreeItem.vue
index 9d7ab4afc5..a5d78f07f1 100644
--- a/web_src/js/components/DiffFileTreeItem.vue
+++ b/web_src/js/components/DiffFileTreeItem.vue
@@ -37,7 +37,7 @@ export default {
   >
     <!-- file -->
     <SvgIcon name="octicon-file"/>
-    <span class="gt-ellipsis gt-f1">{{ item.name }}</span>
+    <span class="gt-ellipsis tw-flex-1">{{ item.name }}</span>
     <SvgIcon :name="getIconForDiffType(item.file.Type).name" :class="getIconForDiffType(item.file.Type).classes"/>
   </a>
   <div v-else class="item-directory" :title="item.name" @click.stop="collapsed = !collapsed">
diff --git a/web_src/js/components/PullRequestMergeForm.vue b/web_src/js/components/PullRequestMergeForm.vue
index 170d0d85c6..5f2e19f2e5 100644
--- a/web_src/js/components/PullRequestMergeForm.vue
+++ b/web_src/js/components/PullRequestMergeForm.vue
@@ -136,7 +136,7 @@ export default {
       </div>
     </form>
 
-    <div v-if="!showActionForm" class="gt-df">
+    <div v-if="!showActionForm" class="tw-flex">
       <!-- the merge button -->
       <div class="ui buttons merge-button" :class="[mergeForm.emptyCommit ? 'grey' : mergeForm.allOverridableChecksOk ? 'primary' : 'red']" @click="toggleActionForm(true)">
         <button class="ui button">
diff --git a/web_src/js/components/RepoBranchTagSelector.vue b/web_src/js/components/RepoBranchTagSelector.vue
index 83289c8852..a5ac689e5a 100644
--- a/web_src/js/components/RepoBranchTagSelector.vue
+++ b/web_src/js/components/RepoBranchTagSelector.vue
@@ -245,8 +245,8 @@ export default sfc; // activate IDE's Vue plugin
 </script>
 <template>
   <div class="ui dropdown custom">
-    <button class="branch-dropdown-button gt-ellipsis ui basic small compact button gt-df gt-m-0" @click="menuVisible = !menuVisible" @keyup.enter="menuVisible = !menuVisible">
-      <span class="text gt-df gt-ac gt-mr-2">
+    <button class="branch-dropdown-button gt-ellipsis ui basic small compact button tw-flex gt-m-0" @click="menuVisible = !menuVisible" @keyup.enter="menuVisible = !menuVisible">
+      <span class="text tw-flex tw-content-center gt-mr-2">
         <template v-if="release">{{ textReleaseCompare }}</template>
         <template v-else>
           <svg-icon v-if="isViewTag" name="octicon-tag"/>
diff --git a/web_src/js/components/RepoCodeFrequency.vue b/web_src/js/components/RepoCodeFrequency.vue
index c55bbff9cd..305732afc1 100644
--- a/web_src/js/components/RepoCodeFrequency.vue
+++ b/web_src/js/components/RepoCodeFrequency.vue
@@ -144,10 +144,10 @@ export default {
 </script>
 <template>
   <div>
-    <div class="ui header gt-df gt-ac gt-sb">
+    <div class="ui header tw-flex tw-content-center tw-justify-between">
       {{ isLoading ? locale.loadingTitle : errorText ? locale.loadingTitleFailed: `Code frequency over the history of ${repoLink.slice(1)}` }}
     </div>
-    <div class="gt-df ui segment main-graph">
+    <div class="tw-flex ui segment main-graph">
       <div v-if="isLoading || errorText !== ''" class="gt-tc tw-m-auto">
         <div v-if="isLoading">
           <SvgIcon name="octicon-sync" class="gt-mr-3 job-status-rotate"/>
diff --git a/web_src/js/components/RepoContributors.vue b/web_src/js/components/RepoContributors.vue
index 6093c762cb..ca51ca8aba 100644
--- a/web_src/js/components/RepoContributors.vue
+++ b/web_src/js/components/RepoContributors.vue
@@ -303,7 +303,7 @@ export default {
 </script>
 <template>
   <div>
-    <div class="ui header gt-df gt-ac gt-sb">
+    <div class="ui header tw-flex tw-content-center tw-justify-between">
       <div>
         <relative-time
           v-if="xAxisMin > 0"
@@ -352,7 +352,7 @@ export default {
         </div>
       </div>
     </div>
-    <div class="gt-df ui segment main-graph">
+    <div class="tw-flex ui segment main-graph">
       <div v-if="isLoading || errorText !== ''" class="gt-tc tw-m-auto">
         <div v-if="isLoading">
           <SvgIcon name="octicon-sync" class="gt-mr-3 job-status-rotate"/>
@@ -374,17 +374,17 @@ export default {
         :key="index"
         v-memo="[sortedContributors, type]"
       >
-        <div class="ui top attached header gt-df gt-f1">
+        <div class="ui top attached header tw-flex tw-flex-1">
           <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">
+            <img class="ui avatar tw-align-middle" 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">
+            <p class="gt-font-12 tw-flex 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">
diff --git a/web_src/js/components/RepoRecentCommits.vue b/web_src/js/components/RepoRecentCommits.vue
index c1fd40f506..23738b8060 100644
--- a/web_src/js/components/RepoRecentCommits.vue
+++ b/web_src/js/components/RepoRecentCommits.vue
@@ -121,10 +121,10 @@ export default {
 </script>
 <template>
   <div>
-    <div class="ui header gt-df gt-ac gt-sb">
+    <div class="ui header tw-flex tw-content-center tw-justify-between">
       {{ isLoading ? locale.loadingTitle : errorText ? locale.loadingTitleFailed: "Number of commits in the past year" }}
     </div>
-    <div class="gt-df ui segment main-graph">
+    <div class="tw-flex ui segment main-graph">
       <div v-if="isLoading || errorText !== ''" class="gt-tc tw-m-auto">
         <div v-if="isLoading">
           <SvgIcon name="octicon-sync" class="gt-mr-3 job-status-rotate"/>
diff --git a/web_src/js/features/repo-issue-content.js b/web_src/js/features/repo-issue-content.js
index 9e2b773730..f67a22ea6f 100644
--- a/web_src/js/features/repo-issue-content.js
+++ b/web_src/js/features/repo-issue-content.js
@@ -16,7 +16,7 @@ function showContentHistoryDetail(issueBaseUrl, commentId, historyId, itemTitleH
   $dialog = $(`
 <div class="ui modal content-history-detail-dialog">
   ${svg('octicon-x', 16, 'close icon inside')}
-  <div class="header gt-df gt-ac gt-sb">
+  <div class="header tw-flex tw-content-center tw-justify-between">
     <div>${itemTitleHtml}</div>
     <div class="ui dropdown dialog-header-options gt-mr-5 gt-hidden">
       ${i18nTextOptions}
diff --git a/web_src/js/features/repo-issue-list.js b/web_src/js/features/repo-issue-list.js
index 48658fd723..4bdd5e5a8e 100644
--- a/web_src/js/features/repo-issue-list.js
+++ b/web_src/js/features/repo-issue-list.js
@@ -108,7 +108,7 @@ function initRepoIssueListAuthorDropdown() {
         // the content is provided by backend IssuePosters handler
         const processedResults = []; // to be used by dropdown to generate menu items
         for (const item of resp.results) {
-          let html = `<img class="ui avatar gt-vm" src="${htmlEscape(item.avatar_link)}" aria-hidden="true" alt="" width="20" height="20"><span class="gt-ellipsis">${htmlEscape(item.username)}</span>`;
+          let html = `<img class="ui avatar tw-align-middle" src="${htmlEscape(item.avatar_link)}" aria-hidden="true" alt="" width="20" height="20"><span class="gt-ellipsis">${htmlEscape(item.username)}</span>`;
           if (item.full_name) html += `<span class="search-fullname gt-ml-3">${htmlEscape(item.full_name)}</span>`;
           processedResults.push({value: item.user_id, name: html});
         }

From 9c9f40f6d355ecc08c01854a712a7b9c2aaced1b Mon Sep 17 00:00:00 2001
From: Earl Warren <contact@earl-warren.org>
Date: Sun, 24 Mar 2024 21:27:48 +0100
Subject: [PATCH 51/74] Replace 10 more gt- classes with tw- (#29945) (step 2)

Same operation ran to cover all Forgejo specific files as well.

---

Likely the biggest change of the tailwind refactors. Only thing of note
is that `tw-flex-1` resolves to `flex: 1 1 0%` while our `gt-f1` was
`flex: 1 1 0`, I don't think it will make any difference. Commands I've
ran:

```sh
perl -p -i -e 's#gt-vm#tw-align-middle#g' web_src/js/**/* templates/**/* models/**/*
perl -p -i -e 's#gt-fw#tw-flex-wrap#g' web_src/js/**/* templates/**/* models/**/*
perl -p -i -e 's#gt-f1#tw-flex-1#g' web_src/js/**/* templates/**/* models/**/*
perl -p -i -e 's#gt-fc#tw-flex-col#g' web_src/js/**/* templates/**/* models/**/*
perl -p -i -e 's#gt-sb#tw-justify-between#g' web_src/js/**/* templates/**/* models/**/*
perl -p -i -e 's#gt-je#tw-justify-end#g' web_src/js/**/* templates/**/* models/**/*
perl -p -i -e 's#gt-jc#tw-justify-center#g' web_src/js/**/* templates/**/* models/**/*
perl -p -i -e 's#gt-ac#tw-content-center#g' web_src/js/**/* templates/**/* models/**/* tests/**/*
perl -p -i -e 's#gt-df#tw-flex#g' web_src/js/**/* templates/**/* models/**/* tests/**/*
perl -p -i -e 's#gt-dib#tw-inline-block#g' web_src/js/**/* templates/**/* models/**/* tests/**/*

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
(cherry picked from commit f88ad5424f381bf2a45fd863b551c5a72891bb68)
---
 templates/org/settings/blocked_users.tmpl        |  2 +-
 templates/repo/diff/conversation.tmpl            | 12 ++++++------
 templates/repo/header.tmpl                       |  2 +-
 templates/repo/issue/filter_actions.tmpl         |  2 +-
 templates/repo/issue/filter_list.tmpl            |  6 +++---
 .../repo/issue/view_content/conversation.tmpl    | 16 ++++++++--------
 templates/repo/release/list.tmpl                 |  4 ++--
 templates/repo/wiki/view.tmpl                    |  2 +-
 templates/shared/repo_search.tmpl                |  2 +-
 web_src/js/components/DashboardRepoList.vue      | 12 ++++++------
 10 files changed, 30 insertions(+), 30 deletions(-)

diff --git a/templates/org/settings/blocked_users.tmpl b/templates/org/settings/blocked_users.tmpl
index 7608ebb435..4701e696ad 100644
--- a/templates/org/settings/blocked_users.tmpl
+++ b/templates/org/settings/blocked_users.tmpl
@@ -1,7 +1,7 @@
 {{template "org/settings/layout_head" (dict "ctxData" . "pageClass" "organization settings blocked-users")}}
 <div class="org-setting-content">
 	<div class="ui attached segment">
-		<form class="ui form ignore-dirty gt-df gt-fw gt-gap-3" action="{{$.Link}}/block" method="post">
+		<form class="ui form ignore-dirty tw-flex tw-flex-wrap gt-gap-3" action="{{$.Link}}/block" method="post">
 			{{.CsrfTokenHtml}}
 			<input type="hidden" name="uid" value="">
 			<div class="ui left">
diff --git a/templates/repo/diff/conversation.tmpl b/templates/repo/diff/conversation.tmpl
index feca7b6c0b..e2e87646aa 100644
--- a/templates/repo/diff/conversation.tmpl
+++ b/templates/repo/diff/conversation.tmpl
@@ -5,8 +5,8 @@
 {{$referenceUrl := printf "%s#%s" $.Issue.Link (index .comments 0).HashTag}}
 <div class="conversation-holder" data-path="{{(index .comments 0).TreePath}}" data-side="{{if lt (index .comments 0).Line 0}}left{{else}}right{{end}}" data-idx="{{(index .comments 0).UnsignedLine}}">
 	{{if $resolved}}
-		<div class="ui attached header resolved-placeholder gt-df gt-ac gt-sb">
-			<div class="ui grey text gt-df gt-ac gt-fw gt-gap-2">
+		<div class="ui attached header resolved-placeholder tw-flex tw-content-center tw-justify-between">
+			<div class="ui grey text tw-flex tw-content-center tw-flex-wrap gt-gap-2">
 				{{svg "octicon-check" 16 "icon gt-mr-2"}}
 				<b>{{$resolveDoer.Name}}</b> {{ctx.Locale.Tr "repo.issues.review.resolved_by"}}
 				{{if $invalid}}
@@ -19,12 +19,12 @@
 					</a>
 				{{end}}
 			</div>
-			<div class="gt-df gt-ac gt-gap-3">
-				<button id="show-outdated-{{(index .comments 0).ID}}" data-comment="{{(index .comments 0).ID}}" class="ui tiny labeled button show-outdated gt-df gt-ac">
+			<div class="tw-flex tw-content-center gt-gap-3">
+				<button id="show-outdated-{{(index .comments 0).ID}}" data-comment="{{(index .comments 0).ID}}" class="ui tiny labeled button show-outdated tw-flex tw-content-center">
 					{{svg "octicon-unfold" 16 "gt-mr-3"}}
 					{{ctx.Locale.Tr "repo.issues.review.show_resolved"}}
 				</button>
-				<button id="hide-outdated-{{(index .comments 0).ID}}" data-comment="{{(index .comments 0).ID}}" class="ui tiny labeled button hide-outdated gt-df gt-ac gt-hidden">
+				<button id="hide-outdated-{{(index .comments 0).ID}}" data-comment="{{(index .comments 0).ID}}" class="ui tiny labeled button hide-outdated tw-flex tw-content-center gt-hidden">
 					{{svg "octicon-fold" 16 "gt-mr-3"}}
 					{{ctx.Locale.Tr "repo.issues.review.hide_resolved"}}
 				</button>
@@ -37,7 +37,7 @@
 				{{template "repo/diff/comments" dict "root" $ "comments" .comments}}
 			</ui>
 		</div>
-		<div class="gt-df gt-je gt-ac gt-fw gt-mt-3">
+		<div class="tw-flex tw-justify-end tw-content-center tw-flex-wrap gt-mt-3">
 			<div class="ui buttons gt-mr-2">
 				<button class="ui icon tiny basic button previous-conversation">
 					{{svg "octicon-arrow-up" 12 "icon"}} {{ctx.Locale.Tr "repo.issues.previous"}}
diff --git a/templates/repo/header.tmpl b/templates/repo/header.tmpl
index f59dd621bf..128c794ddc 100644
--- a/templates/repo/header.tmpl
+++ b/templates/repo/header.tmpl
@@ -2,7 +2,7 @@
 {{with .Repository}}
 	<div class="ui container">
 		<div class="repo-header">
-			<div class="flex-item gt-ac">
+			<div class="flex-item tw-content-center">
 				<div class="flex-item-leading">{{template "repo/icon" .}}</div>
 				<div class="flex-item-main">
 					<div class="flex-item-title gt-font-18">
diff --git a/templates/repo/issue/filter_actions.tmpl b/templates/repo/issue/filter_actions.tmpl
index a2296f6597..064b75af02 100644
--- a/templates/repo/issue/filter_actions.tmpl
+++ b/templates/repo/issue/filter_actions.tmpl
@@ -29,7 +29,7 @@
 						<div class="divider"></div>
 					{{end}}
 					{{$previousExclusiveScope = $exclusiveScope}}
-					<div class="item issue-action gt-df gt-sb" data-action="toggle" data-element-id="{{.ID}}" data-url="{{$.RepoLink}}/issues/labels">
+					<div class="item issue-action tw-flex tw-justify-between" data-action="toggle" data-element-id="{{.ID}}" data-url="{{$.RepoLink}}/issues/labels">
 						{{if SliceUtils.Contains $.SelLabelIDs .ID}}{{if $exclusiveScope}}{{svg "octicon-dot-fill"}}{{else}}{{svg "octicon-check"}}{{end}}{{end}} {{RenderLabel $.Context .}}
 						{{template "repo/issue/labels/label_archived" .}}
 					</div>
diff --git a/templates/repo/issue/filter_list.tmpl b/templates/repo/issue/filter_list.tmpl
index 0349737c0d..3cae8d8b19 100644
--- a/templates/repo/issue/filter_list.tmpl
+++ b/templates/repo/issue/filter_list.tmpl
@@ -32,7 +32,7 @@
 				<div class="divider"></div>
 			{{end}}
 			{{$previousExclusiveScope = $exclusiveScope}}
-			<a rel="nofollow" class="item label-filter-item gt-df gt-ac" {{if .IsArchived}}data-is-archived{{end}} href="?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{.QueryString}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}" data-label-id="{{.ID}}">
+			<a rel="nofollow" class="item label-filter-item tw-flex tw-content-center" {{if .IsArchived}}data-is-archived{{end}} href="?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{.QueryString}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}" data-label-id="{{.ID}}">
 				{{if .IsExcluded}}
 					{{svg "octicon-circle-slash"}}
 				{{else if .IsSelected}}
@@ -107,7 +107,7 @@
 				{{ctx.Locale.Tr "repo.issues.new.open_projects"}}
 			</div>
 			{{range .OpenProjects}}
-				<a rel="nofollow" class="{{if $.ProjectID}}{{if eq $.ProjectID .ID}}active selected{{end}}{{end}} item gt-df" href="?type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{$.SelectLabels}}&milestone={{$.MilestoneID}}&project={{.ID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">
+				<a rel="nofollow" class="{{if $.ProjectID}}{{if eq $.ProjectID .ID}}active selected{{end}}{{end}} item tw-flex" href="?type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{$.SelectLabels}}&milestone={{$.MilestoneID}}&project={{.ID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">
 					{{svg .IconName 18 "gt-mr-3 tw-shrink-0"}}<span class="gt-ellipsis">{{.Title}}</span>
 				</a>
 			{{end}}
@@ -160,7 +160,7 @@
 		<a rel="nofollow" class="{{if eq .AssigneeID -1}}active selected {{end}}item" href="?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee=-1&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_assginee_no_assignee"}}</a>
 		<div class="divider"></div>
 		{{range .Assignees}}
-			<a rel="nofollow" class="{{if eq $.AssigneeID .ID}}active selected{{end}} item gt-df" href="?type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{$.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{.ID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">
+			<a rel="nofollow" class="{{if eq $.AssigneeID .ID}}active selected{{end}} item tw-flex" href="?type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{$.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{.ID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">
 				{{ctx.AvatarUtils.Avatar . 20}}{{template "repo/search_name" .}}
 			</a>
 		{{end}}
diff --git a/templates/repo/issue/view_content/conversation.tmpl b/templates/repo/issue/view_content/conversation.tmpl
index b6e075d0ce..b302ee8c2d 100644
--- a/templates/repo/issue/view_content/conversation.tmpl
+++ b/templates/repo/issue/view_content/conversation.tmpl
@@ -3,8 +3,8 @@
 {{$resolveDoer := (index .comments 0).ResolveDoer}}
 {{$isNotPending := (not (eq (index .comments 0).Review.Type 0))}}
 <div class="ui segments conversation-holder">
-	<div class="ui segment collapsible-comment-box gt-py-3 gt-df gt-ac gt-sb">
-		<div class="gt-df gt-ac">
+	<div class="ui segment collapsible-comment-box gt-py-3 tw-flex tw-content-center tw-justify-between">
+		<div class="tw-flex tw-content-center">
 			<a href="{{(index .comments 0).CodeCommentLink ctx}}" class="file-comment gt-ml-3 gt-word-break">{{(index .comments 0).TreePath}}</a>
 			{{if $invalid}}
 				<span class="ui label basic small gt-ml-3" data-tooltip-content="{{ctx.Locale.Tr "repo.issues.review.outdated_description"}}">
@@ -14,7 +14,7 @@
 		</div>
 		<div>
 			{{if or $invalid $resolved}}
-				<button id="show-outdated-{{(index .comments 0).ID}}" data-comment="{{(index .comments 0).ID}}" class="{{if not $resolved}}gt-hidden {{end}}ui compact labeled button show-outdated gt-df gt-ac">
+				<button id="show-outdated-{{(index .comments 0).ID}}" data-comment="{{(index .comments 0).ID}}" class="{{if not $resolved}}gt-hidden {{end}}ui compact labeled button show-outdated tw-flex tw-content-center">
 					{{svg "octicon-unfold" 16 "gt-mr-3"}}
 					{{if $resolved}}
 						{{ctx.Locale.Tr "repo.issues.review.show_resolved"}}
@@ -22,7 +22,7 @@
 						{{ctx.Locale.Tr "repo.issues.review.show_outdated"}}
 					{{end}}
 				</button>
-				<button id="hide-outdated-{{(index .comments 0).ID}}" data-comment="{{(index .comments 0).ID}}" class="{{if $resolved}}gt-hidden {{end}}ui compact labeled button hide-outdated gt-df gt-ac">
+				<button id="hide-outdated-{{(index .comments 0).ID}}" data-comment="{{(index .comments 0).ID}}" class="{{if $resolved}}gt-hidden {{end}}ui compact labeled button hide-outdated tw-flex tw-content-center">
 					{{svg "octicon-fold" 16 "gt-mr-3"}}
 					{{if $resolved}}
 						{{ctx.Locale.Tr "repo.issues.review.hide_resolved"}}
@@ -55,7 +55,7 @@
 				<div class="comment code-comment gt-pb-4" id="{{.HashTag}}">
 					<div class="content">
 						<div class="header comment-header">
-							<div class="comment-header-left gt-df gt-ac">
+							<div class="comment-header-left tw-flex tw-content-center">
 								{{if not .OriginalAuthor}}
 									<a class="avatar">
 										{{ctx.AvatarUtils.Avatar .Poster 20}}
@@ -76,7 +76,7 @@
 									{{ctx.Locale.Tr "repo.issues.commented_at" .HashTag $createdSubStr}}
 								</span>
 							</div>
-							<div class="comment-header-right actions gt-df gt-ac">
+							<div class="comment-header-right actions tw-flex tw-content-center">
 								{{template "repo/issue/view_content/show_role" dict "ShowRole" .ShowRole}}
 								{{if not $.Repository.IsArchived}}
 									{{template "repo/issue/view_content/add_reaction" dict "ctxData" $ "ActionURL" (printf "%s/comments/%d/reactions" $.RepoLink .ID)}}
@@ -106,8 +106,8 @@
 				</div>
 			{{end}}
 		</div>
-		<div class="code-comment-buttons gt-df gt-ac gt-fw gt-mt-3 gt-mb-2 gt-mx-3">
-			<div class="gt-f1">
+		<div class="code-comment-buttons tw-flex tw-content-center tw-flex-wrap gt-mt-3 gt-mb-2 gt-mx-3">
+			<div class="tw-flex-1">
 				{{if $resolved}}
 					<div class="ui grey text">
 						{{svg "octicon-check" 16 "gt-mr-2"}}
diff --git a/templates/repo/release/list.tmpl b/templates/repo/release/list.tmpl
index f1934493d8..477b2ea8d7 100644
--- a/templates/repo/release/list.tmpl
+++ b/templates/repo/release/list.tmpl
@@ -16,10 +16,10 @@
 						{{end}}
 					</div>
 					<div class="ui twelve wide column detail">
-						<div class="gt-df gt-ac gt-sb gt-fw gt-mb-3">
+						<div class="tw-flex tw-content-center tw-justify-between tw-flex-wrap gt-mb-3">
 							<h4 class="release-list-title gt-word-break">
 								<a href="{{$.RepoLink}}/releases/tag/{{$release.TagName | PathEscapeSegments}}">{{$release.Title}}</a>
-								{{template "repo/commit_statuses" dict "Status" $info.CommitStatus "Statuses" $info.CommitStatuses "AdditionalClasses" "gt-df"}}
+								{{template "repo/commit_statuses" dict "Status" $info.CommitStatus "Statuses" $info.CommitStatuses "AdditionalClasses" "tw-flex"}}
 								{{if $release.IsDraft}}
 									<span class="ui yellow label">{{ctx.Locale.Tr "repo.release.draft"}}</span>
 								{{else if $release.IsPrerelease}}
diff --git a/templates/repo/wiki/view.tmpl b/templates/repo/wiki/view.tmpl
index ba94adeab7..7519a63461 100644
--- a/templates/repo/wiki/view.tmpl
+++ b/templates/repo/wiki/view.tmpl
@@ -4,7 +4,7 @@
 	{{$title := .title}}
 	<div class="ui container">
 		<div class="repo-button-row">
-			<div class="gt-df gt-ac">
+			<div class="tw-flex tw-content-center">
 				<div class="ui floating filter dropdown" data-no-results="{{ctx.Locale.Tr "repo.pulls.no_results"}}">
 					<div class="ui basic small button">
 						<span class="text">
diff --git a/templates/shared/repo_search.tmpl b/templates/shared/repo_search.tmpl
index 40cc2292d9..25381b9ef8 100644
--- a/templates/shared/repo_search.tmpl
+++ b/templates/shared/repo_search.tmpl
@@ -1,5 +1,5 @@
 <div class="ui small secondary filter menu">
-	<form id="repo-search-form" class="ui form ignore-dirty tw-flex-1 tw-flex tw-flex-row tw-gap-x-2 gt-ac">
+	<form id="repo-search-form" class="ui form ignore-dirty tw-flex-1 tw-flex tw-flex-row tw-gap-x-2 tw-content-center">
 		{{if .Language}}<input hidden name="language" value="{{.Language}}">{{end}}
 		{{if .TopicOnly}}<input hidden name="topic" value="{{.TopicOnly}}">{{end}}
 		<div class="ui small fluid action input tw-flex-1">
diff --git a/web_src/js/components/DashboardRepoList.vue b/web_src/js/components/DashboardRepoList.vue
index 3b3402f458..802b2571e9 100644
--- a/web_src/js/components/DashboardRepoList.vue
+++ b/web_src/js/components/DashboardRepoList.vue
@@ -344,8 +344,8 @@ export default sfc; // activate the IDE's Vue plugin
       <a :class="{item: true, active: tab === 'organizations'}" @click="changeTab('organizations')">{{ textMyOrgs }} <span class="ui grey label gt-ml-3">{{ organizationsTotalCount }}</span></a>
     </div>
     <div v-show="tab === 'repos'" class="ui tab active list dashboard-repos">
-      <h4 v-if="isOrganization" class="ui top attached gt-mt-4 gt-df gt-ac">
-        <div class="gt-f1 gt-df gt-ac">
+      <h4 v-if="isOrganization" class="ui top attached gt-mt-4 tw-flex tw-content-center">
+        <div class="tw-flex-1 tw-flex tw-content-center">
           {{ textMyRepos }}
           <span class="ui grey label gt-ml-3">{{ reposTotalCount }}</span>
         </div>
@@ -407,7 +407,7 @@ export default sfc; // activate the IDE's Vue plugin
       </div>
       <div v-if="repos.length" class="ui attached table segment tw-rounded-b">
         <ul class="repo-owner-name-list">
-          <li class="gt-df gt-ac gt-py-3" v-for="repo, index in repos" :class="{'active': index === activeIndex}" :key="repo.id">
+          <li class="tw-flex tw-content-center gt-py-3" v-for="repo, index in repos" :class="{'active': index === activeIndex}" :key="repo.id">
             <a class="repo-list-link muted" :href="repo.link">
               <svg-icon :name="repoIcon(repo)" :size="16" class-name="repo-list-icon"/>
               <div class="text truncate">{{ repo.full_name }}</div>
@@ -415,7 +415,7 @@ export default sfc; // activate the IDE's Vue plugin
                 <svg-icon name="octicon-archive" :size="16"/>
               </div>
             </a>
-            <a class="gt-df gt-ac" v-if="repo.latest_commit_status" :href="repo.latest_commit_status.TargetLink" :data-tooltip-content="repo.locale_latest_commit_status.State">
+            <a class="tw-flex tw-content-center" v-if="repo.latest_commit_status" :href="repo.latest_commit_status.TargetLink" :data-tooltip-content="repo.locale_latest_commit_status.State">
               <!-- the commit status icon logic is taken from templates/repo/commit_status.tmpl -->
               <svg-icon :name="statusIcon(repo.latest_commit_status.State)" :class-name="'gt-ml-3 commit-status icon text ' + statusColor(repo.latest_commit_status.State)" :size="16"/>
             </a>
@@ -456,7 +456,7 @@ export default sfc; // activate the IDE's Vue plugin
     <div v-if="!isOrganization" v-show="tab === 'organizations'" class="ui tab active list dashboard-orgs">
       <div v-if="organizations.length" class="ui attached table segment tw-rounded-b">
         <ul class="repo-owner-name-list">
-          <li class="gt-df gt-ac gt-py-3" v-for="org in organizations" :key="org.name">
+          <li class="tw-flex tw-content-center gt-py-3" v-for="org in organizations" :key="org.name">
             <a class="repo-list-link muted" :href="subUrl + '/' + encodeURIComponent(org.name)">
               <svg-icon name="octicon-organization" :size="16" class-name="repo-list-icon"/>
               <div class="text truncate">{{ org.name }}</div>
@@ -466,7 +466,7 @@ export default sfc; // activate the IDE's Vue plugin
                 </span>
               </div>
             </a>
-            <div class="text light grey gt-df gt-ac gt-ml-3">
+            <div class="text light grey tw-flex tw-content-center gt-ml-3">
               {{ org.num_repos }}
               <svg-icon name="octicon-repo" :size="16" class-name="gt-ml-2 gt-mt-1"/>
             </div>

From b96845ae0176e0fa6e1563a76b3f2db027af9043 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Fri, 22 Mar 2024 15:06:53 +0100
Subject: [PATCH 52/74] Enforce trailing comma in JS on multiline (#30002)

To keep blame info accurate and to avoid [changes like
this](https://github.com/go-gitea/gitea/pull/29977/files#diff-c3422631a14edbe1e508c4b22f0c718db318be08a6e889427802f9b6165d88d6R359),
it's good to always have a trailing comma, so let's enforce it in JS.

This rule is completely automatically fixable with `make lint-js-fix`
and that's what I did here.

(cherry picked from commit 3d751b6ec18e57698ce86b79866031d2c80c2071)

Conflicts:
	web_src/js/components/DashboardRepoList.vue
	trivial context conflict because of '3b7b899afa fix commit_status'
---
 .eslintrc.yaml                                |  2 +-
 playwright.config.js                          |  2 +-
 tools/generate-images.js                      |  2 +-
 tools/generate-svg.js                         |  4 +--
 web_src/js/components/ActionRunStatus.vue     | 10 +++----
 web_src/js/components/ActivityHeatmap.vue     |  4 +--
 web_src/js/components/ContextPopup.vue        |  6 ++--
 web_src/js/components/DashboardRepoList.vue   |  4 +--
 web_src/js/components/DiffCommitSelector.vue  |  6 ++--
 web_src/js/components/DiffFileList.vue        |  2 +-
 web_src/js/components/DiffFileTree.vue        |  6 ++--
 web_src/js/components/DiffFileTreeItem.vue    |  2 +-
 .../js/components/PullRequestMergeForm.vue    |  2 +-
 web_src/js/components/RepoActionView.vue      |  6 ++--
 .../js/components/RepoActivityTopAuthors.vue  |  2 +-
 .../js/components/RepoBranchTagSelector.vue   |  6 ++--
 web_src/js/components/RepoCodeFrequency.vue   |  6 ++--
 web_src/js/components/RepoContributors.vue    |  8 ++---
 web_src/js/components/RepoRecentCommits.vue   |  6 ++--
 .../components/ScopedAccessTokenSelector.vue  |  4 +--
 web_src/js/features/captcha.js                |  4 +--
 web_src/js/features/code-frequency.js         |  2 +-
 web_src/js/features/codeeditor.js             |  4 +--
 web_src/js/features/common-global.js          |  2 +-
 .../js/features/comp/EasyMDEToolbarActions.js |  2 +-
 web_src/js/features/comp/SearchUserBox.js     |  8 ++---
 web_src/js/features/contextpopup.js           |  2 +-
 web_src/js/features/contributors.js           |  2 +-
 .../js/features/eventsource.sharedworker.js   |  2 +-
 web_src/js/features/imagediff.js              | 30 +++++++++----------
 web_src/js/features/install.js                |  2 +-
 web_src/js/features/org-team.js               |  6 ++--
 web_src/js/features/recent-commits.js         |  2 +-
 web_src/js/features/repo-code.js              |  2 +-
 web_src/js/features/repo-home.js              | 12 ++++----
 web_src/js/features/repo-issue-content.js     |  2 +-
 web_src/js/features/repo-issue.js             | 14 ++++-----
 web_src/js/features/repo-settings.js          |  8 ++---
 web_src/js/features/repo-template.js          |  6 ++--
 web_src/js/features/repo-wiki.js              |  2 +-
 web_src/js/features/tribute.js                |  4 +--
 web_src/js/features/user-auth-webauthn.js     |  6 ++--
 web_src/js/modules/tippy.js                   |  2 +-
 web_src/js/standalone/swagger.js              |  6 ++--
 web_src/js/svg.js                             |  2 +-
 web_src/js/utils/dom.js                       |  2 +-
 web_src/js/webcomponents/polyfills.js         |  2 +-
 webpack.config.js                             |  6 ++--
 48 files changed, 117 insertions(+), 117 deletions(-)

diff --git a/.eslintrc.yaml b/.eslintrc.yaml
index 72039a6013..eeb3e20cb8 100644
--- a/.eslintrc.yaml
+++ b/.eslintrc.yaml
@@ -119,7 +119,7 @@ rules:
   "@stylistic/js/arrow-spacing": [2, {before: true, after: true}]
   "@stylistic/js/block-spacing": [0]
   "@stylistic/js/brace-style": [2, 1tbs, {allowSingleLine: true}]
-  "@stylistic/js/comma-dangle": [2, only-multiline]
+  "@stylistic/js/comma-dangle": [2, always-multiline]
   "@stylistic/js/comma-spacing": [2, {before: false, after: true}]
   "@stylistic/js/comma-style": [2, last]
   "@stylistic/js/computed-property-spacing": [2, never]
diff --git a/playwright.config.js b/playwright.config.js
index 6595bc4312..fdf6514f26 100644
--- a/playwright.config.js
+++ b/playwright.config.js
@@ -27,7 +27,7 @@ export default {
      * Maximum time expect() should wait for the condition to be met.
      * For example in `await expect(locator).toHaveText();`
      */
-    timeout: 2000
+    timeout: 2000,
   },
 
   /* Fail the build on CI if you accidentally left test.only in the source code. */
diff --git a/tools/generate-images.js b/tools/generate-images.js
index cc2855c18e..0bd3af29e4 100755
--- a/tools/generate-images.js
+++ b/tools/generate-images.js
@@ -20,7 +20,7 @@ async function generate(svg, path, {size, bg}) {
         'removeDimensions',
         {
           name: 'addAttributesToSVGElement',
-          params: {attributes: [{width: size}, {height: size}]}
+          params: {attributes: [{width: size}, {height: size}]},
         },
       ],
     });
diff --git a/tools/generate-svg.js b/tools/generate-svg.js
index 660ac9157e..1c5851e7ea 100755
--- a/tools/generate-svg.js
+++ b/tools/generate-svg.js
@@ -39,8 +39,8 @@ async function processFile(file, {prefix, fullName} = {}) {
           attributes: [
             {'xmlns': 'http://www.w3.org/2000/svg'},
             {'width': '16'}, {'height': '16'}, {'aria-hidden': 'true'},
-          ]
-        }
+          ],
+        },
       },
     ],
   });
diff --git a/web_src/js/components/ActionRunStatus.vue b/web_src/js/components/ActionRunStatus.vue
index 08a47eded7..4eccddffdf 100644
--- a/web_src/js/components/ActionRunStatus.vue
+++ b/web_src/js/components/ActionRunStatus.vue
@@ -10,20 +10,20 @@ export default {
   props: {
     status: {
       type: String,
-      required: true
+      required: true,
     },
     size: {
       type: Number,
-      default: 16
+      default: 16,
     },
     className: {
       type: String,
-      default: ''
+      default: '',
     },
     localeStatus: {
       type: String,
-      default: ''
-    }
+      default: '',
+    },
   },
 };
 </script>
diff --git a/web_src/js/components/ActivityHeatmap.vue b/web_src/js/components/ActivityHeatmap.vue
index cc03f1bb2a..be0841e831 100644
--- a/web_src/js/components/ActivityHeatmap.vue
+++ b/web_src/js/components/ActivityHeatmap.vue
@@ -11,7 +11,7 @@ export default {
     locale: {
       type: Object,
       default: () => {},
-    }
+    },
   },
   data: () => ({
     colorRange: [
@@ -49,7 +49,7 @@ export default {
 
       const newSearch = params.toString();
       window.location.search = newSearch.length ? `?${newSearch}` : '';
-    }
+    },
   },
 };
 </script>
diff --git a/web_src/js/components/ContextPopup.vue b/web_src/js/components/ContextPopup.vue
index 3a1b828cca..149cabd41e 100644
--- a/web_src/js/components/ContextPopup.vue
+++ b/web_src/js/components/ContextPopup.vue
@@ -69,7 +69,7 @@ export default {
         }
         return {name: label.name, color: `#${label.color}`, textColor};
       });
-    }
+    },
   },
   mounted() {
     this.$refs.root.addEventListener('ce-load-context-popup', (e) => {
@@ -97,8 +97,8 @@ export default {
       } finally {
         this.loading = false;
       }
-    }
-  }
+    },
+  },
 };
 </script>
 <template>
diff --git a/web_src/js/components/DashboardRepoList.vue b/web_src/js/components/DashboardRepoList.vue
index 802b2571e9..4b1fe7c636 100644
--- a/web_src/js/components/DashboardRepoList.vue
+++ b/web_src/js/components/DashboardRepoList.vue
@@ -252,7 +252,7 @@ const sfc = {
           return {
             ...webSearchRepo.repository,
             latest_commit_status: webSearchRepo.latest_commit_status,
-            locale_latest_commit_status_state: webSearchRepo.locale_latest_commit_status
+            locale_latest_commit_status: webSearchRepo.locale_latest_commit_status,
           };
         });
         const count = response.headers.get('X-Total-Count');
@@ -324,7 +324,7 @@ const sfc = {
       if (this.activeIndex === -1 || this.activeIndex > this.repos.length - 1) {
         this.activeIndex = 0;
       }
-    }
+    },
   },
 };
 
diff --git a/web_src/js/components/DiffCommitSelector.vue b/web_src/js/components/DiffCommitSelector.vue
index e8ceffa3e8..df712a2cb4 100644
--- a/web_src/js/components/DiffCommitSelector.vue
+++ b/web_src/js/components/DiffCommitSelector.vue
@@ -14,7 +14,7 @@ export default {
       },
       commits: [],
       hoverActivated: false,
-      lastReviewCommitSha: null
+      lastReviewCommitSha: null,
     };
   },
   computed: {
@@ -29,7 +29,7 @@ export default {
     },
     issueLink() {
       return this.$el.parentNode.getAttribute('data-issuelink');
-    }
+    },
   },
   mounted() {
     document.body.addEventListener('click', this.onBodyClick);
@@ -185,7 +185,7 @@ export default {
         }
       }
     },
-  }
+  },
 };
 </script>
 <template>
diff --git a/web_src/js/components/DiffFileList.vue b/web_src/js/components/DiffFileList.vue
index 2499d998a8..64493b348a 100644
--- a/web_src/js/components/DiffFileList.vue
+++ b/web_src/js/components/DiffFileList.vue
@@ -31,7 +31,7 @@ export default {
     },
     loadMoreData() {
       loadMoreFiles(this.store.linkLoadMore);
-    }
+    },
   },
 };
 </script>
diff --git a/web_src/js/components/DiffFileTree.vue b/web_src/js/components/DiffFileTree.vue
index 3686629df8..83d57b00d1 100644
--- a/web_src/js/components/DiffFileTree.vue
+++ b/web_src/js/components/DiffFileTree.vue
@@ -30,7 +30,7 @@ export default {
           let newParent = {
             name: split,
             children: [],
-            isFile
+            isFile,
           };
 
           if (isFile === true) {
@@ -40,7 +40,7 @@ export default {
           if (parent) {
             // check if the folder already exists
             const existingFolder = parent.children.find(
-              (x) => x.name === split
+              (x) => x.name === split,
             );
             if (existingFolder) {
               newParent = existingFolder;
@@ -74,7 +74,7 @@ export default {
       // reduce the depth of our tree.
       mergeChildIfOnlyOneDir(result);
       return result;
-    }
+    },
   },
   mounted() {
     // Default to true if unset
diff --git a/web_src/js/components/DiffFileTreeItem.vue b/web_src/js/components/DiffFileTreeItem.vue
index a5d78f07f1..0f6e54363f 100644
--- a/web_src/js/components/DiffFileTreeItem.vue
+++ b/web_src/js/components/DiffFileTreeItem.vue
@@ -7,7 +7,7 @@ export default {
   props: {
     item: {
       type: Object,
-      required: true
+      required: true,
     },
   },
   data: () => ({
diff --git a/web_src/js/components/PullRequestMergeForm.vue b/web_src/js/components/PullRequestMergeForm.vue
index 5f2e19f2e5..35acbdf74f 100644
--- a/web_src/js/components/PullRequestMergeForm.vue
+++ b/web_src/js/components/PullRequestMergeForm.vue
@@ -43,7 +43,7 @@ export default {
       for (const elem of document.querySelectorAll('[data-pull-merge-style]')) {
         toggleElem(elem, elem.getAttribute('data-pull-merge-style') === val);
       }
-    }
+    },
   },
   created() {
     this.mergeStyleAllowedCount = this.mergeForm.mergeStyles.reduce((v, msd) => v + (msd.allowed ? 1 : 0), 0);
diff --git a/web_src/js/components/RepoActionView.vue b/web_src/js/components/RepoActionView.vue
index 4a21069220..a855574c6c 100644
--- a/web_src/js/components/RepoActionView.vue
+++ b/web_src/js/components/RepoActionView.vue
@@ -69,7 +69,7 @@ const sfc = {
             name: '',
             link: '',
           },
-        }
+        },
       },
       currentJob: {
         title: '',
@@ -314,7 +314,7 @@ const sfc = {
       const logLine = this.$refs.steps.querySelector(selectedLogStep);
       if (!logLine) return;
       logLine.querySelector('.line-num').click();
-    }
+    },
   },
 };
 
@@ -357,7 +357,7 @@ export function initRepositoryActionView() {
         skipped: el.getAttribute('data-locale-status-skipped'),
         blocked: el.getAttribute('data-locale-status-blocked'),
       },
-    }
+    },
   });
   view.mount(el);
 }
diff --git a/web_src/js/components/RepoActivityTopAuthors.vue b/web_src/js/components/RepoActivityTopAuthors.vue
index fe41218d88..a41fb61d78 100644
--- a/web_src/js/components/RepoActivityTopAuthors.vue
+++ b/web_src/js/components/RepoActivityTopAuthors.vue
@@ -47,7 +47,7 @@ const sfc = {
     this.colors.barColor = refStyle.backgroundColor;
     this.colors.textColor = refStyle.color;
     this.colors.textAltColor = refAltStyle.color;
-  }
+  },
 };
 
 export function initRepoActivityTopAuthorsChart() {
diff --git a/web_src/js/components/RepoBranchTagSelector.vue b/web_src/js/components/RepoBranchTagSelector.vue
index a5ac689e5a..e3e0c13fb9 100644
--- a/web_src/js/components/RepoBranchTagSelector.vue
+++ b/web_src/js/components/RepoBranchTagSelector.vue
@@ -36,7 +36,7 @@ const sfc = {
     },
     shouldCreateTag() {
       return this.mode === 'tags';
-    }
+    },
   },
 
   watch: {
@@ -45,7 +45,7 @@ const sfc = {
         this.focusSearchField();
         this.fetchBranchesOrTags();
       }
-    }
+    },
   },
 
   beforeMount() {
@@ -209,7 +209,7 @@ const sfc = {
         this.isLoading = false;
       }
     },
-  }
+  },
 };
 
 export function initRepoBranchTagSelector(selector) {
diff --git a/web_src/js/components/RepoCodeFrequency.vue b/web_src/js/components/RepoCodeFrequency.vue
index 305732afc1..4e11d51a4a 100644
--- a/web_src/js/components/RepoCodeFrequency.vue
+++ b/web_src/js/components/RepoCodeFrequency.vue
@@ -39,7 +39,7 @@ export default {
   props: {
     locale: {
       type: Object,
-      required: true
+      required: true,
     },
   },
   data: () => ({
@@ -128,12 +128,12 @@ export default {
             },
             ticks: {
               maxRotation: 0,
-              maxTicksLimit: 12
+              maxTicksLimit: 12,
             },
           },
           y: {
             ticks: {
-              maxTicksLimit: 6
+              maxTicksLimit: 6,
             },
           },
         },
diff --git a/web_src/js/components/RepoContributors.vue b/web_src/js/components/RepoContributors.vue
index ca51ca8aba..731b21bea3 100644
--- a/web_src/js/components/RepoContributors.vue
+++ b/web_src/js/components/RepoContributors.vue
@@ -34,7 +34,7 @@ const customEventListener = {
       chart.resetZoom();
       opts.instance.updateOtherCharts(args.event, true);
     }
-  }
+  },
 };
 
 Chart.defaults.color = chartJsColors.text;
@@ -82,7 +82,7 @@ export default {
         this.xAxisMax = this.xAxisEnd;
         this.type = val;
         this.sortContributors();
-      }
+      },
     });
   },
   methods: {
@@ -175,7 +175,7 @@ export default {
       // 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])
+        ...this.totalStats.weeks.map((o) => o[this.type]),
       );
       const [coefficient, exp] = maxValue.toExponential().split('e').map(Number);
       if (coefficient % 1 === 0) return maxValue;
@@ -187,7 +187,7 @@ export default {
       // 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)
+        ...this.sortedContributors.map((c) => c.max_contribution_type),
       );
       const [coefficient, exp] = maxValue.toExponential().split('e').map(Number);
       if (coefficient % 1 === 0) return maxValue;
diff --git a/web_src/js/components/RepoRecentCommits.vue b/web_src/js/components/RepoRecentCommits.vue
index 23738b8060..1818d57943 100644
--- a/web_src/js/components/RepoRecentCommits.vue
+++ b/web_src/js/components/RepoRecentCommits.vue
@@ -35,7 +35,7 @@ export default {
   props: {
     locale: {
       type: Object,
-      required: true
+      required: true,
     },
   },
   data: () => ({
@@ -105,12 +105,12 @@ export default {
             },
             ticks: {
               maxRotation: 0,
-              maxTicksLimit: 52
+              maxTicksLimit: 52,
             },
           },
           y: {
             ticks: {
-              maxTicksLimit: 6
+              maxTicksLimit: 6,
             },
           },
         },
diff --git a/web_src/js/components/ScopedAccessTokenSelector.vue b/web_src/js/components/ScopedAccessTokenSelector.vue
index f6af7e447f..ae4e8299f2 100644
--- a/web_src/js/components/ScopedAccessTokenSelector.vue
+++ b/web_src/js/components/ScopedAccessTokenSelector.vue
@@ -39,7 +39,7 @@ const sfc = {
         'repository',
         'user');
       return categories;
-    }
+    },
   },
 
   mounted() {
@@ -68,7 +68,7 @@ const sfc = {
       }
       // no scopes selected, show validation error
       showElem(warningEl);
-    }
+    },
   },
 };
 
diff --git a/web_src/js/features/captcha.js b/web_src/js/features/captcha.js
index 3da5dbda41..c803a5006b 100644
--- a/web_src/js/features/captcha.js
+++ b/web_src/js/features/captcha.js
@@ -9,7 +9,7 @@ export async function initCaptcha() {
 
   const params = {
     sitekey: siteKey,
-    theme: isDark ? 'dark' : 'light'
+    theme: isDark ? 'dark' : 'light',
   };
 
   switch (captchaEl.getAttribute('data-captcha-type')) {
@@ -42,7 +42,7 @@ export async function initCaptcha() {
         siteKey: {
           instanceUrl: new URL(instanceURL),
           key: siteKey,
-        }
+        },
       });
       break;
     }
diff --git a/web_src/js/features/code-frequency.js b/web_src/js/features/code-frequency.js
index 103d82f6e3..47e1539ddc 100644
--- a/web_src/js/features/code-frequency.js
+++ b/web_src/js/features/code-frequency.js
@@ -11,7 +11,7 @@ export async function initRepoCodeFrequency() {
         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) {
diff --git a/web_src/js/features/codeeditor.js b/web_src/js/features/codeeditor.js
index fceb2f7620..4fb8bb9e63 100644
--- a/web_src/js/features/codeeditor.js
+++ b/web_src/js/features/codeeditor.js
@@ -80,7 +80,7 @@ export async function createMonaco(textarea, filename, editorOpts) {
     rules: [
       {
         background: getColor('--color-code-bg'),
-      }
+      },
     ],
     colors: {
       'editor.background': getColor('--color-code-bg'),
@@ -98,7 +98,7 @@ export async function createMonaco(textarea, filename, editorOpts) {
       'input.foreground': getColor('--color-input-text'),
       'scrollbar.shadow': getColor('--color-shadow'),
       'progressBar.background': getColor('--color-primary'),
-    }
+    },
   });
 
   // Quick fix: https://github.com/microsoft/monaco-editor/issues/2962
diff --git a/web_src/js/features/common-global.js b/web_src/js/features/common-global.js
index d99f606c8a..2469361c6e 100644
--- a/web_src/js/features/common-global.js
+++ b/web_src/js/features/common-global.js
@@ -335,7 +335,7 @@ export function initGlobalLinkActions() {
           const data = await response.json();
           window.location.href = data.redirect;
         }
-      }
+      },
     }).modal('show');
   }
 
diff --git a/web_src/js/features/comp/EasyMDEToolbarActions.js b/web_src/js/features/comp/EasyMDEToolbarActions.js
index 8286d5d871..c97d683704 100644
--- a/web_src/js/features/comp/EasyMDEToolbarActions.js
+++ b/web_src/js/features/comp/EasyMDEToolbarActions.js
@@ -139,7 +139,7 @@ export function easyMDEToolbarActions(EasyMDE, editor) {
       },
       icon: svg('octicon-chevron-right'),
       title: 'Add Inline Code',
-    }
+    },
   };
 
   for (const [key, value] of Object.entries(actions)) {
diff --git a/web_src/js/features/comp/SearchUserBox.js b/web_src/js/features/comp/SearchUserBox.js
index 541052c174..83d7044f11 100644
--- a/web_src/js/features/comp/SearchUserBox.js
+++ b/web_src/js/features/comp/SearchUserBox.js
@@ -22,7 +22,7 @@ export function initCompSearchUserBox() {
         $.each(response.data, (_i, item) => {
           const resultItem = {
             title: item.login,
-            image: item.avatar_url
+            image: item.avatar_url,
           };
           if (item.full_name) {
             resultItem.description = htmlEscape(item.full_name);
@@ -37,15 +37,15 @@ export function initCompSearchUserBox() {
         if (allowEmailInput && items.length === 0 && looksLikeEmailAddressCheck.test(searchQuery)) {
           const resultItem = {
             title: searchQuery,
-            description: allowEmailDescription
+            description: allowEmailDescription,
           };
           items.push(resultItem);
         }
 
         return {results: items};
-      }
+      },
     },
     searchFields: ['login', 'full_name'],
-    showNoResults: false
+    showNoResults: false,
   });
 }
diff --git a/web_src/js/features/contextpopup.js b/web_src/js/features/contextpopup.js
index 51363b810a..ce90f3e505 100644
--- a/web_src/js/features/contextpopup.js
+++ b/web_src/js/features/contextpopup.js
@@ -37,7 +37,7 @@ export function attachRefIssueContextPopup(refIssues) {
       interactiveBorder: 5,
       onShow: () => {
         el.firstChild.dispatchEvent(new CustomEvent('ce-load-context-popup', {detail: {owner, repo, index}}));
-      }
+      },
     });
   }
 }
diff --git a/web_src/js/features/contributors.js b/web_src/js/features/contributors.js
index 66185ac315..1d9cba5b9b 100644
--- a/web_src/js/features/contributors.js
+++ b/web_src/js/features/contributors.js
@@ -18,7 +18,7 @@ export async function initRepoContributors() {
         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) {
diff --git a/web_src/js/features/eventsource.sharedworker.js b/web_src/js/features/eventsource.sharedworker.js
index 2ac7d93cc1..62581cf687 100644
--- a/web_src/js/features/eventsource.sharedworker.js
+++ b/web_src/js/features/eventsource.sharedworker.js
@@ -48,7 +48,7 @@ class Source {
     this.eventSource.addEventListener(eventType, (event) => {
       this.notifyClients({
         type: eventType,
-        data: event.data
+        data: event.data,
       });
     });
   }
diff --git a/web_src/js/features/imagediff.js b/web_src/js/features/imagediff.js
index 2bac13b0bf..7b77b30ccc 100644
--- a/web_src/js/features/imagediff.js
+++ b/web_src/js/features/imagediff.js
@@ -20,19 +20,19 @@ function getDefaultSvgBoundsIfUndefined(text, src) {
     if (img.width > 1 && img.width < MaxSize && img.height > 1 && img.height < MaxSize) {
       return {
         width: img.width,
-        height: img.height
+        height: img.height,
       };
     }
     if (svg.hasAttribute('viewBox')) {
       const viewBox = svg.viewBox.baseVal;
       return {
         width: DefaultSize,
-        height: DefaultSize * viewBox.width / viewBox.height
+        height: DefaultSize * viewBox.width / viewBox.height,
       };
     }
     return {
       width: DefaultSize,
-      height: DefaultSize
+      height: DefaultSize,
     };
   }
   return null;
@@ -42,15 +42,15 @@ export function initImageDiff() {
   function createContext(image1, image2) {
     const size1 = {
       width: image1 && image1.width || 0,
-      height: image1 && image1.height || 0
+      height: image1 && image1.height || 0,
     };
     const size2 = {
       width: image2 && image2.width || 0,
-      height: image2 && image2.height || 0
+      height: image2 && image2.height || 0,
     };
     const max = {
       width: Math.max(size2.width, size1.width),
-      height: Math.max(size2.height, size1.height)
+      height: Math.max(size2.height, size1.height),
     };
 
     return {
@@ -63,8 +63,8 @@ export function initImageDiff() {
         Math.floor(max.width - size1.width) / 2,
         Math.floor(max.height - size1.height) / 2,
         Math.floor(max.width - size2.width) / 2,
-        Math.floor(max.height - size2.height) / 2
-      ]
+        Math.floor(max.height - size2.height) / 2,
+      ],
     };
   }
 
@@ -79,12 +79,12 @@ export function initImageDiff() {
       path: this.getAttribute('data-path-after'),
       mime: this.getAttribute('data-mime-after'),
       $images: $container.find('img.image-after'), // matches 3 <img>
-      $boundsInfo: $container.find('.bounds-info-after')
+      $boundsInfo: $container.find('.bounds-info-after'),
     }, {
       path: this.getAttribute('data-path-before'),
       mime: this.getAttribute('data-mime-before'),
       $images: $container.find('img.image-before'), // matches 3 <img>
-      $boundsInfo: $container.find('.bounds-info-before')
+      $boundsInfo: $container.find('.bounds-info-before'),
     }];
 
     await Promise.all(imageInfos.map(async (info) => {
@@ -222,21 +222,21 @@ export function initImageDiff() {
 
       sizes.image1.css({
         width: sizes.size1.width * factor,
-        height: sizes.size1.height * factor
+        height: sizes.size1.height * factor,
       });
       sizes.image2.css({
         width: sizes.size2.width * factor,
-        height: sizes.size2.height * factor
+        height: sizes.size2.height * factor,
       });
       sizes.image1.parent().css({
         margin: `${sizes.ratio[1] * factor}px ${sizes.ratio[0] * factor}px`,
         width: sizes.size1.width * factor + 2,
-        height: sizes.size1.height * factor + 2
+        height: sizes.size1.height * factor + 2,
       });
       sizes.image2.parent().css({
         margin: `${sizes.ratio[3] * factor}px ${sizes.ratio[2] * factor}px`,
         width: sizes.size2.width * factor + 2,
-        height: sizes.size2.height * factor + 2
+        height: sizes.size2.height * factor + 2,
       });
 
       // some inner elements are `position: absolute`, so the container's height must be large enough
@@ -248,7 +248,7 @@ export function initImageDiff() {
 
       const $range = $container.find("input[type='range']");
       const onInput = () => sizes.image1.parent().css({
-        opacity: $range.val() / 100
+        opacity: $range.val() / 100,
       });
       $range.on('input', onInput);
       onInput();
diff --git a/web_src/js/features/install.js b/web_src/js/features/install.js
index 1826ff7cde..0c2246b933 100644
--- a/web_src/js/features/install.js
+++ b/web_src/js/features/install.js
@@ -19,7 +19,7 @@ function initPreInstall() {
   const defaultDbHosts = {
     mysql: '127.0.0.1:3306',
     postgres: '127.0.0.1:5432',
-    mssql: '127.0.0.1:1433'
+    mssql: '127.0.0.1:1433',
   };
 
   const dbHost = document.getElementById('db_host');
diff --git a/web_src/js/features/org-team.js b/web_src/js/features/org-team.js
index 6ae3a90f4d..2236bc58bc 100644
--- a/web_src/js/features/org-team.js
+++ b/web_src/js/features/org-team.js
@@ -26,14 +26,14 @@ export function initOrgTeamSearchRepoBox() {
         $.each(response.data, (_i, item) => {
           items.push({
             title: item.repository.full_name.split('/')[1],
-            description: item.repository.full_name
+            description: item.repository.full_name,
           });
         });
 
         return {results: items};
-      }
+      },
     },
     searchFields: ['full_name'],
-    showNoResults: false
+    showNoResults: false,
   });
 }
diff --git a/web_src/js/features/recent-commits.js b/web_src/js/features/recent-commits.js
index ded10d39be..030c251a05 100644
--- a/web_src/js/features/recent-commits.js
+++ b/web_src/js/features/recent-commits.js
@@ -11,7 +11,7 @@ export async function initRepoRecentCommits() {
         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) {
diff --git a/web_src/js/features/repo-code.js b/web_src/js/features/repo-code.js
index c4a81ea165..08fae763b8 100644
--- a/web_src/js/features/repo-code.js
+++ b/web_src/js/features/repo-code.js
@@ -116,7 +116,7 @@ function showLineButton() {
       tippy.popper.addEventListener('click', () => {
         tippy.hide();
       }, {once: true});
-    }
+    },
   });
 }
 
diff --git a/web_src/js/features/repo-home.js b/web_src/js/features/repo-home.js
index 50f324d788..6ac7b96b9e 100644
--- a/web_src/js/features/repo-home.js
+++ b/web_src/js/features/repo-home.js
@@ -146,7 +146,7 @@ export function initRepoTopicBar() {
       addedValue = addedValue.toLowerCase().trim();
       $($addedChoice).attr('data-value', addedValue);
       $($addedChoice).attr('data-text', addedValue);
-    }
+    },
   });
 
   $.fn.form.settings.rules.validateTopic = function (_values, regExp) {
@@ -168,14 +168,14 @@ export function initRepoTopicBar() {
           {
             type: 'validateTopic',
             value: /^\s*[a-z0-9][-.a-z0-9]{0,35}\s*$/,
-            prompt: topicPrompts.formatPrompt
+            prompt: topicPrompts.formatPrompt,
           },
           {
             type: 'maxCount[25]',
-            prompt: topicPrompts.countPrompt
-          }
-        ]
+            prompt: topicPrompts.countPrompt,
+          },
+        ],
       },
-    }
+    },
   });
 }
diff --git a/web_src/js/features/repo-issue-content.js b/web_src/js/features/repo-issue-content.js
index f67a22ea6f..33ea55f027 100644
--- a/web_src/js/features/repo-issue-content.js
+++ b/web_src/js/features/repo-issue-content.js
@@ -60,7 +60,7 @@ function showContentHistoryDetail(issueBaseUrl, commentId, historyId, itemTitleH
     },
     onHide() {
       $(this).dropdown('clear', true);
-    }
+    },
   });
   $dialog.modal({
     async onShow() {
diff --git a/web_src/js/features/repo-issue.js b/web_src/js/features/repo-issue.js
index c91dd06ac9..bf4ec15372 100644
--- a/web_src/js/features/repo-issue.js
+++ b/web_src/js/features/repo-issue.js
@@ -59,7 +59,7 @@ async function updateDeadline(deadlineString) {
 
   try {
     const response = await POST($('#update-issue-deadline-form').attr('action'), {
-      data: {due_date: realDeadline}
+      data: {due_date: realDeadline},
     });
 
     if (response.ok) {
@@ -268,7 +268,7 @@ export function initRepoPullRequestUpdate() {
         $pullUpdateButton.find('.button-text').text($choice.text());
         $pullUpdateButton.data('do', $url);
       }
-    }
+    },
   });
 }
 
@@ -316,7 +316,7 @@ export function initRepoIssueReferenceRepositorySearch() {
           $.each(response.data, (_r, repo) => {
             filteredResponse.results.push({
               name: htmlEscape(repo.repository.full_name),
-              value: repo.repository.full_name
+              value: repo.repository.full_name,
             });
           });
           return filteredResponse;
@@ -327,7 +327,7 @@ export function initRepoIssueReferenceRepositorySearch() {
         const $form = $choice.closest('form');
         $form.attr('action', `${appSubUrl}/${_text}/issues/new`);
       },
-      fullTextSearch: true
+      fullTextSearch: true,
     });
 }
 
@@ -443,7 +443,7 @@ export function initRepoPullRequestReview() {
         }
         window.scrollTo({
           top: $commentDiv.offset().top - offset,
-          behavior: 'instant'
+          behavior: 'instant',
         });
       }
     }
@@ -661,7 +661,7 @@ export function initRepoIssueBranchSelect() {
     // Replace branch name to keep translation from HTML template
     $selectionTextField.html($selectionTextField.html().replace(
       `${baseName}:${branchNameOld}`,
-      `${baseName}:${branchNameNew}`
+      `${baseName}:${branchNameNew}`,
     ));
     $selectionTextField.data('branch', branchNameNew); // update branch name in setting
   };
@@ -695,7 +695,7 @@ export function initIssueTemplateCommentEditors($commentForm) {
     const editor = await initComboMarkdownEditor($markdownEditor, {
       onContentChanged: (editor) => {
         $formField.val(editor.value());
-      }
+      },
     });
 
     $formField.on('focus', async () => {
diff --git a/web_src/js/features/repo-settings.js b/web_src/js/features/repo-settings.js
index 0418f3a14a..58b714fbb7 100644
--- a/web_src/js/features/repo-settings.js
+++ b/web_src/js/features/repo-settings.js
@@ -39,7 +39,7 @@ export function initRepoSettingsCollaboration() {
             $text.text('(none)'); // prevent from misleading users when the access mode is undefined
           }
         }, 0);
-      }
+      },
     });
   });
 }
@@ -56,15 +56,15 @@ export function initRepoSettingSearchTeamBox() {
         $.each(response.data, (_i, item) => {
           items.push({
             title: item.name,
-            description: `${item.permission} access` // TODO: translate this string
+            description: `${item.permission} access`, // TODO: translate this string
           });
         });
 
         return {results: items};
-      }
+      },
     },
     searchFields: ['name', 'description'],
-    showNoResults: false
+    showNoResults: false,
   });
 }
 
diff --git a/web_src/js/features/repo-template.js b/web_src/js/features/repo-template.js
index 1e83e74780..5f63e8b3ba 100644
--- a/web_src/js/features/repo-template.js
+++ b/web_src/js/features/repo-template.js
@@ -29,13 +29,13 @@ export function initRepoTemplateSearch() {
             const filteredResponse = {success: true, results: []};
             filteredResponse.results.push({
               name: '',
-              value: ''
+              value: '',
             });
             // Parse the response from the api to work with our dropdown
             $.each(response.data, (_r, repo) => {
               filteredResponse.results.push({
                 name: htmlEscape(repo.repository.full_name),
-                value: repo.repository.id
+                value: repo.repository.id,
               });
             });
             return filteredResponse;
@@ -43,7 +43,7 @@ export function initRepoTemplateSearch() {
           cache: false,
         },
 
-        fullTextSearch: true
+        fullTextSearch: true,
       });
   };
   $('#uid').on('change', changeOwner);
diff --git a/web_src/js/features/repo-wiki.js b/web_src/js/features/repo-wiki.js
index d51bf35c81..03a2c68c5a 100644
--- a/web_src/js/features/repo-wiki.js
+++ b/web_src/js/features/repo-wiki.js
@@ -60,7 +60,7 @@ async function initRepoWikiFormEditor() {
         'gitea-code-inline', 'code', 'quote', '|', 'gitea-checkbox-empty', 'gitea-checkbox-checked', '|',
         'unordered-list', 'ordered-list', '|',
         'link', 'image', 'table', 'horizontal-rule', '|',
-        'preview', 'fullscreen', 'side-by-side', '|', 'gitea-switch-to-textarea'
+        'preview', 'fullscreen', 'side-by-side', '|', 'gitea-switch-to-textarea',
       ],
     },
   });
diff --git a/web_src/js/features/tribute.js b/web_src/js/features/tribute.js
index 055777be79..70a5de6913 100644
--- a/web_src/js/features/tribute.js
+++ b/web_src/js/features/tribute.js
@@ -25,7 +25,7 @@ function makeCollections({mentions, emoji}) {
       },
       menuItemTemplate: (item) => {
         return `<div class="tribute-item">${emojiHTML(item.original)}<span>${htmlEscape(item.original)}</span></div>`;
-      }
+      },
     });
   }
 
@@ -41,7 +41,7 @@ function makeCollections({mentions, emoji}) {
             ${item.original.fullname && item.original.fullname !== '' ? `<span class="fullname">${htmlEscape(item.original.fullname)}</span>` : ''}
           </div>
         `;
-      }
+      },
     });
   }
 
diff --git a/web_src/js/features/user-auth-webauthn.js b/web_src/js/features/user-auth-webauthn.js
index 363e039760..6dfbb4d765 100644
--- a/web_src/js/features/user-auth-webauthn.js
+++ b/web_src/js/features/user-auth-webauthn.js
@@ -26,7 +26,7 @@ export async function initUserAuthWebAuthn() {
   }
   try {
     const credential = await navigator.credentials.get({
-      publicKey: options.publicKey
+      publicKey: options.publicKey,
     });
     await verifyAssertion(credential);
   } catch (err) {
@@ -37,7 +37,7 @@ export async function initUserAuthWebAuthn() {
     delete options.publicKey.extensions.appid;
     try {
       const credential = await navigator.credentials.get({
-        publicKey: options.publicKey
+        publicKey: options.publicKey,
       });
       await verifyAssertion(credential);
     } catch (err) {
@@ -185,7 +185,7 @@ async function webAuthnRegisterRequest() {
 
   try {
     const credential = await navigator.credentials.create({
-      publicKey: options.publicKey
+      publicKey: options.publicKey,
     });
     await webauthnRegistered(credential);
   } catch (err) {
diff --git a/web_src/js/modules/tippy.js b/web_src/js/modules/tippy.js
index e7eb39f457..220c9e5512 100644
--- a/web_src/js/modules/tippy.js
+++ b/web_src/js/modules/tippy.js
@@ -148,7 +148,7 @@ export function initGlobalTooltips() {
   const observerConnect = (observer) => observer.observe(document, {
     subtree: true,
     childList: true,
-    attributeFilter: ['data-tooltip-content', 'title']
+    attributeFilter: ['data-tooltip-content', 'title'],
   });
   const observer = new MutationObserver((mutationList, observer) => {
     const pending = observer.takeRecords();
diff --git a/web_src/js/standalone/swagger.js b/web_src/js/standalone/swagger.js
index cb91089daf..00854ef5d7 100644
--- a/web_src/js/standalone/swagger.js
+++ b/web_src/js/standalone/swagger.js
@@ -21,11 +21,11 @@ window.addEventListener('load', async () => {
     docExpansion: 'none',
     defaultModelRendering: 'model', // don't show examples by default, because they may be incomplete
     presets: [
-      SwaggerUI.presets.apis
+      SwaggerUI.presets.apis,
     ],
     plugins: [
-      SwaggerUI.plugins.DownloadUrl
-    ]
+      SwaggerUI.plugins.DownloadUrl,
+    ],
   });
 
   window.ui = ui;
diff --git a/web_src/js/svg.js b/web_src/js/svg.js
index 6ad06f599d..3544b47c3d 100644
--- a/web_src/js/svg.js
+++ b/web_src/js/svg.js
@@ -189,7 +189,7 @@ export const SvgIcon = {
     name: {type: String, required: true},
     size: {type: Number, default: 16},
     className: {type: String, default: ''},
-    symbolId: {type: String}
+    symbolId: {type: String},
   },
   render() {
     let {svgOuter, svgInnerHtml} = svgParseOuterInner(this.name);
diff --git a/web_src/js/utils/dom.js b/web_src/js/utils/dom.js
index 016c3b55f0..703f15d41d 100644
--- a/web_src/js/utils/dom.js
+++ b/web_src/js/utils/dom.js
@@ -191,7 +191,7 @@ export function autosize(textarea, {viewportMarginBottom = 0} = {}) {
       textarea.removeEventListener('mousemove', onUserResize);
       textarea.removeEventListener('input', resizeToFit);
       textarea.form?.removeEventListener('reset', onFormReset);
-    }
+    },
   };
 }
 
diff --git a/web_src/js/webcomponents/polyfills.js b/web_src/js/webcomponents/polyfills.js
index 88c7276881..38f50fa02f 100644
--- a/web_src/js/webcomponents/polyfills.js
+++ b/web_src/js/webcomponents/polyfills.js
@@ -9,7 +9,7 @@ try {
       return {
         format(value) {
           return ` ${value} ${options.unit}`;
-        }
+        },
       };
     }
     return intlNumberFormat(locales, options);
diff --git a/webpack.config.js b/webpack.config.js
index 0a0f573a3c..8ab96b08ac 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -182,7 +182,7 @@ export default {
                 ],
               },
             },
-          }
+          },
         ],
       },
       {
@@ -195,14 +195,14 @@ export default {
         type: 'asset/resource',
         generator: {
           filename: 'fonts/[name].[contenthash:8][ext]',
-        }
+        },
       },
       {
         test: /\.png$/i,
         type: 'asset/resource',
         generator: {
           filename: 'img/webpack/[name].[contenthash:8][ext]',
-        }
+        },
       },
     ],
   },

From 3ea7437fa61a012ac34e0696196d3dd9f4839106 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Fri, 22 Mar 2024 20:51:29 +0100
Subject: [PATCH 53/74] Fix incorrect tailwind migration (#30007)

Fixes https://github.com/go-gitea/gitea/issues/30005. Regression from
https://github.com/go-gitea/gitea/pull/29945.

There was only once instance of `tw-content-center` before that PR, so I
just ran below command and reverted that one instance.

```sh
perl -p -i -e 's#tw-content-center#tw-items-center#g' web_src/js/**/* templates/**/* models/**/* tests/**/*
```

(cherry picked from commit 04f9ad056882fc3f21b247b16f84437adf0f36d8)

Conflicts:
	templates/repo/diff/conversation.tmpl
	templates/repo/header.tmpl
	templates/repo/issue/filter_list.tmpl
	templates/repo/issue/view_content/conversation.tmpl
	templates/repo/wiki/view.tmpl
	web_src/js/components/DashboardRepoList.vue
	re-ran the command after discarding the Gitea changes to
	ensure all Forgejo files are also covered
---
 templates/admin/emails/list.tmpl              |  2 +-
 templates/admin/org/list.tmpl                 |  2 +-
 templates/admin/repo/unadopted.tmpl           |  2 +-
 templates/admin/stacktrace-row.tmpl           |  4 +--
 templates/admin/stacktrace.tmpl               |  2 +-
 templates/base/head_navbar.tmpl               |  8 ++---
 templates/base/paginate.tmpl                  |  2 +-
 templates/explore/search.tmpl                 |  2 +-
 templates/explore/user_list.tmpl              |  2 +-
 templates/org/header.tmpl                     |  2 +-
 templates/org/home.tmpl                       |  4 +--
 templates/org/member/members.tmpl             |  2 +-
 templates/org/settings/labels.tmpl            |  2 +-
 templates/org/team/members.tmpl               |  4 +--
 templates/org/team/repositories.tmpl          |  2 +-
 templates/projects/view.tmpl                  |  2 +-
 templates/repo/actions/list.tmpl              |  2 +-
 templates/repo/actions/runs_list.tmpl         |  2 +-
 templates/repo/actions/status.tmpl            |  2 +-
 templates/repo/blame.tmpl                     |  6 ++--
 templates/repo/branch/list.tmpl               |  8 ++---
 templates/repo/branch_dropdown.tmpl           |  2 +-
 .../code/recently_pushed_new_branches.tmpl    |  2 +-
 templates/repo/commit_page.tmpl               | 12 +++----
 templates/repo/commits.tmpl                   |  2 +-
 templates/repo/commits_list_small.tmpl        |  2 +-
 templates/repo/commits_table.tmpl             |  4 +--
 templates/repo/diff/box.tmpl                  | 16 ++++-----
 templates/repo/diff/comments.tmpl             |  6 ++--
 templates/repo/diff/conversation.tmpl         | 12 +++----
 templates/repo/diff/new_review.tmpl           |  2 +-
 templates/repo/find/files.tmpl                |  2 +-
 templates/repo/forks.tmpl                     |  2 +-
 templates/repo/graph/commits.tmpl             |  6 ++--
 templates/repo/header.tmpl                    |  2 +-
 templates/repo/home.tmpl                      |  6 ++--
 templates/repo/issue/card.tmpl                |  2 +-
 templates/repo/issue/filter_list.tmpl         |  2 +-
 templates/repo/issue/milestone_issues.tmpl    |  2 +-
 templates/repo/issue/milestones.tmpl          |  2 +-
 templates/repo/issue/view_content.tmpl        |  6 ++--
 .../repo/issue/view_content/attachments.tmpl  |  2 +-
 .../repo/issue/view_content/comments.tmpl     | 14 ++++----
 .../repo/issue/view_content/conversation.tmpl | 14 ++++----
 .../repo/issue/view_content/sidebar.tmpl      | 36 +++++++++----------
 templates/repo/migrate/migrate.tmpl           |  2 +-
 templates/repo/projects/view.tmpl             |  2 +-
 templates/repo/pulls/tab_menu.tmpl            |  2 +-
 templates/repo/release/list.tmpl              |  2 +-
 templates/repo/release_tag_header.tmpl        |  2 +-
 templates/repo/settings/branches.tmpl         |  2 +-
 templates/repo/settings/collaboration.tmpl    |  2 +-
 templates/repo/settings/options.tmpl          |  2 +-
 templates/repo/tag/list.tmpl                  |  8 ++---
 templates/repo/view_file.tmpl                 |  6 ++--
 templates/repo/wiki/pages.tmpl                |  2 +-
 templates/repo/wiki/view.tmpl                 |  2 +-
 templates/shared/repo_search.tmpl             |  2 +-
 templates/shared/searchbottom.tmpl            |  4 +--
 templates/shared/secrets/add_list.tmpl        |  2 +-
 templates/shared/user/org_profile_avatar.tmpl |  2 +-
 templates/shared/variables/variable_list.tmpl |  2 +-
 templates/user/auth/signin_inner.tmpl         |  4 +--
 templates/user/auth/signup_inner.tmpl         |  4 +--
 templates/user/auth/webauthn.tmpl             |  2 +-
 templates/user/dashboard/milestones.tmpl      |  2 +-
 .../user/notification/notification_div.tmpl   | 10 +++---
 .../settings/applications_oauth2_list.tmpl    |  2 +-
 templates/user/settings/security/openid.tmpl  |  2 +-
 templates/webhook/new.tmpl                    |  2 +-
 tests/integration/release_test.go             |  2 +-
 web_src/js/components/ActionRunStatus.vue     |  2 +-
 web_src/js/components/DashboardRepoList.vue   | 12 +++----
 web_src/js/components/DiffFileList.vue        |  4 +--
 .../js/components/RepoBranchTagSelector.vue   |  2 +-
 web_src/js/components/RepoCodeFrequency.vue   |  2 +-
 web_src/js/components/RepoContributors.vue    |  2 +-
 web_src/js/components/RepoRecentCommits.vue   |  2 +-
 web_src/js/features/repo-issue-content.js     |  2 +-
 79 files changed, 163 insertions(+), 163 deletions(-)

diff --git a/templates/admin/emails/list.tmpl b/templates/admin/emails/list.tmpl
index 0b5249fbd9..b72aef8f35 100644
--- a/templates/admin/emails/list.tmpl
+++ b/templates/admin/emails/list.tmpl
@@ -4,7 +4,7 @@
 			{{ctx.Locale.Tr "admin.emails.email_manage_panel"}} ({{ctx.Locale.Tr "admin.total" .Total}})
 		</h4>
 		<div class="ui attached segment">
-			<div class="ui secondary filter menu tw-content-center gt-mx-0">
+			<div class="ui secondary filter menu tw-items-center gt-mx-0">
 				<form class="ui form ignore-dirty tw-flex-1">
 					{{template "shared/search/combo" dict "Value" .Keyword}}
 				</form>
diff --git a/templates/admin/org/list.tmpl b/templates/admin/org/list.tmpl
index abd43d297e..ca0ee30092 100644
--- a/templates/admin/org/list.tmpl
+++ b/templates/admin/org/list.tmpl
@@ -7,7 +7,7 @@
 			</div>
 		</h4>
 		<div class="ui attached segment">
-			<div class="ui secondary filter menu tw-content-center gt-mx-0">
+			<div class="ui secondary filter menu tw-items-center gt-mx-0">
 				<form class="ui form ignore-dirty tw-flex-1">
 					{{template "shared/search/combo" dict "Value" .Keyword "Placeholder" (ctx.Locale.Tr "search.org_kind")}}
 				</form>
diff --git a/templates/admin/repo/unadopted.tmpl b/templates/admin/repo/unadopted.tmpl
index eb8188de14..9166a844a7 100644
--- a/templates/admin/repo/unadopted.tmpl
+++ b/templates/admin/repo/unadopted.tmpl
@@ -20,7 +20,7 @@
 				{{if .Dirs}}
 					<div class="ui aligned divided list">
 						{{range $dirI, $dir := .Dirs}}
-							<div class="item tw-flex tw-content-center">
+							<div class="item tw-flex tw-items-center">
 								<span class="tw-flex-1"> {{svg "octicon-file-directory-fill"}} {{$dir}}</span>
 								<div>
 									<button class="ui button primary show-modal gt-p-3" data-modal="#adopt-unadopted-modal-{{$dirI}}">{{svg "octicon-plus"}} {{ctx.Locale.Tr "repo.adopt_preexisting_label"}}</button>
diff --git a/templates/admin/stacktrace-row.tmpl b/templates/admin/stacktrace-row.tmpl
index fdce81eda7..3f639ba161 100644
--- a/templates/admin/stacktrace-row.tmpl
+++ b/templates/admin/stacktrace-row.tmpl
@@ -1,5 +1,5 @@
 <div class="item">
-	<div class="tw-flex tw-content-center">
+	<div class="tw-flex tw-items-center">
 		<div class="icon gt-ml-3 gt-mr-3">
 			{{if eq .Process.Type "request"}}
 				{{svg "octicon-globe" 16}}
@@ -40,7 +40,7 @@
 						</summary>
 						<div class="list">
 							{{range .Entry}}
-								<div class="item tw-flex tw-content-center">
+								<div class="item tw-flex tw-items-center">
 									<span class="icon gt-mr-4">{{svg "octicon-dot-fill" 16}}</span>
 									<div class="content tw-flex-1">
 										<div class="header"><code>{{.Function}}</code></div>
diff --git a/templates/admin/stacktrace.tmpl b/templates/admin/stacktrace.tmpl
index 3c13c1e9dd..e324570c96 100644
--- a/templates/admin/stacktrace.tmpl
+++ b/templates/admin/stacktrace.tmpl
@@ -1,7 +1,7 @@
 {{template "admin/layout_head" (dict "ctxData" . "pageClass" "admin monitor")}}
 <div class="admin-setting-content">
 
-	<div class="tw-flex tw-content-center">
+	<div class="tw-flex tw-items-center">
 		<div class="tw-flex-1">
 			<div class="ui compact small menu">
 				<a class="{{if eq .ShowGoroutineList "process"}}active {{end}}item" href="?show=process">{{ctx.Locale.Tr "admin.monitor.process"}}</a>
diff --git a/templates/base/head_navbar.tmpl b/templates/base/head_navbar.tmpl
index 03ece45fe2..ad80d3367c 100644
--- a/templates/base/head_navbar.tmpl
+++ b/templates/base/head_navbar.tmpl
@@ -56,7 +56,7 @@
 	<div class="navbar-right ui secondary menu">
 		{{if and .IsSigned .MustChangePassword}}
 			<div class="ui dropdown jump item" data-tooltip-content="{{ctx.Locale.Tr "user_profile_and_more"}}">
-				<span class="text tw-flex tw-content-center">
+				<span class="text tw-flex tw-items-center">
 					{{ctx.AvatarUtils.Avatar .SignedUser 24 "gt-mr-2"}}
 					<span class="mobile-only gt-ml-3">{{.SignedUser.Name}}</span>
 					<span class="not-mobile">{{svg "octicon-triangle-down"}}</span>
@@ -83,8 +83,8 @@
 				<span class="mobile-only gt-ml-3">{{ctx.Locale.Tr "active_stopwatch"}}</span>
 			</a>
 			<div class="active-stopwatch-popup item tippy-target gt-p-3">
-				<div class="tw-flex tw-content-center">
-					<a class="stopwatch-link tw-flex tw-content-center" href="{{.ActiveStopwatch.IssueLink}}">
+				<div class="tw-flex tw-items-center">
+					<a class="stopwatch-link tw-flex tw-items-center" href="{{.ActiveStopwatch.IssueLink}}">
 						{{svg "octicon-issue-opened" 16 "gt-mr-3"}}
 						<span class="stopwatch-issue">{{.ActiveStopwatch.RepoSlug}}#{{.ActiveStopwatch.IssueIndex}}</span>
 						<span class="ui primary label stopwatch-time gt-my-0 gt-mx-4" data-seconds="{{.ActiveStopwatch.Seconds}}">
@@ -142,7 +142,7 @@
 			</div><!-- end dropdown menu create new -->
 
 			<div class="ui dropdown jump item gt-mx-0 gt-pr-3" data-tooltip-content="{{ctx.Locale.Tr "user_profile_and_more"}}">
-				<span class="text tw-flex tw-content-center">
+				<span class="text tw-flex tw-items-center">
 					{{ctx.AvatarUtils.Avatar .SignedUser 24 "gt-mr-2"}}
 					<span class="mobile-only gt-ml-3">{{.SignedUser.Name}}</span>
 					<span class="not-mobile">{{svg "octicon-triangle-down"}}</span>
diff --git a/templates/base/paginate.tmpl b/templates/base/paginate.tmpl
index 8c2adc1f94..4455c20b3f 100644
--- a/templates/base/paginate.tmpl
+++ b/templates/base/paginate.tmpl
@@ -17,7 +17,7 @@
 					{{if eq .Num -1}}
 						<a class="disabled item">...</a>
 					{{else}}
-						<a class="{{if .IsCurrent}}active {{end}}item tw-content-center" {{if not .IsCurrent}}href="{{$paginationLink}}?page={{.Num}}{{if $paginationParams}}&{{$paginationParams}}{{end}}"{{end}}>{{.Num}}</a>
+						<a class="{{if .IsCurrent}}active {{end}}item tw-items-center" {{if not .IsCurrent}}href="{{$paginationLink}}?page={{.Num}}{{if $paginationParams}}&{{$paginationParams}}{{end}}"{{end}}>{{.Num}}</a>
 					{{end}}
 				{{end}}
 				<a class="{{if not .HasNext}}disabled{{end}} item navigation" {{if .HasNext}}href="{{$paginationLink}}?page={{.Next}}{{if $paginationParams}}&{{$paginationParams}}{{end}}"{{end}}>
diff --git a/templates/explore/search.tmpl b/templates/explore/search.tmpl
index 505fc64548..c12ff325f9 100644
--- a/templates/explore/search.tmpl
+++ b/templates/explore/search.tmpl
@@ -1,4 +1,4 @@
-<div class="ui small secondary filter menu tw-content-center gt-mx-0">
+<div class="ui small secondary filter menu tw-items-center gt-mx-0">
 	<form class="ui form ignore-dirty tw-flex-1">
 		{{if .PageIsExploreUsers}}
 			{{template "shared/search/combo" dict "Value" .Keyword "Placeholder" (ctx.Locale.Tr "search.user_kind")}}
diff --git a/templates/explore/user_list.tmpl b/templates/explore/user_list.tmpl
index e49ca1d069..f2cf939ffb 100644
--- a/templates/explore/user_list.tmpl
+++ b/templates/explore/user_list.tmpl
@@ -1,6 +1,6 @@
 <div class="flex-list">
 	{{range .Users}}
-		<div class="flex-item tw-content-center">
+		<div class="flex-item tw-items-center">
 			<div class="flex-item-leading">
 				{{ctx.AvatarUtils.Avatar . 48}}
 			</div>
diff --git a/templates/org/header.tmpl b/templates/org/header.tmpl
index c8a0ad3ab0..204ba7e3c1 100644
--- a/templates/org/header.tmpl
+++ b/templates/org/header.tmpl
@@ -7,7 +7,7 @@
 				{{if .Org.Visibility.IsLimited}}<span class="ui large basic horizontal label">{{ctx.Locale.Tr "org.settings.visibility.limited_shortname"}}</span>{{end}}
 				{{if .Org.Visibility.IsPrivate}}<span class="ui large basic horizontal label">{{ctx.Locale.Tr "org.settings.visibility.private_shortname"}}</span>{{end}}
 			</span>
-			<span class="tw-flex tw-content-center gt-gap-2 tw-ml-auto gt-font-16 tw-whitespace-nowrap">
+			<span class="tw-flex tw-items-center gt-gap-2 tw-ml-auto gt-font-16 tw-whitespace-nowrap">
 				{{if .EnableFeed}}
 					<a class="ui basic label button gt-mr-0" href="{{.Org.HomeLink}}.rss" data-tooltip-content="{{ctx.Locale.Tr "rss_feed"}}">
 						{{svg "octicon-rss" 24}}
diff --git a/templates/org/home.tmpl b/templates/org/home.tmpl
index 0ea0a280fd..920a42a5da 100644
--- a/templates/org/home.tmpl
+++ b/templates/org/home.tmpl
@@ -32,7 +32,7 @@
 				{{if .NumMembers}}
 					<h4 class="ui top attached header tw-flex">
 						<strong class="tw-flex-1">{{ctx.Locale.Tr "org.members"}}</strong>
-						<a class="text grey tw-flex tw-content-center" href="{{.OrgLink}}/members"><span>{{.NumMembers}}</span> {{svg "octicon-chevron-right"}}</a>
+						<a class="text grey tw-flex tw-items-center" href="{{.OrgLink}}/members"><span>{{.NumMembers}}</span> {{svg "octicon-chevron-right"}}</a>
 					</h4>
 					<div class="ui attached segment members">
 						{{$isMember := .IsOrganizationMember}}
@@ -46,7 +46,7 @@
 				{{if .IsOrganizationMember}}
 					<div class="ui top attached header tw-flex">
 						<strong class="tw-flex-1">{{ctx.Locale.Tr "org.teams"}}</strong>
-						<a class="text grey tw-flex tw-content-center" href="{{.OrgLink}}/teams"><span>{{.Org.NumTeams}}</span> {{svg "octicon-chevron-right"}}</a>
+						<a class="text grey tw-flex tw-items-center" href="{{.OrgLink}}/teams"><span>{{.Org.NumTeams}}</span> {{svg "octicon-chevron-right"}}</a>
 					</div>
 					<div class="ui attached table segment teams">
 						{{range .Teams}}
diff --git a/templates/org/member/members.tmpl b/templates/org/member/members.tmpl
index cb9e60da29..4388dc9520 100644
--- a/templates/org/member/members.tmpl
+++ b/templates/org/member/members.tmpl
@@ -7,7 +7,7 @@
 		<div class="flex-list">
 			{{range .Members}}
 				{{$isPublic := index $.MembersIsPublicMember .ID}}
-				<div class="flex-item {{if $.PublicOnly}}tw-content-center{{end}}">
+				<div class="flex-item {{if $.PublicOnly}}tw-items-center{{end}}">
 					<div class="flex-item-leading">
 						<a href="{{.HomeLink}}">{{ctx.AvatarUtils.Avatar . 48}}</a>
 					</div>
diff --git a/templates/org/settings/labels.tmpl b/templates/org/settings/labels.tmpl
index 19a7d5355e..25a562c975 100644
--- a/templates/org/settings/labels.tmpl
+++ b/templates/org/settings/labels.tmpl
@@ -1,6 +1,6 @@
 {{template "org/settings/layout_head" (dict "ctxData" . "pageClass" "organization settings labels")}}
 				<div class="org-setting-content">
-					<div class="tw-flex tw-content-center">
+					<div class="tw-flex tw-items-center">
 						<div class="tw-flex-1">
 							{{ctx.Locale.Tr "org.settings.labels_desc"}}
 						</div>
diff --git a/templates/org/team/members.tmpl b/templates/org/team/members.tmpl
index d86aeb7ce4..65430cbda3 100644
--- a/templates/org/team/members.tmpl
+++ b/templates/org/team/members.tmpl
@@ -24,7 +24,7 @@
 				<div class="ui attached segment">
 					<div class="flex-list">
 						{{range .Team.Members}}
-							<div class="flex-item tw-content-center">
+							<div class="flex-item tw-items-center">
 								<div class="flex-item-leading">
 									<a href="{{.HomeLink}}">{{ctx.AvatarUtils.Avatar . 32}}</a>
 								</div>
@@ -56,7 +56,7 @@
 				<div class="ui attached segment">
 					<div class="flex-list">
 						{{range .Invites}}
-							<div class="flex-item tw-content-center">
+							<div class="flex-item tw-items-center">
 								<div class="flex-item-main">
 									{{.Email}}
 								</div>
diff --git a/templates/org/team/repositories.tmpl b/templates/org/team/repositories.tmpl
index 9efe8f9f09..0c59eafbe1 100644
--- a/templates/org/team/repositories.tmpl
+++ b/templates/org/team/repositories.tmpl
@@ -28,7 +28,7 @@
 				<div class="ui attached segment">
 					<div class="flex-list">
 						{{range .Team.Repos}}
-							<div class="flex-item tw-content-center">
+							<div class="flex-item tw-items-center">
 								<div class="flex-item-leading">
 									{{template "repo/icon" .}}
 								</div>
diff --git a/templates/projects/view.tmpl b/templates/projects/view.tmpl
index d36ecdfc85..93c2cdbb57 100644
--- a/templates/projects/view.tmpl
+++ b/templates/projects/view.tmpl
@@ -1,7 +1,7 @@
 {{$canWriteProject := and .CanWriteProjects (or (not .Repository) (not .Repository.IsArchived))}}
 
 <div class="ui container">
-	<div class="tw-flex tw-justify-between tw-content-center gt-mb-4">
+	<div class="tw-flex tw-justify-between tw-items-center gt-mb-4">
 		<h2 class="gt-mb-0">{{.Project.Title}}</h2>
 		{{if $canWriteProject}}
 			<div class="ui compact mini menu">
diff --git a/templates/repo/actions/list.tmpl b/templates/repo/actions/list.tmpl
index f4215829ba..916949d4f9 100644
--- a/templates/repo/actions/list.tmpl
+++ b/templates/repo/actions/list.tmpl
@@ -25,7 +25,7 @@
 				</div>
 			</div>
 			<div class="twelve wide column content">
-				<div class="ui secondary filter menu tw-justify-end tw-flex tw-content-center">
+				<div class="ui secondary filter menu tw-justify-end tw-flex tw-items-center">
 					<!-- Actor -->
 					<div class="ui{{if not .Actors}} disabled{{end}} dropdown jump item">
 						<span class="text">{{ctx.Locale.Tr "actions.runs.actor"}}</span>
diff --git a/templates/repo/actions/runs_list.tmpl b/templates/repo/actions/runs_list.tmpl
index b898837a26..d393df6539 100644
--- a/templates/repo/actions/runs_list.tmpl
+++ b/templates/repo/actions/runs_list.tmpl
@@ -6,7 +6,7 @@
 	</div>
 	{{end}}
 	{{range .Runs}}
-		<div class="flex-item tw-content-center">
+		<div class="flex-item tw-items-center">
 			<div class="flex-item-leading">
 				{{template "repo/actions/status" (dict "status" .Status.String)}}
 			</div>
diff --git a/templates/repo/actions/status.tmpl b/templates/repo/actions/status.tmpl
index e42eafe8f6..a0e02cf8d7 100644
--- a/templates/repo/actions/status.tmpl
+++ b/templates/repo/actions/status.tmpl
@@ -12,7 +12,7 @@
 {{- $className = .className -}}
 {{- end -}}
 
-<span class="tw-flex tw-content-center" data-tooltip-content="{{ctx.Locale.Tr (printf "actions.status.%s" .status)}}">
+<span class="tw-flex tw-items-center" data-tooltip-content="{{ctx.Locale.Tr (printf "actions.status.%s" .status)}}">
 {{if eq .status "success"}}
 	{{svg "octicon-check-circle-fill" $size (printf "text green %s" $className)}}
 {{else if eq .status "skipped"}}
diff --git a/templates/repo/blame.tmpl b/templates/repo/blame.tmpl
index 4d4f2069ee..67046b814c 100644
--- a/templates/repo/blame.tmpl
+++ b/templates/repo/blame.tmpl
@@ -11,11 +11,11 @@
 	{{end}}
 {{end}}
 <div class="{{TabSizeClass .Editorconfig .FileName}} non-diff-file-content">
-	<h4 class="file-header ui top attached header tw-flex tw-content-center tw-justify-between tw-flex-wrap">
-		<div class="file-header-left tw-flex tw-content-center gt-py-3 gt-pr-4">
+	<h4 class="file-header ui top attached header tw-flex tw-items-center tw-justify-between tw-flex-wrap">
+		<div class="file-header-left tw-flex tw-items-center gt-py-3 gt-pr-4">
 			{{template "repo/file_info" .}}
 		</div>
-		<div class="file-header-right file-actions tw-flex tw-content-center tw-flex-wrap">
+		<div class="file-header-right file-actions tw-flex tw-items-center tw-flex-wrap">
 			<div class="ui buttons">
 				<a class="ui tiny button" href="{{$.RawFileLink}}">{{ctx.Locale.Tr "repo.file_raw"}}</a>
 				{{if not .IsViewCommit}}
diff --git a/templates/repo/branch/list.tmpl b/templates/repo/branch/list.tmpl
index 21121c4f09..7e061696e4 100644
--- a/templates/repo/branch/list.tmpl
+++ b/templates/repo/branch/list.tmpl
@@ -25,7 +25,7 @@
 									<button class="btn interact-fg gt-px-2" data-clipboard-text="{{.DefaultBranchBranch.DBBranch.Name}}">{{svg "octicon-copy" 14}}</button>
 									{{template "repo/commit_statuses" dict "Status" (index $.CommitStatus .DefaultBranchBranch.DBBranch.CommitID) "Statuses" (index $.CommitStatuses .DefaultBranchBranch.DBBranch.CommitID)}}
 								</div>
-								<p class="info tw-flex tw-content-center gt-my-2">{{svg "octicon-git-commit" 16 "gt-mr-2"}}<a href="{{.RepoLink}}/commit/{{PathEscape .DefaultBranchBranch.DBBranch.CommitID}}">{{ShortSha .DefaultBranchBranch.DBBranch.CommitID}}</a> · <span class="commit-message">{{RenderCommitMessage $.Context .DefaultBranchBranch.DBBranch.CommitMessage (.Repository.ComposeMetas ctx)}}</span> · {{ctx.Locale.Tr "org.repo_updated"}} {{TimeSince .DefaultBranchBranch.DBBranch.CommitTime.AsTime ctx.Locale}}{{if .DefaultBranchBranch.DBBranch.Pusher}} &nbsp;{{template "shared/user/avatarlink" dict "user" .DefaultBranchBranch.DBBranch.Pusher}}{{template "shared/user/namelink" .DefaultBranchBranch.DBBranch.Pusher}}{{end}}</p>
+								<p class="info tw-flex tw-items-center gt-my-2">{{svg "octicon-git-commit" 16 "gt-mr-2"}}<a href="{{.RepoLink}}/commit/{{PathEscape .DefaultBranchBranch.DBBranch.CommitID}}">{{ShortSha .DefaultBranchBranch.DBBranch.CommitID}}</a> · <span class="commit-message">{{RenderCommitMessage $.Context .DefaultBranchBranch.DBBranch.CommitMessage (.Repository.ComposeMetas ctx)}}</span> · {{ctx.Locale.Tr "org.repo_updated"}} {{TimeSince .DefaultBranchBranch.DBBranch.CommitTime.AsTime ctx.Locale}}{{if .DefaultBranchBranch.DBBranch.Pusher}} &nbsp;{{template "shared/user/avatarlink" dict "user" .DefaultBranchBranch.DBBranch.Pusher}}{{template "shared/user/namelink" .DefaultBranchBranch.DBBranch.Pusher}}{{end}}</p>
 							</td>
 							<td class="right aligned middle aligned overflow-visible">
 								{{if and $.IsWriter (not $.Repository.IsArchived) (not .IsDeleted)}}
@@ -67,8 +67,8 @@
 			</div>
 		{{end}}
 
-		<h4 class="ui top attached header tw-flex tw-content-center tw-justify-between">
-			<div class="tw-flex tw-content-center">
+		<h4 class="ui top attached header tw-flex tw-items-center tw-justify-between">
+			<div class="tw-flex tw-items-center">
 				{{ctx.Locale.Tr "repo.branches"}}
 			</div>
 		</h4>
@@ -98,7 +98,7 @@
 									<button class="btn interact-fg gt-px-2" data-clipboard-text="{{.DBBranch.Name}}">{{svg "octicon-copy" 14}}</button>
 									{{template "repo/commit_statuses" dict "Status" (index $.CommitStatus .DBBranch.CommitID) "Statuses" (index $.CommitStatuses .DBBranch.CommitID)}}
 								</div>
-								<p class="info tw-flex tw-content-center gt-my-2">{{svg "octicon-git-commit" 16 "gt-mr-2"}}<a href="{{$.RepoLink}}/commit/{{PathEscape .DBBranch.CommitID}}">{{ShortSha .DBBranch.CommitID}}</a> · <span class="commit-message">{{RenderCommitMessage $.Context .DBBranch.CommitMessage ($.Repository.ComposeMetas ctx)}}</span> · {{ctx.Locale.Tr "org.repo_updated"}} {{TimeSince .DBBranch.CommitTime.AsTime ctx.Locale}}{{if .DBBranch.Pusher}} &nbsp;{{template "shared/user/avatarlink" dict "user" .DBBranch.Pusher}} &nbsp;{{template "shared/user/namelink" .DBBranch.Pusher}}{{end}}</p>
+								<p class="info tw-flex tw-items-center gt-my-2">{{svg "octicon-git-commit" 16 "gt-mr-2"}}<a href="{{$.RepoLink}}/commit/{{PathEscape .DBBranch.CommitID}}">{{ShortSha .DBBranch.CommitID}}</a> · <span class="commit-message">{{RenderCommitMessage $.Context .DBBranch.CommitMessage ($.Repository.ComposeMetas ctx)}}</span> · {{ctx.Locale.Tr "org.repo_updated"}} {{TimeSince .DBBranch.CommitTime.AsTime ctx.Locale}}{{if .DBBranch.Pusher}} &nbsp;{{template "shared/user/avatarlink" dict "user" .DBBranch.Pusher}} &nbsp;{{template "shared/user/namelink" .DBBranch.Pusher}}{{end}}</p>
 							{{end}}
 							</td>
 							<td class="two wide ui">
diff --git a/templates/repo/branch_dropdown.tmpl b/templates/repo/branch_dropdown.tmpl
index 19da1b6474..e389a3c758 100644
--- a/templates/repo/branch_dropdown.tmpl
+++ b/templates/repo/branch_dropdown.tmpl
@@ -71,7 +71,7 @@
 	{{/* show dummy elements before Vue componment is mounted, this code must match the code in BranchTagSelector.vue */}}
 	<div class="ui dropdown custom">
 		<button class="branch-dropdown-button gt-ellipsis ui basic small compact button tw-flex gt-m-0">
-			<span class="text tw-flex tw-content-center gt-mr-2">
+			<span class="text tw-flex tw-items-center gt-mr-2">
 				{{if .release}}
 					{{ctx.Locale.Tr "repo.release.compare"}}
 				{{else}}
diff --git a/templates/repo/code/recently_pushed_new_branches.tmpl b/templates/repo/code/recently_pushed_new_branches.tmpl
index 7e138c41e9..8dd401d283 100644
--- a/templates/repo/code/recently_pushed_new_branches.tmpl
+++ b/templates/repo/code/recently_pushed_new_branches.tmpl
@@ -1,5 +1,5 @@
 {{range .RecentlyPushedNewBranches}}
-	<div class="ui positive message tw-flex tw-content-center">
+	<div class="ui positive message tw-flex tw-items-center">
 		<div class="tw-flex-1">
 			{{$timeSince := TimeSince .CommitTime.AsTime ctx.Locale}}
 			{{$repo := .GetRepo $.Context}}
diff --git a/templates/repo/commit_page.tmpl b/templates/repo/commit_page.tmpl
index 1670781e24..345c28f475 100644
--- a/templates/repo/commit_page.tmpl
+++ b/templates/repo/commit_page.tmpl
@@ -139,8 +139,8 @@
 			{{end}}
 			{{template "repo/commit_load_branches_and_tags" .}}
 		</div>
-		<div class="ui attached segment tw-flex tw-content-center tw-justify-between gt-py-2 commit-header-row tw-flex-wrap {{$class}}">
-				<div class="tw-flex tw-content-center author">
+		<div class="ui attached segment tw-flex tw-items-center tw-justify-between gt-py-2 commit-header-row tw-flex-wrap {{$class}}">
+				<div class="tw-flex tw-items-center author">
 					{{if .Author}}
 						{{ctx.AvatarUtils.Avatar .Author 28 "gt-mr-3"}}
 						{{if .Author.FullName}}
@@ -164,7 +164,7 @@
 						{{end}}
 					{{end}}
 				</div>
-				<div class="ui horizontal list tw-flex tw-content-center">
+				<div class="ui horizontal list tw-flex tw-items-center">
 					{{if .Parents}}
 						<div class="item">
 							<span>{{ctx.Locale.Tr "repo.diff.parent"}}</span>
@@ -184,8 +184,8 @@
 				</div>
 		</div>
 		{{if .Commit.Signature}}
-			<div class="ui bottom attached message tw-text-left tw-flex tw-content-center tw-justify-between commit-header-row tw-flex-wrap gt-mb-0 {{$class}}">
-				<div class="tw-flex tw-content-center">
+			<div class="ui bottom attached message tw-text-left tw-flex tw-items-center tw-justify-between commit-header-row tw-flex-wrap gt-mb-0 {{$class}}">
+				<div class="tw-flex tw-items-center">
 					{{if .Verification.Verified}}
 						{{if ne .Verification.SigningUser.ID 0}}
 							{{svg "gitea-lock" 16 "gt-mr-3"}}
@@ -209,7 +209,7 @@
 						<span class="ui text">{{ctx.Locale.Tr .Verification.Reason}}</span>
 					{{end}}
 				</div>
-				<div class="tw-flex tw-content-center">
+				<div class="tw-flex tw-items-center">
 					{{if .Verification.Verified}}
 						{{if ne .Verification.SigningUser.ID 0}}
 							{{svg "octicon-verified" 16 "gt-mr-3"}}
diff --git a/templates/repo/commits.tmpl b/templates/repo/commits.tmpl
index 29afc350f6..1263bc9dd4 100644
--- a/templates/repo/commits.tmpl
+++ b/templates/repo/commits.tmpl
@@ -4,7 +4,7 @@
 	<div class="ui container">
 		{{template "repo/sub_menu" .}}
 		<div class="repo-button-row">
-			<div class="tw-flex tw-content-center">
+			<div class="tw-flex tw-items-center">
 				{{template "repo/branch_dropdown" dict "root" . "ContainerClasses" "gt-mr-2"}}
 				<a href="{{.RepoLink}}/graph" class="ui basic small compact button">
 					{{svg "octicon-git-branch"}}
diff --git a/templates/repo/commits_list_small.tmpl b/templates/repo/commits_list_small.tmpl
index 53834f7acb..b195f06483 100644
--- a/templates/repo/commits_list_small.tmpl
+++ b/templates/repo/commits_list_small.tmpl
@@ -13,7 +13,7 @@
 
 		{{$commitLink:= printf "%s/commit/%s" $.comment.Issue.PullRequest.BaseRepo.Link (PathEscape .ID.String)}}
 
-		<span class="shabox tw-flex tw-content-center tw-float-right">
+		<span class="shabox tw-flex tw-items-center tw-float-right">
 			{{template "repo/commit_statuses" dict "Status" .Status "Statuses" .Statuses}}
 			{{$class := "ui sha label"}}
 			{{if .Signature}}
diff --git a/templates/repo/commits_table.tmpl b/templates/repo/commits_table.tmpl
index 330130ac0d..48e9368c65 100644
--- a/templates/repo/commits_table.tmpl
+++ b/templates/repo/commits_table.tmpl
@@ -1,5 +1,5 @@
-<h4 class="ui top attached header commits-table tw-flex tw-content-center tw-justify-between">
-	<div class="commits-table-left tw-flex tw-content-center">
+<h4 class="ui top attached header commits-table tw-flex tw-items-center tw-justify-between">
+	<div class="commits-table-left tw-flex tw-items-center">
 		{{if or .PageIsCommits (gt .CommitCount 0)}}
 			{{.CommitCount}} {{ctx.Locale.Tr "repo.commits.commits"}}
 		{{else if .IsNothingToCompare}}
diff --git a/templates/repo/diff/box.tmpl b/templates/repo/diff/box.tmpl
index fc6a5e0c3c..34194ebbdc 100644
--- a/templates/repo/diff/box.tmpl
+++ b/templates/repo/diff/box.tmpl
@@ -1,7 +1,7 @@
 {{$showFileTree := (and (not .DiffNotAvailable) (gt .Diff.NumFiles 1))}}
 <div>
 	<div class="diff-detail-box diff-box">
-		<div class="tw-flex tw-content-center tw-flex-wrap gt-gap-3 gt-ml-1">
+		<div class="tw-flex tw-items-center tw-flex-wrap gt-gap-3 gt-ml-1">
 			{{if $showFileTree}}
 				<button class="diff-toggle-file-tree-button not-mobile btn interact-fg" data-show-text="{{ctx.Locale.Tr "repo.diff.show_file_tree"}}" data-hide-text="{{ctx.Locale.Tr "repo.diff.hide_file_tree"}}">
 					{{/* the icon meaning is reversed here, "octicon-sidebar-collapse" means show the file tree */}}
@@ -18,14 +18,14 @@
 				</script>
 			{{end}}
 			{{if not .DiffNotAvailable}}
-				<div class="diff-detail-stats tw-flex tw-content-center tw-flex-wrap">
+				<div class="diff-detail-stats tw-flex tw-items-center tw-flex-wrap">
 					{{svg "octicon-diff" 16 "gt-mr-2"}}{{ctx.Locale.Tr "repo.diff.stats_desc" .Diff.NumFiles .Diff.TotalAddition .Diff.TotalDeletion}}
 				</div>
 			{{end}}
 		</div>
 		<div class="diff-detail-actions">
 			{{if and .PageIsPullFiles $.SignedUserID (not .IsArchived) (not .DiffNotAvailable)}}
-				<div class="not-mobile tw-flex tw-content-center tw-flex-col tw-whitespace-nowrap gt-mr-2">
+				<div class="not-mobile tw-flex tw-items-center tw-flex-col tw-whitespace-nowrap gt-mr-2">
 					<label for="viewed-files-summary" id="viewed-files-summary-label" data-text-changed-template="{{ctx.Locale.Tr "repo.pulls.viewed_files_label"}}">
 						{{ctx.Locale.Tr "repo.pulls.viewed_files_label" .Diff.NumViewedFiles .Diff.NumFiles}}
 					</label>
@@ -110,8 +110,8 @@
 					{{$isExpandable := or (gt $file.Addition 0) (gt $file.Deletion 0) $file.IsBin}}
 					{{$isReviewFile := and $.IsSigned $.PageIsPullFiles (not $.IsArchived) $.IsShowingAllCommits}}
 					<div class="diff-file-box diff-box file-content {{TabSizeClass $.Editorconfig $file.Name}} gt-mt-0" id="diff-{{$file.NameHash}}" data-old-filename="{{$file.OldName}}" data-new-filename="{{$file.Name}}" {{if or ($file.ShouldBeHidden) (not $isExpandable)}}data-folded="true"{{end}}>
-						<h4 class="diff-file-header sticky-2nd-row ui top attached normal header tw-flex tw-content-center tw-justify-between tw-flex-wrap">
-							<div class="diff-file-name tw-flex tw-content-center gt-gap-2 tw-flex-wrap">
+						<h4 class="diff-file-header sticky-2nd-row ui top attached normal header tw-flex tw-items-center tw-justify-between tw-flex-wrap">
+							<div class="diff-file-name tw-flex tw-items-center gt-gap-2 tw-flex-wrap">
 								<button class="fold-file btn interact-bg gt-p-2{{if not $isExpandable}} tw-invisible{{end}}">
 									{{if $file.ShouldBeHidden}}
 										{{svg "octicon-chevron-right" 18}}
@@ -119,7 +119,7 @@
 										{{svg "octicon-chevron-down" 18}}
 									{{end}}
 								</button>
-								<div class="gt-font-semibold tw-flex tw-content-center gt-mono">
+								<div class="gt-font-semibold tw-flex tw-items-center gt-mono">
 									{{if $file.IsBin}}
 										<span class="gt-ml-1 gt-mr-3">
 											{{ctx.Locale.Tr "repo.diff.bin"}}
@@ -144,7 +144,7 @@
 									<span class="gt-ml-4 gt-mono">{{ctx.Locale.Tr ($file.ModeTranslationKey $file.Mode)}}</span>
 								{{end}}
 							</div>
-							<div class="diff-file-header-actions tw-flex tw-content-center gt-gap-2 tw-flex-wrap">
+							<div class="diff-file-header-actions tw-flex tw-items-center gt-gap-2 tw-flex-wrap">
 								{{if $showFileViewToggle}}
 									<div class="ui compact icon buttons">
 										<button class="ui tiny basic button file-view-toggle" data-toggle-selector="#diff-source-{{$file.NameHash}}" data-tooltip-content="{{ctx.Locale.Tr "repo.file_view_source"}}">{{svg "octicon-code"}}</button>
@@ -221,7 +221,7 @@
 
 				{{if .Diff.IsIncomplete}}
 					<div class="diff-file-box diff-box file-content gt-mt-3" id="diff-incomplete">
-						<h4 class="ui top attached normal header tw-flex tw-content-center tw-justify-between">
+						<h4 class="ui top attached normal header tw-flex tw-items-center tw-justify-between">
 							{{ctx.Locale.Tr "repo.diff.too_many_files"}}
 							<a class="ui basic tiny button" id="diff-show-more-files" data-href="?skip-to={{.Diff.End}}&file-only=true">{{ctx.Locale.Tr "repo.diff.show_more"}}</a>
 						</h4>
diff --git a/templates/repo/diff/comments.tmpl b/templates/repo/diff/comments.tmpl
index 6747328bb7..b561f094c5 100644
--- a/templates/repo/diff/comments.tmpl
+++ b/templates/repo/diff/comments.tmpl
@@ -8,8 +8,8 @@
 		{{template "shared/user/avatarlink" dict "user" .Poster}}
 	{{end}}
 	<div class="content comment-container">
-		<div class="ui top attached header comment-header tw-flex tw-content-center tw-justify-between">
-			<div class="comment-header-left tw-flex tw-content-center">
+		<div class="ui top attached header comment-header tw-flex tw-items-center tw-justify-between">
+			<div class="comment-header-left tw-flex tw-items-center">
 				{{if .OriginalAuthor}}
 					<span class="text black gt-font-semibold gt-mr-2">
 						{{svg (MigrationIcon $.root.Repository.GetOriginalURLHostname)}}
@@ -30,7 +30,7 @@
 					</span>
 				{{end}}
 			</div>
-			<div class="comment-header-right actions tw-flex tw-content-center">
+			<div class="comment-header-right actions tw-flex tw-items-center">
 				{{if .Invalidated}}
 					{{$referenceUrl := printf "%s#%s" $.root.Issue.Link .HashTag}}
 					<a href="{{AppSubUrl}}{{$referenceUrl}}" class="ui label basic small" data-tooltip-content="{{ctx.Locale.Tr "repo.issues.review.outdated_description"}}">
diff --git a/templates/repo/diff/conversation.tmpl b/templates/repo/diff/conversation.tmpl
index e2e87646aa..a453c6bd09 100644
--- a/templates/repo/diff/conversation.tmpl
+++ b/templates/repo/diff/conversation.tmpl
@@ -5,8 +5,8 @@
 {{$referenceUrl := printf "%s#%s" $.Issue.Link (index .comments 0).HashTag}}
 <div class="conversation-holder" data-path="{{(index .comments 0).TreePath}}" data-side="{{if lt (index .comments 0).Line 0}}left{{else}}right{{end}}" data-idx="{{(index .comments 0).UnsignedLine}}">
 	{{if $resolved}}
-		<div class="ui attached header resolved-placeholder tw-flex tw-content-center tw-justify-between">
-			<div class="ui grey text tw-flex tw-content-center tw-flex-wrap gt-gap-2">
+		<div class="ui attached header resolved-placeholder tw-flex tw-items-center tw-justify-between">
+			<div class="ui grey text tw-flex tw-items-center tw-flex-wrap gt-gap-2">
 				{{svg "octicon-check" 16 "icon gt-mr-2"}}
 				<b>{{$resolveDoer.Name}}</b> {{ctx.Locale.Tr "repo.issues.review.resolved_by"}}
 				{{if $invalid}}
@@ -19,12 +19,12 @@
 					</a>
 				{{end}}
 			</div>
-			<div class="tw-flex tw-content-center gt-gap-3">
-				<button id="show-outdated-{{(index .comments 0).ID}}" data-comment="{{(index .comments 0).ID}}" class="ui tiny labeled button show-outdated tw-flex tw-content-center">
+			<div class="tw-flex tw-items-center gt-gap-3">
+				<button id="show-outdated-{{(index .comments 0).ID}}" data-comment="{{(index .comments 0).ID}}" class="ui tiny labeled button show-outdated tw-flex tw-items-center">
 					{{svg "octicon-unfold" 16 "gt-mr-3"}}
 					{{ctx.Locale.Tr "repo.issues.review.show_resolved"}}
 				</button>
-				<button id="hide-outdated-{{(index .comments 0).ID}}" data-comment="{{(index .comments 0).ID}}" class="ui tiny labeled button hide-outdated tw-flex tw-content-center gt-hidden">
+				<button id="hide-outdated-{{(index .comments 0).ID}}" data-comment="{{(index .comments 0).ID}}" class="ui tiny labeled button hide-outdated tw-flex tw-items-center gt-hidden">
 					{{svg "octicon-fold" 16 "gt-mr-3"}}
 					{{ctx.Locale.Tr "repo.issues.review.hide_resolved"}}
 				</button>
@@ -37,7 +37,7 @@
 				{{template "repo/diff/comments" dict "root" $ "comments" .comments}}
 			</ui>
 		</div>
-		<div class="tw-flex tw-justify-end tw-content-center tw-flex-wrap gt-mt-3">
+		<div class="tw-flex tw-justify-end tw-items-center tw-flex-wrap gt-mt-3">
 			<div class="ui buttons gt-mr-2">
 				<button class="ui icon tiny basic button previous-conversation">
 					{{svg "octicon-arrow-up" 12 "icon"}} {{ctx.Locale.Tr "repo.issues.previous"}}
diff --git a/templates/repo/diff/new_review.tmpl b/templates/repo/diff/new_review.tmpl
index 9d1eef712d..9c824db0ad 100644
--- a/templates/repo/diff/new_review.tmpl
+++ b/templates/repo/diff/new_review.tmpl
@@ -10,7 +10,7 @@
 			<form class="ui form form-fetch-action" action="{{.Link}}/reviews/submit" method="post">
 				{{.CsrfTokenHtml}}
 				<input type="hidden" name="commit_id" value="{{.AfterCommitID}}">
-				<div class="field tw-flex tw-content-center">
+				<div class="field tw-flex tw-items-center">
 					<div class="tw-flex-1">{{ctx.Locale.Tr "repo.diff.review.header"}}</div>
 					<a class="muted close">{{svg "octicon-x" 16}}</a>
 				</div>
diff --git a/templates/repo/find/files.tmpl b/templates/repo/find/files.tmpl
index de2c34a158..eebdcb2b1b 100644
--- a/templates/repo/find/files.tmpl
+++ b/templates/repo/find/files.tmpl
@@ -2,7 +2,7 @@
 <div role="main" aria-label="{{.Title}}" class="page-content repository">
 	{{template "repo/header" .}}
 	<div class="ui container">
-		<div class="tw-flex tw-content-center">
+		<div class="tw-flex tw-items-center">
 			<a href="{{$.RepoLink}}">{{.RepoName}}</a>
 			<span class="gt-mx-3">/</span>
 			<div class="ui input tw-flex-1">
diff --git a/templates/repo/forks.tmpl b/templates/repo/forks.tmpl
index 0a4b369cdb..6acb89f367 100644
--- a/templates/repo/forks.tmpl
+++ b/templates/repo/forks.tmpl
@@ -6,7 +6,7 @@
 			{{ctx.Locale.Tr "repo.forks"}}
 		</h2>
 		{{range .Forks}}
-			<div class="tw-flex tw-content-center gt-py-3">
+			<div class="tw-flex tw-items-center gt-py-3">
 				<span class="gt-mr-2">{{ctx.AvatarUtils.Avatar .Owner}}</span>
 				<a href="{{.Owner.HomeLink}}">{{.Owner.Name}}</a> / <a href="{{.Link}}">{{.Name}}</a>
 			</div>
diff --git a/templates/repo/graph/commits.tmpl b/templates/repo/graph/commits.tmpl
index fc7cf925ab..b22527c8ef 100644
--- a/templates/repo/graph/commits.tmpl
+++ b/templates/repo/graph/commits.tmpl
@@ -31,7 +31,7 @@
 					<span class="message tw-inline-block gt-ellipsis gt-mr-3">
 						<span>{{RenderCommitMessage $.Context $commit.Subject ($.Repository.ComposeMetas ctx)}}</span>
 					</span>
-					<span class="commit-refs tw-flex tw-content-center gt-mr-2">
+					<span class="commit-refs tw-flex tw-items-center gt-mr-2">
 						{{range $commit.Refs}}
 							{{$refGroup := .RefGroup}}
 							{{if eq $refGroup "pull"}}
@@ -58,7 +58,7 @@
 							{{end}}
 						{{end}}
 					</span>
-					<span class="author tw-flex tw-content-center gt-mr-3">
+					<span class="author tw-flex tw-items-center gt-mr-3">
 						{{$userName := $commit.Commit.Author.Name}}
 						{{if $commit.User}}
 							{{if $commit.User.FullName}}
@@ -71,7 +71,7 @@
 							{{$userName}}
 						{{end}}
 					</span>
-					<span class="time tw-flex tw-content-center">{{DateTime "full" $commit.Date}}</span>
+					<span class="time tw-flex tw-items-center">{{DateTime "full" $commit.Date}}</span>
 				{{end}}
 			</li>
 		{{end}}
diff --git a/templates/repo/header.tmpl b/templates/repo/header.tmpl
index 128c794ddc..b6f5ed4996 100644
--- a/templates/repo/header.tmpl
+++ b/templates/repo/header.tmpl
@@ -2,7 +2,7 @@
 {{with .Repository}}
 	<div class="ui container">
 		<div class="repo-header">
-			<div class="flex-item tw-content-center">
+			<div class="flex-item tw-items-center">
 				<div class="flex-item-leading">{{template "repo/icon" .}}</div>
 				<div class="flex-item-main">
 					<div class="flex-item-title gt-font-18">
diff --git a/templates/repo/home.tmpl b/templates/repo/home.tmpl
index 05524e79a3..60be13b132 100644
--- a/templates/repo/home.tmpl
+++ b/templates/repo/home.tmpl
@@ -27,7 +27,7 @@
 				</form>
 			</div>
 		</div>
-		<div class="tw-flex tw-content-center tw-flex-wrap gt-gap-2" id="repo-topics">
+		<div class="tw-flex tw-items-center tw-flex-wrap gt-gap-2" id="repo-topics">
 			{{range .Topics}}<a class="ui repo-topic large label topic gt-m-0" href="{{AppSubUrl}}/explore/repos?q={{.Name}}&topic=1">{{.Name}}</a>{{end}}
 			{{if and .Permission.IsAdmin (not .Repository.IsArchived)}}<button id="manage_topic" class="btn interact-fg gt-font-12">{{ctx.Locale.Tr "repo.topic.manage_topics"}}</button>{{end}}
 		</div>
@@ -69,7 +69,7 @@
 		{{end}}
 		{{template "repo/sub_menu" .}}
 		<div class="repo-button-row">
-			<div class="tw-flex tw-content-center tw-flex-wrap gt-gap-y-3">
+			<div class="tw-flex tw-items-center tw-flex-wrap gt-gap-y-3">
 				{{template "repo/branch_dropdown" dict "root" . "ContainerClasses" "gt-mr-2"}}
 				{{if and .CanCompareOrPull .IsViewBranch (not .Repository.IsArchived)}}
 					{{$cmpBranch := ""}}
@@ -129,7 +129,7 @@
 					</span>
 				{{end}}
 			</div>
-			<div class="tw-flex tw-content-center">
+			<div class="tw-flex tw-items-center">
 				<!-- Only show clone panel in repository home page -->
 				{{if eq $n 0}}
 					<div class="clone-panel ui action tiny input">
diff --git a/templates/repo/issue/card.tmpl b/templates/repo/issue/card.tmpl
index 3e1af71b21..3790e42b96 100644
--- a/templates/repo/issue/card.tmpl
+++ b/templates/repo/issue/card.tmpl
@@ -13,7 +13,7 @@
 			</div>
 			<a class="issue-card-title muted issue-title" href="{{.Link}}">{{.Title | RenderEmoji ctx | RenderCodeBlock}}</a>
 			{{if and $.isPinnedIssueCard $.Page.IsRepoAdmin}}
-				<a role="button" class="issue-card-unpin muted tw-flex tw-content-center" data-tooltip-content={{ctx.Locale.Tr "repo.issues.unpin_issue"}} data-issue-id="{{.ID}}" data-unpin-url="{{$.Page.Link}}/unpin/{{.Index}}">
+				<a role="button" class="issue-card-unpin muted tw-flex tw-items-center" data-tooltip-content={{ctx.Locale.Tr "repo.issues.unpin_issue"}} data-issue-id="{{.ID}}" data-unpin-url="{{$.Page.Link}}/unpin/{{.Index}}">
 					{{svg "octicon-x" 16}}
 				</a>
 			{{end}}
diff --git a/templates/repo/issue/filter_list.tmpl b/templates/repo/issue/filter_list.tmpl
index 3cae8d8b19..cdb4b36bfd 100644
--- a/templates/repo/issue/filter_list.tmpl
+++ b/templates/repo/issue/filter_list.tmpl
@@ -32,7 +32,7 @@
 				<div class="divider"></div>
 			{{end}}
 			{{$previousExclusiveScope = $exclusiveScope}}
-			<a rel="nofollow" class="item label-filter-item tw-flex tw-content-center" {{if .IsArchived}}data-is-archived{{end}} href="?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{.QueryString}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}" data-label-id="{{.ID}}">
+			<a rel="nofollow" class="item label-filter-item tw-flex tw-items-center" {{if .IsArchived}}data-is-archived{{end}} href="?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{.QueryString}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}" data-label-id="{{.ID}}">
 				{{if .IsExcluded}}
 					{{svg "octicon-circle-slash"}}
 				{{else if .IsSelected}}
diff --git a/templates/repo/issue/milestone_issues.tmpl b/templates/repo/issue/milestone_issues.tmpl
index e029bf6031..2028375c03 100644
--- a/templates/repo/issue/milestone_issues.tmpl
+++ b/templates/repo/issue/milestone_issues.tmpl
@@ -29,7 +29,7 @@
 		<div class="tw-flex tw-flex-col gt-gap-3">
 			<progress class="milestone-progress-big" value="{{.Milestone.Completeness}}" max="100"></progress>
 			<div class="tw-flex gt-gap-4">
-				<div classs="tw-flex tw-content-center">
+				<div classs="tw-flex tw-items-center">
 					{{$closedDate:= TimeSinceUnix .Milestone.ClosedDateUnix ctx.Locale}}
 					{{if .IsClosed}}
 						{{svg "octicon-clock"}} {{ctx.Locale.Tr "repo.milestones.closed" $closedDate}}
diff --git a/templates/repo/issue/milestones.tmpl b/templates/repo/issue/milestones.tmpl
index af7dd70193..57b697d8fd 100644
--- a/templates/repo/issue/milestones.tmpl
+++ b/templates/repo/issue/milestones.tmpl
@@ -23,7 +23,7 @@
 							{{svg "octicon-milestone" 16}}
 							<a class="muted" href="{{$.RepoLink}}/milestone/{{.ID}}">{{.Name}}</a>
 						</h3>
-						<div class="tw-flex tw-content-center">
+						<div class="tw-flex tw-items-center">
 							<span class="gt-mr-3">{{.Completeness}}%</span>
 							<progress value="{{.Completeness}}" max="100"></progress>
 						</div>
diff --git a/templates/repo/issue/view_content.tmpl b/templates/repo/issue/view_content.tmpl
index e7d25ed8a3..d885b47908 100644
--- a/templates/repo/issue/view_content.tmpl
+++ b/templates/repo/issue/view_content.tmpl
@@ -20,8 +20,8 @@
 				</a>
 				{{end}}
 				<div class="content comment-container">
-					<div class="ui top attached header comment-header tw-flex tw-content-center tw-justify-between" role="heading" aria-level="3">
-						<div class="comment-header-left tw-flex tw-content-center">
+					<div class="ui top attached header comment-header tw-flex tw-items-center tw-justify-between" role="heading" aria-level="3">
+						<div class="comment-header-left tw-flex tw-items-center">
 							{{if .Issue.OriginalAuthor}}
 								<span class="text black gt-font-semibold">
 									{{svg (MigrationIcon .Repository.GetOriginalURLHostname)}}
@@ -43,7 +43,7 @@
 								</span>
 							{{end}}
 						</div>
-						<div class="comment-header-right actions tw-flex tw-content-center">
+						<div class="comment-header-right actions tw-flex tw-items-center">
 							{{template "repo/issue/view_content/show_role" dict "ShowRole" .Issue.ShowRole "IgnorePoster" true}}
 							{{if not $.Repository.IsArchived}}
 								{{template "repo/issue/view_content/add_reaction" dict "ctxData" $ "ActionURL" (printf "%s/issues/%d/reactions" $.RepoLink .Issue.Index)}}
diff --git a/templates/repo/issue/view_content/attachments.tmpl b/templates/repo/issue/view_content/attachments.tmpl
index 151131366f..0635a201be 100644
--- a/templates/repo/issue/view_content/attachments.tmpl
+++ b/templates/repo/issue/view_content/attachments.tmpl
@@ -18,7 +18,7 @@
 					<span><strong>{{.Name}}</strong></span>
 				</a>
 			</div>
-			<div class="gt-p-3 tw-flex tw-content-center">
+			<div class="gt-p-3 tw-flex tw-items-center">
 				<span class="ui text grey">{{.Size | FileSize}}</span>
 			</div>
 		</div>
diff --git a/templates/repo/issue/view_content/comments.tmpl b/templates/repo/issue/view_content/comments.tmpl
index b593eb8b4a..a6d0b801f0 100644
--- a/templates/repo/issue/view_content/comments.tmpl
+++ b/templates/repo/issue/view_content/comments.tmpl
@@ -25,8 +25,8 @@
 				</a>
 			{{end}}
 				<div class="content comment-container">
-					<div class="ui top attached header comment-header tw-flex tw-content-center tw-justify-between" role="heading" aria-level="3">
-						<div class="comment-header-left tw-flex tw-content-center">
+					<div class="ui top attached header comment-header tw-flex tw-items-center tw-justify-between" role="heading" aria-level="3">
+						<div class="comment-header-left tw-flex tw-items-center">
 							{{if .OriginalAuthor}}
 								<span class="text black gt-font-semibold gt-mr-2">
 									{{svg (MigrationIcon $.Repository.GetOriginalURLHostname)}}
@@ -50,7 +50,7 @@
 								</span>
 							{{end}}
 						</div>
-						<div class="comment-header-right actions tw-flex tw-content-center">
+						<div class="comment-header-right actions tw-flex tw-items-center">
 							{{template "repo/issue/view_content/show_role" dict "ShowRole" .ShowRole}}
 							{{if not $.Repository.IsArchived}}
 								{{template "repo/issue/view_content/add_reaction" dict "ctxData" $ "ActionURL" (printf "%s/comments/%d/reactions" $.RepoLink .ID)}}
@@ -403,8 +403,8 @@
 				{{if or .Content .Attachments}}
 				<div class="timeline-item comment">
 					<div class="content comment-container">
-						<div class="ui top attached header comment-header tw-flex tw-content-center tw-justify-between">
-							<div class="comment-header-left tw-flex tw-content-center">
+						<div class="ui top attached header comment-header tw-flex tw-items-center tw-justify-between">
+							<div class="comment-header-left tw-flex tw-items-center">
 								{{if gt .Poster.ID 0}}
 									<a class="inline-timeline-avatar" href="{{.Poster.HomeLink}}">
 										{{ctx.AvatarUtils.Avatar .Poster 24}}
@@ -425,7 +425,7 @@
 									{{ctx.Locale.Tr "repo.issues.review.left_comment"}}
 								</span>
 							</div>
-							<div class="comment-header-right actions tw-flex tw-content-center">
+							<div class="comment-header-right actions tw-flex tw-items-center">
 								{{template "repo/issue/view_content/show_role" dict "ShowRole" .ShowRole}}
 								{{if not $.Repository.IsArchived}}
 									{{template "repo/issue/view_content/add_reaction" dict "ctxData" $ "ActionURL" (printf "%s/comments/%d/reactions" $.RepoLink .ID)}}
@@ -622,7 +622,7 @@
 				{{if .Content}}
 					<div class="timeline-item comment">
 						<div class="content">
-							<div class="ui top attached header comment-header-left tw-flex tw-content-center arrow-top">
+							<div class="ui top attached header comment-header-left tw-flex tw-items-center arrow-top">
 								{{if gt .Poster.ID 0}}
 									<a class="inline-timeline-avatar" href="{{.Poster.HomeLink}}">
 										{{ctx.AvatarUtils.Avatar .Poster 24}}
diff --git a/templates/repo/issue/view_content/conversation.tmpl b/templates/repo/issue/view_content/conversation.tmpl
index b302ee8c2d..f94aa290fd 100644
--- a/templates/repo/issue/view_content/conversation.tmpl
+++ b/templates/repo/issue/view_content/conversation.tmpl
@@ -3,8 +3,8 @@
 {{$resolveDoer := (index .comments 0).ResolveDoer}}
 {{$isNotPending := (not (eq (index .comments 0).Review.Type 0))}}
 <div class="ui segments conversation-holder">
-	<div class="ui segment collapsible-comment-box gt-py-3 tw-flex tw-content-center tw-justify-between">
-		<div class="tw-flex tw-content-center">
+	<div class="ui segment collapsible-comment-box gt-py-3 tw-flex tw-items-center tw-justify-between">
+		<div class="tw-flex tw-items-center">
 			<a href="{{(index .comments 0).CodeCommentLink ctx}}" class="file-comment gt-ml-3 gt-word-break">{{(index .comments 0).TreePath}}</a>
 			{{if $invalid}}
 				<span class="ui label basic small gt-ml-3" data-tooltip-content="{{ctx.Locale.Tr "repo.issues.review.outdated_description"}}">
@@ -14,7 +14,7 @@
 		</div>
 		<div>
 			{{if or $invalid $resolved}}
-				<button id="show-outdated-{{(index .comments 0).ID}}" data-comment="{{(index .comments 0).ID}}" class="{{if not $resolved}}gt-hidden {{end}}ui compact labeled button show-outdated tw-flex tw-content-center">
+				<button id="show-outdated-{{(index .comments 0).ID}}" data-comment="{{(index .comments 0).ID}}" class="{{if not $resolved}}gt-hidden {{end}}ui compact labeled button show-outdated tw-flex tw-items-center">
 					{{svg "octicon-unfold" 16 "gt-mr-3"}}
 					{{if $resolved}}
 						{{ctx.Locale.Tr "repo.issues.review.show_resolved"}}
@@ -22,7 +22,7 @@
 						{{ctx.Locale.Tr "repo.issues.review.show_outdated"}}
 					{{end}}
 				</button>
-				<button id="hide-outdated-{{(index .comments 0).ID}}" data-comment="{{(index .comments 0).ID}}" class="{{if $resolved}}gt-hidden {{end}}ui compact labeled button hide-outdated tw-flex tw-content-center">
+				<button id="hide-outdated-{{(index .comments 0).ID}}" data-comment="{{(index .comments 0).ID}}" class="{{if $resolved}}gt-hidden {{end}}ui compact labeled button hide-outdated tw-flex tw-items-center">
 					{{svg "octicon-fold" 16 "gt-mr-3"}}
 					{{if $resolved}}
 						{{ctx.Locale.Tr "repo.issues.review.hide_resolved"}}
@@ -55,7 +55,7 @@
 				<div class="comment code-comment gt-pb-4" id="{{.HashTag}}">
 					<div class="content">
 						<div class="header comment-header">
-							<div class="comment-header-left tw-flex tw-content-center">
+							<div class="comment-header-left tw-flex tw-items-center">
 								{{if not .OriginalAuthor}}
 									<a class="avatar">
 										{{ctx.AvatarUtils.Avatar .Poster 20}}
@@ -76,7 +76,7 @@
 									{{ctx.Locale.Tr "repo.issues.commented_at" .HashTag $createdSubStr}}
 								</span>
 							</div>
-							<div class="comment-header-right actions tw-flex tw-content-center">
+							<div class="comment-header-right actions tw-flex tw-items-center">
 								{{template "repo/issue/view_content/show_role" dict "ShowRole" .ShowRole}}
 								{{if not $.Repository.IsArchived}}
 									{{template "repo/issue/view_content/add_reaction" dict "ctxData" $ "ActionURL" (printf "%s/comments/%d/reactions" $.RepoLink .ID)}}
@@ -106,7 +106,7 @@
 				</div>
 			{{end}}
 		</div>
-		<div class="code-comment-buttons tw-flex tw-content-center tw-flex-wrap gt-mt-3 gt-mb-2 gt-mx-3">
+		<div class="code-comment-buttons tw-flex tw-items-center tw-flex-wrap gt-mt-3 gt-mb-2 gt-mx-3">
 			<div class="tw-flex-1">
 				{{if $resolved}}
 					<div class="ui grey text">
diff --git a/templates/repo/issue/view_content/sidebar.tmpl b/templates/repo/issue/view_content/sidebar.tmpl
index 3e3ca41922..561fe8c0ad 100644
--- a/templates/repo/issue/view_content/sidebar.tmpl
+++ b/templates/repo/issue/view_content/sidebar.tmpl
@@ -3,7 +3,7 @@
 	{{if .Issue.IsPull}}
 		<input id="reviewer_id" name="reviewer_id" type="hidden" value="{{.reviewer_id}}">
 		<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 tw-flex tw-content-center muted">
+			<a class="text tw-flex tw-items-center muted">
 				<strong>{{ctx.Locale.Tr "repo.issues.review.reviewers"}}</strong>
 				{{if and .CanChooseReviewer (not .Repository.IsArchived)}}
 					{{svg "octicon-gear" 16 "gt-ml-2"}}
@@ -50,17 +50,17 @@
 			<span class="no-select item {{if or .OriginalReviews .PullReviewers}}gt-hidden{{end}}">{{ctx.Locale.Tr "repo.issues.new.no_reviewers"}}</span>
 			<div class="selected">
 				{{range .PullReviewers}}
-					<div class="item tw-flex tw-content-center gt-py-3">
-						<div class="tw-flex tw-content-center tw-flex-1">
+					<div class="item tw-flex tw-items-center gt-py-3">
+						<div class="tw-flex tw-items-center tw-flex-1">
 							{{if .User}}
 								<a class="muted sidebar-item-link" href="{{.User.HomeLink}}">{{ctx.AvatarUtils.Avatar .User 20 "gt-mr-3"}}{{.User.GetDisplayName}}</a>
 							{{else if .Team}}
 								<span class="text">{{svg "octicon-people" 20 "gt-mr-3"}}{{$.Issue.Repo.OwnerName}}/{{.Team.Name}}</span>
 							{{end}}
 						</div>
-						<div class="tw-flex tw-content-center gt-gap-3">
+						<div class="tw-flex tw-items-center gt-gap-3">
 							{{if (and $.Permission.IsAdmin (or (eq .Review.Type 1) (eq .Review.Type 3)) (not $.Issue.IsClosed))}}
-								<a href="#" class="ui muted icon tw-flex tw-content-center show-modal" data-tooltip-content="{{ctx.Locale.Tr "repo.issues.dismiss_review"}}" data-modal="#dismiss-review-modal-{{.Review.ID}}">
+								<a href="#" class="ui muted icon tw-flex tw-items-center show-modal" data-tooltip-content="{{ctx.Locale.Tr "repo.issues.dismiss_review"}}" data-modal="#dismiss-review-modal-{{.Review.ID}}">
 									{{svg "octicon-x" 20}}
 								</a>
 								<div class="ui small modal" id="dismiss-review-modal-{{.Review.ID}}">
@@ -99,14 +99,14 @@
 					</div>
 				{{end}}
 				{{range .OriginalReviews}}
-					<div class="item tw-flex tw-content-center gt-py-3">
-						<div class="tw-flex tw-content-center tw-flex-1">
+					<div class="item tw-flex tw-items-center gt-py-3">
+						<div class="tw-flex tw-items-center tw-flex-1">
 							<a class="muted" href="{{$.Repository.OriginalURL}}" data-tooltip-content="{{ctx.Locale.Tr "repo.migrated_from_fake" $.Repository.GetOriginalURLHostname}}">
 								{{svg (MigrationIcon $.Repository.GetOriginalURLHostname) 20 "gt-mr-3"}}
 								{{.OriginalAuthor}}
 							</a>
 						</div>
-						<div class="tw-flex tw-content-center gt-gap-3">
+						<div class="tw-flex tw-items-center gt-gap-3">
 							{{svg (printf "octicon-%s" .Type.Icon) 16 (printf "text %s" (.HTMLTypeColorName))}}
 						</div>
 					</div>
@@ -368,7 +368,7 @@
 		</div>
 		{{if ne .Issue.DeadlineUnix 0}}
 			<p>
-				<div class="tw-flex tw-justify-between tw-content-center">
+				<div class="tw-flex tw-justify-between tw-items-center">
 					<div class="due-date {{if .Issue.IsOverdue}}text red{{end}}" {{if .Issue.IsOverdue}}data-tooltip-content="{{ctx.Locale.Tr "repo.issues.due_date_overdue"}}"{{end}}>
 						{{svg "octicon-calendar" 16 "gt-mr-3"}}
 						{{DateTime "long" .Issue.DeadlineUnix.FormatDate}}
@@ -424,7 +424,7 @@
 				</span>
 				<div class="ui relaxed divided list">
 					{{range .BlockingDependencies}}
-						<div class="item dependency{{if .Issue.IsClosed}} is-closed{{end}} tw-flex tw-content-center tw-justify-between">
+						<div class="item dependency{{if .Issue.IsClosed}} is-closed{{end}} tw-flex tw-items-center tw-justify-between">
 							<div class="item-left tw-flex tw-justify-center tw-flex-col tw-flex-1 gt-ellipsis">
 								<a class="title muted" href="{{.Issue.Link}}" data-tooltip-content="#{{.Issue.Index}} {{.Issue.Title | RenderEmoji $.Context}}">
 									#{{.Issue.Index}} {{.Issue.Title | RenderEmoji $.Context}}
@@ -433,7 +433,7 @@
 									{{.Repository.OwnerName}}/{{.Repository.Name}}
 								</div>
 							</div>
-							<div class="item-right tw-flex tw-content-center gt-m-2">
+							<div class="item-right tw-flex tw-items-center gt-m-2">
 								{{if and $.CanCreateIssueDependencies (not $.Repository.IsArchived)}}
 									<a class="delete-dependency-button ci muted" data-id="{{.Issue.ID}}" data-type="blocking" data-tooltip-content="{{ctx.Locale.Tr "repo.issues.dependency.remove_info"}}">
 										{{svg "octicon-trash" 16}}
@@ -443,7 +443,7 @@
 						</div>
 					{{end}}
 					{{if .BlockingDependenciesNotPermitted}}
-						<div class="item tw-flex tw-content-center tw-justify-between gt-ellipsis">
+						<div class="item tw-flex tw-items-center tw-justify-between gt-ellipsis">
 							<span>{{ctx.Locale.TrN (len .BlockingDependenciesNotPermitted) "repo.issues.dependency.no_permission_1" "repo.issues.dependency.no_permission_n" (len .BlockingDependenciesNotPermitted)}}</span>
 						</div>
 					{{end}}
@@ -456,7 +456,7 @@
 				</span>
 				<div class="ui relaxed divided list">
 					{{range .BlockedByDependencies}}
-						<div class="item dependency{{if .Issue.IsClosed}} is-closed{{end}} tw-flex tw-content-center tw-justify-between">
+						<div class="item dependency{{if .Issue.IsClosed}} is-closed{{end}} tw-flex tw-items-center tw-justify-between">
 							<div class="item-left tw-flex tw-justify-center tw-flex-col tw-flex-1 gt-ellipsis">
 								<a class="title muted" href="{{.Issue.Link}}" data-tooltip-content="#{{.Issue.Index}} {{.Issue.Title | RenderEmoji $.Context}}">
 									#{{.Issue.Index}} {{.Issue.Title | RenderEmoji $.Context}}
@@ -465,7 +465,7 @@
 									{{.Repository.OwnerName}}/{{.Repository.Name}}
 								</div>
 							</div>
-							<div class="item-right tw-flex tw-content-center gt-m-2">
+							<div class="item-right tw-flex tw-items-center gt-m-2">
 								{{if and $.CanCreateIssueDependencies (not $.Repository.IsArchived)}}
 									<a class="delete-dependency-button ci muted" data-id="{{.Issue.ID}}" data-type="blockedBy" data-tooltip-content="{{ctx.Locale.Tr "repo.issues.dependency.remove_info"}}">
 										{{svg "octicon-trash" 16}}
@@ -476,7 +476,7 @@
 					{{end}}
 					{{if $.CanCreateIssueDependencies}}
 						{{range .BlockedByDependenciesNotPermitted}}
-							<div class="item dependency{{if .Issue.IsClosed}} is-closed{{end}} tw-flex tw-content-center tw-justify-between">
+							<div class="item dependency{{if .Issue.IsClosed}} is-closed{{end}} tw-flex tw-items-center tw-justify-between">
 								<div class="item-left tw-flex tw-justify-center tw-flex-col tw-flex-1 gt-ellipsis">
 									<div class="gt-ellipsis">
 										<span data-tooltip-content="{{ctx.Locale.Tr "repo.issues.dependency.no_permission.can_remove"}}">{{svg "octicon-lock" 16}}</span>
@@ -488,7 +488,7 @@
 										{{.Repository.OwnerName}}/{{.Repository.Name}}
 									</div>
 								</div>
-								<div class="item-right tw-flex tw-content-center gt-m-2">
+								<div class="item-right tw-flex tw-items-center gt-m-2">
 									{{if and $.CanCreateIssueDependencies (not $.Repository.IsArchived)}}
 										<a class="delete-dependency-button ci muted" data-id="{{.Issue.ID}}" data-type="blocking" data-tooltip-content="{{ctx.Locale.Tr "repo.issues.dependency.remove_info"}}">
 											{{svg "octicon-trash" 16}}
@@ -498,7 +498,7 @@
 							</div>
 						{{end}}
 					{{else if .BlockedByDependenciesNotPermitted}}
-						<div class="item tw-flex tw-content-center tw-justify-between gt-ellipsis">
+						<div class="item tw-flex tw-items-center tw-justify-between gt-ellipsis">
 							<span>{{ctx.Locale.TrN (len .BlockedByDependenciesNotPermitted) "repo.issues.dependency.no_permission_1" "repo.issues.dependency.no_permission_n" (len .BlockedByDependenciesNotPermitted)}}</span>
 						</div>
 					{{end}}
@@ -555,7 +555,7 @@
 	<div class="divider"></div>
 	<div class="ui equal width compact grid">
 		{{$issueReferenceLink := printf "%s#%d" .Issue.Repo.FullName .Issue.Index}}
-		<div class="row tw-content-center" data-tooltip-content="{{$issueReferenceLink}}">
+		<div class="row tw-items-center" data-tooltip-content="{{$issueReferenceLink}}">
 			<span class="text column truncate">{{ctx.Locale.Tr "repo.issues.reference_link" $issueReferenceLink}}</span>
 			<button class="ui two wide button column gt-p-3" data-clipboard-text="{{$issueReferenceLink}}">{{svg "octicon-copy" 14}}</button>
 		</div>
diff --git a/templates/repo/migrate/migrate.tmpl b/templates/repo/migrate/migrate.tmpl
index c0336b9b97..32465bc394 100644
--- a/templates/repo/migrate/migrate.tmpl
+++ b/templates/repo/migrate/migrate.tmpl
@@ -5,7 +5,7 @@
 			{{template "repo/migrate/helper" .}}
 			<div class="ui cards migrate-entries">
 				{{range .Services}}
-					<a class="ui card migrate-entry tw-flex tw-content-center" href="{{AppSubUrl}}/repo/migrate?service_type={{.}}&org={{$.Org}}&mirror={{$.Mirror}}">
+					<a class="ui card migrate-entry tw-flex tw-items-center" href="{{AppSubUrl}}/repo/migrate?service_type={{.}}&org={{$.Org}}&mirror={{$.Mirror}}">
 						{{if eq .Name "github"}}
 							{{svg "octicon-mark-github" 184 "gt-p-4"}}
 						{{else if eq .Name "gitlab"}}
diff --git a/templates/repo/projects/view.tmpl b/templates/repo/projects/view.tmpl
index b227ce4439..eea1057a50 100644
--- a/templates/repo/projects/view.tmpl
+++ b/templates/repo/projects/view.tmpl
@@ -2,7 +2,7 @@
 <div role="main" aria-label="{{.Title}}" class="page-content repository projects view-project">
 	{{template "repo/header" .}}
 	<div class="ui container padded">
-		<div class="tw-flex tw-justify-between tw-content-center gt-mb-4">
+		<div class="tw-flex tw-justify-between tw-items-center gt-mb-4">
 			{{template "repo/issue/navbar" .}}
 			<a class="ui small primary button" href="{{.RepoLink}}/issues/new/choose?project={{.Project.ID}}">{{ctx.Locale.Tr "repo.issues.new"}}</a>
 		</div>
diff --git a/templates/repo/pulls/tab_menu.tmpl b/templates/repo/pulls/tab_menu.tmpl
index fb00acde32..2a653c7c69 100644
--- a/templates/repo/pulls/tab_menu.tmpl
+++ b/templates/repo/pulls/tab_menu.tmpl
@@ -15,7 +15,7 @@
 			{{ctx.Locale.Tr "repo.pulls.tab_files"}}
 			<span class="ui small label">{{if .NumFiles}}{{.NumFiles}}{{else}}-{{end}}</span>
 		</a>
-		<span class="item tw-ml-auto gt-pr-0 gt-font-bold tw-flex tw-content-center gt-gap-3">
+		<span class="item tw-ml-auto gt-pr-0 gt-font-bold tw-flex tw-items-center gt-gap-3">
 			<span><span class="text green">{{if .Diff.TotalAddition}}+{{.Diff.TotalAddition}}{{end}}</span> <span class="text red">{{if .Diff.TotalDeletion}}-{{.Diff.TotalDeletion}}{{end}}</span></span>
 			<span class="diff-stats-bar">
 				<div class="diff-stats-add-bar" style="width: {{Eval 100 "*" .Diff.TotalAddition "/" "(" .Diff.TotalAddition "+" .Diff.TotalDeletion "+" 0.0 ")"}}%"></div>
diff --git a/templates/repo/release/list.tmpl b/templates/repo/release/list.tmpl
index 477b2ea8d7..8c64380d45 100644
--- a/templates/repo/release/list.tmpl
+++ b/templates/repo/release/list.tmpl
@@ -16,7 +16,7 @@
 						{{end}}
 					</div>
 					<div class="ui twelve wide column detail">
-						<div class="tw-flex tw-content-center tw-justify-between tw-flex-wrap gt-mb-3">
+						<div class="tw-flex tw-items-center tw-justify-between tw-flex-wrap gt-mb-3">
 							<h4 class="release-list-title gt-word-break">
 								<a href="{{$.RepoLink}}/releases/tag/{{$release.TagName | PathEscapeSegments}}">{{$release.Title}}</a>
 								{{template "repo/commit_statuses" dict "Status" $info.CommitStatus "Statuses" $info.CommitStatuses "AdditionalClasses" "tw-flex"}}
diff --git a/templates/repo/release_tag_header.tmpl b/templates/repo/release_tag_header.tmpl
index 1df1b5ba50..7a00cbe49c 100644
--- a/templates/repo/release_tag_header.tmpl
+++ b/templates/repo/release_tag_header.tmpl
@@ -3,7 +3,7 @@
 
 {{if $canReadReleases}}
 	<div class="tw-flex">
-		<div class="tw-flex-1 tw-flex tw-content-center">
+		<div class="tw-flex-1 tw-flex tw-items-center">
 			<h2 class="ui compact small menu header small-menu-items">
 				<a class="{{if and .PageIsReleaseList (not .PageIsSingleTag)}}active {{end}}item" href="{{.RepoLink}}/releases">{{ctx.Locale.PrettyNumber .NumReleases}} {{ctx.Locale.TrN .NumReleases "repo.release" "repo.releases"}}</a>
 				{{if $canReadCode}}
diff --git a/templates/repo/settings/branches.tmpl b/templates/repo/settings/branches.tmpl
index 2610ae02fe..ee6bdfbf2f 100644
--- a/templates/repo/settings/branches.tmpl
+++ b/templates/repo/settings/branches.tmpl
@@ -41,7 +41,7 @@
 			<div class="ui attached segment">
 				<div class="flex-list">
 					{{range .ProtectedBranches}}
-						<div class="flex-item tw-content-center">
+						<div class="flex-item tw-items-center">
 							<div class="flex-item-main">
 								<div class="flex-item-title">
 									<div class="ui basic primary label">{{.RuleName}}</div>
diff --git a/templates/repo/settings/collaboration.tmpl b/templates/repo/settings/collaboration.tmpl
index 8783de2544..2a4ec577e7 100644
--- a/templates/repo/settings/collaboration.tmpl
+++ b/templates/repo/settings/collaboration.tmpl
@@ -7,7 +7,7 @@
 		<div class="ui attached segment">
 			<div class="flex-list">
 				{{range .Collaborators}}
-					<div class="flex-item tw-content-center">
+					<div class="flex-item tw-items-center">
 						<div class="flex-item-leading">
 							<a href="{{.HomeLink}}">{{ctx.AvatarUtils.Avatar . 32}}</a>
 						</div>
diff --git a/templates/repo/settings/options.tmpl b/templates/repo/settings/options.tmpl
index 4f505bdf02..a28d430f85 100644
--- a/templates/repo/settings/options.tmpl
+++ b/templates/repo/settings/options.tmpl
@@ -492,7 +492,7 @@
 					</div>
 				</div>
 				{{if not .Repository.IsMirror}}
-					<div class="flex-item tw-content-center">
+					<div class="flex-item tw-items-center">
 						<div class="flex-item-main">
 							{{if .Repository.IsArchived}}
 								<div class="flex-item-title">{{ctx.Locale.Tr "repo.settings.unarchive.header"}}</div>
diff --git a/templates/repo/tag/list.tmpl b/templates/repo/tag/list.tmpl
index 0348334623..06c02c5f75 100644
--- a/templates/repo/tag/list.tmpl
+++ b/templates/repo/tag/list.tmpl
@@ -5,7 +5,7 @@
 		{{template "base/alert" .}}
 		{{template "repo/release_tag_header" .}}
 		<h4 class="ui top attached header">
-			<div class="five wide column tw-flex tw-content-center">
+			<div class="five wide column tw-flex tw-items-center">
 				{{svg "octicon-tag" 16 "gt-mr-2"}}{{ctx.Locale.Tr "repo.release.tags"}}
 			</div>
 		</h4>
@@ -18,12 +18,12 @@
 							<td class="tag">
 								<h3 class="release-tag-name gt-mb-3">
 									{{if $canReadReleases}}
-										<a class="tw-flex tw-content-center" href="{{$.RepoLink}}/releases/tag/{{.TagName | PathEscapeSegments}}" rel="nofollow">{{.TagName}}</a>
+										<a class="tw-flex tw-items-center" href="{{$.RepoLink}}/releases/tag/{{.TagName | PathEscapeSegments}}" rel="nofollow">{{.TagName}}</a>
 									{{else}}
-										<a class="tw-flex tw-content-center" href="{{$.RepoLink}}/src/tag/{{.TagName | PathEscapeSegments}}" rel="nofollow">{{.TagName}}</a>
+										<a class="tw-flex tw-items-center" href="{{$.RepoLink}}/src/tag/{{.TagName | PathEscapeSegments}}" rel="nofollow">{{.TagName}}</a>
 									{{end}}
 								</h3>
-								<div class="download tw-flex tw-content-center">
+								<div class="download tw-flex tw-items-center">
 									{{if $.Permission.CanRead $.UnitTypeCode}}
 										{{if .CreatedUnix}}
 											<span class="gt-mr-3">{{svg "octicon-clock" 16 "gt-mr-2"}}{{TimeSinceUnix .CreatedUnix ctx.Locale}}</span>
diff --git a/templates/repo/view_file.tmpl b/templates/repo/view_file.tmpl
index c99b193ef0..cbc3bdf675 100644
--- a/templates/repo/view_file.tmpl
+++ b/templates/repo/view_file.tmpl
@@ -25,8 +25,8 @@
 		</div>
 	{{end}}
 
-	<h4 class="file-header ui top attached header tw-flex tw-content-center tw-justify-between tw-flex-wrap">
-		<div class="file-header-left tw-flex tw-content-center gt-py-3 gt-pr-4">
+	<h4 class="file-header ui top attached header tw-flex tw-items-center tw-justify-between tw-flex-wrap">
+		<div class="file-header-left tw-flex tw-items-center gt-py-3 gt-pr-4">
 			{{if .ReadmeInList}}
 				{{svg "octicon-book" 16 "gt-mr-3"}}
 				<strong><a class="default-link muted" href="#readme">{{.FileName}}</a></strong>
@@ -34,7 +34,7 @@
 				{{template "repo/file_info" .}}
 			{{end}}
 		</div>
-		<div class="file-header-right file-actions tw-flex tw-content-center tw-flex-wrap">
+		<div class="file-header-right file-actions tw-flex tw-items-center tw-flex-wrap">
 			{{if .HasSourceRenderedToggle}}
 				<div class="ui compact icon buttons">
 					<a href="?display=source" class="ui mini basic button {{if .IsDisplayingSource}}active{{end}}" data-tooltip-content="{{ctx.Locale.Tr "repo.file_view_source"}}">{{svg "octicon-code" 15}}</a>
diff --git a/templates/repo/wiki/pages.tmpl b/templates/repo/wiki/pages.tmpl
index 646c6a3d36..42c36a9f46 100644
--- a/templates/repo/wiki/pages.tmpl
+++ b/templates/repo/wiki/pages.tmpl
@@ -2,7 +2,7 @@
 <div role="main" aria-label="{{.Title}}" class="page-content repository wiki pages">
 	{{template "repo/header" .}}
 	<div class="ui container">
-		<h2 class="ui header tw-flex tw-content-center tw-justify-between">
+		<h2 class="ui header tw-flex tw-items-center tw-justify-between">
 			<span>{{ctx.Locale.Tr "repo.wiki.pages"}}</span>
 			<span>
 				{{if and .CanWriteWiki (not .Repository.IsMirror)}}
diff --git a/templates/repo/wiki/view.tmpl b/templates/repo/wiki/view.tmpl
index 7519a63461..ebd7f25130 100644
--- a/templates/repo/wiki/view.tmpl
+++ b/templates/repo/wiki/view.tmpl
@@ -4,7 +4,7 @@
 	{{$title := .title}}
 	<div class="ui container">
 		<div class="repo-button-row">
-			<div class="tw-flex tw-content-center">
+			<div class="tw-flex tw-items-center">
 				<div class="ui floating filter dropdown" data-no-results="{{ctx.Locale.Tr "repo.pulls.no_results"}}">
 					<div class="ui basic small button">
 						<span class="text">
diff --git a/templates/shared/repo_search.tmpl b/templates/shared/repo_search.tmpl
index 25381b9ef8..d2fe2de143 100644
--- a/templates/shared/repo_search.tmpl
+++ b/templates/shared/repo_search.tmpl
@@ -1,5 +1,5 @@
 <div class="ui small secondary filter menu">
-	<form id="repo-search-form" class="ui form ignore-dirty tw-flex-1 tw-flex tw-flex-row tw-gap-x-2 tw-content-center">
+	<form id="repo-search-form" class="ui form ignore-dirty tw-flex-1 tw-flex tw-flex-row tw-gap-x-2 tw-items-center">
 		{{if .Language}}<input hidden name="language" value="{{.Language}}">{{end}}
 		{{if .TopicOnly}}<input hidden name="topic" value="{{.TopicOnly}}">{{end}}
 		<div class="ui small fluid action input tw-flex-1">
diff --git a/templates/shared/searchbottom.tmpl b/templates/shared/searchbottom.tmpl
index b920e10bb2..43d6092e8d 100644
--- a/templates/shared/searchbottom.tmpl
+++ b/templates/shared/searchbottom.tmpl
@@ -1,5 +1,5 @@
-<div class="ui bottom attached table segment tw-flex tw-content-center tw-justify-between">
-		<div class="tw-flex tw-content-center gt-ml-4">
+<div class="ui bottom attached table segment tw-flex tw-items-center tw-justify-between">
+		<div class="tw-flex tw-items-center gt-ml-4">
 			{{if .result.Language}}
 					<i class="color-icon gt-mr-3" style="background-color: {{.result.Color}}"></i>{{.result.Language}}
 			{{end}}
diff --git a/templates/shared/secrets/add_list.tmpl b/templates/shared/secrets/add_list.tmpl
index ea36d0cec2..c943a1944d 100644
--- a/templates/shared/secrets/add_list.tmpl
+++ b/templates/shared/secrets/add_list.tmpl
@@ -14,7 +14,7 @@
 	{{if .Secrets}}
 	<div class="flex-list">
 		{{range .Secrets}}
-		<div class="flex-item tw-content-center">
+		<div class="flex-item tw-items-center">
 			<div class="flex-item-leading">
 				{{svg "octicon-key" 32}}
 			</div>
diff --git a/templates/shared/user/org_profile_avatar.tmpl b/templates/shared/user/org_profile_avatar.tmpl
index 07e7b8aed5..2ff1e40ca8 100644
--- a/templates/shared/user/org_profile_avatar.tmpl
+++ b/templates/shared/user/org_profile_avatar.tmpl
@@ -2,7 +2,7 @@
 	<div class="ui container">
 		<div class="ui vertically grid head">
 			<div class="column">
-				<div class="ui header tw-flex tw-content-center gt-word-break">
+				<div class="ui header tw-flex tw-items-center gt-word-break">
 					{{ctx.AvatarUtils.Avatar . 100}}
 					<span class="text thin grey"><a href="{{.HomeLink}}">{{.DisplayName}}</a></span>
 					<span class="org-visibility">
diff --git a/templates/shared/variables/variable_list.tmpl b/templates/shared/variables/variable_list.tmpl
index dc8c7d7a80..fc2ac98e29 100644
--- a/templates/shared/variables/variable_list.tmpl
+++ b/templates/shared/variables/variable_list.tmpl
@@ -16,7 +16,7 @@
 	{{if .Variables}}
 	<div class="flex-list">
 		{{range .Variables}}
-		<div class="flex-item tw-content-center">
+		<div class="flex-item tw-items-center">
 			<div class="flex-item-leading">
 				{{svg "octicon-pencil" 32}}
 			</div>
diff --git a/templates/user/auth/signin_inner.tmpl b/templates/user/auth/signin_inner.tmpl
index f8eb81423c..1950f750ce 100644
--- a/templates/user/auth/signin_inner.tmpl
+++ b/templates/user/auth/signin_inner.tmpl
@@ -55,9 +55,9 @@
 	</div>
 	<div id="oauth2-login-navigator" class="gt-py-2">
 		<div class="tw-flex tw-flex-col tw-justify-center">
-			<div id="oauth2-login-navigator-inner" class="tw-flex tw-flex-col tw-flex-wrap tw-content-center gt-gap-3">
+			<div id="oauth2-login-navigator-inner" class="tw-flex tw-flex-col tw-flex-wrap tw-items-center gt-gap-3">
 				{{range $provider := .OAuth2Providers}}
-					<a class="{{$provider.Name}} ui button tw-flex tw-content-center tw-justify-center gt-py-3 tw-w-full oauth-login-link" href="{{AppSubUrl}}/user/oauth2/{{$provider.DisplayName}}">
+					<a class="{{$provider.Name}} ui button tw-flex tw-items-center tw-justify-center gt-py-3 tw-w-full oauth-login-link" href="{{AppSubUrl}}/user/oauth2/{{$provider.DisplayName}}">
 						{{$provider.IconHTML 28}}
 						{{ctx.Locale.Tr "sign_in_with_provider" $provider.DisplayName}}
 					</a>
diff --git a/templates/user/auth/signup_inner.tmpl b/templates/user/auth/signup_inner.tmpl
index a911537996..b96e9bfb02 100644
--- a/templates/user/auth/signup_inner.tmpl
+++ b/templates/user/auth/signup_inner.tmpl
@@ -60,9 +60,9 @@
 			</div>
 			<div id="oauth2-login-navigator" class="gt-py-2">
 				<div class="tw-flex tw-flex-col tw-justify-center">
-					<div id="oauth2-login-navigator-inner" class="tw-flex tw-flex-col tw-flex-wrap tw-content-center gt-gap-3">
+					<div id="oauth2-login-navigator-inner" class="tw-flex tw-flex-col tw-flex-wrap tw-items-center gt-gap-3">
 						{{range $provider := .OAuth2Providers}}
-							<a class="{{$provider.Name}} ui button tw-flex tw-content-center tw-justify-center gt-py-3 tw-w-full oauth-login-link" href="{{AppSubUrl}}/user/oauth2/{{$provider.DisplayName}}">
+							<a class="{{$provider.Name}} ui button tw-flex tw-items-center tw-justify-center gt-py-3 tw-w-full oauth-login-link" href="{{AppSubUrl}}/user/oauth2/{{$provider.DisplayName}}">
 								{{$provider.IconHTML 28}}
 								{{ctx.Locale.Tr "sign_in_with_provider" $provider.DisplayName}}
 							</a>
diff --git a/templates/user/auth/webauthn.tmpl b/templates/user/auth/webauthn.tmpl
index 375ebba9ae..7c543cb16d 100644
--- a/templates/user/auth/webauthn.tmpl
+++ b/templates/user/auth/webauthn.tmpl
@@ -10,7 +10,7 @@
 				{{template "base/alert" .}}
 				<p>{{ctx.Locale.Tr "webauthn_sign_in"}}</p>
 			</div>
-			<div class="ui attached segment tw-flex tw-content-center tw-justify-center gt-gap-2 gt-py-3">
+			<div class="ui attached segment tw-flex tw-items-center tw-justify-center gt-gap-2 gt-py-3">
 				<div class="is-loading" style="width: 40px; height: 40px"></div>
 				{{ctx.Locale.Tr "webauthn_press_button"}}
 			</div>
diff --git a/templates/user/dashboard/milestones.tmpl b/templates/user/dashboard/milestones.tmpl
index 05f2b30efb..3a260c3d10 100644
--- a/templates/user/dashboard/milestones.tmpl
+++ b/templates/user/dashboard/milestones.tmpl
@@ -79,7 +79,7 @@
 									{{svg "octicon-milestone" 16}}
 									<a class="muted" href="{{.Repo.Link}}/milestone/{{.ID}}">{{.Name}}</a>
 								</h3>
-								<div class="tw-flex tw-content-center">
+								<div class="tw-flex tw-items-center">
 									<span class="gt-mr-3">{{.Completeness}}%</span>
 									<progress value="{{.Completeness}}" max="100"></progress>
 								</div>
diff --git a/templates/user/notification/notification_div.tmpl b/templates/user/notification/notification_div.tmpl
index 9da9e16d93..371da129ce 100644
--- a/templates/user/notification/notification_div.tmpl
+++ b/templates/user/notification/notification_div.tmpl
@@ -1,7 +1,7 @@
 <div role="main" aria-label="{{.Title}}" class="page-content user notification" id="notification_div" data-sequence-number="{{.SequenceNumber}}">
 	<div class="ui container">
 		{{$notificationUnreadCount := call .NotificationUnreadCount}}
-		<div class="tw-flex tw-content-center tw-justify-between gt-mb-4">
+		<div class="tw-flex tw-items-center tw-justify-between gt-mb-4">
 			<div class="small-menu-items ui compact tiny menu">
 				<a class="{{if eq .Status 1}}active {{end}}item" href="{{AppSubUrl}}/notifications?q=unread">
 					{{ctx.Locale.Tr "notification.unread"}}
@@ -25,7 +25,7 @@
 		<div class="gt-p-0">
 			<div id="notification_table">
 				{{if not .Notifications}}
-					<div class="tw-flex tw-content-center tw-flex-col gt-p-4">
+					<div class="tw-flex tw-items-center tw-flex-col gt-p-4">
 						{{svg "octicon-inbox" 56 "gt-mb-4"}}
 						{{if eq .Status 1}}
 							{{ctx.Locale.Tr "notification.no_unread"}}
@@ -35,7 +35,7 @@
 					</div>
 				{{else}}
 					{{range $notification := .Notifications}}
-						<div class="notifications-item tw-flex tw-content-center tw-flex-wrap gt-gap-3 gt-p-3" id="notification_{{.ID}}" data-status="{{.Status}}">
+						<div class="notifications-item tw-flex tw-items-center tw-flex-wrap gt-gap-3 gt-p-3" id="notification_{{.ID}}" data-status="{{.Status}}">
 							<div class="notifications-icon gt-ml-3 gt-mr-2 tw-self-start gt-mt-2">
 								{{if .Issue}}
 									{{template "shared/issueicon" .Issue}}
@@ -60,14 +60,14 @@
 									</span>
 								</div>
 							</a>
-							<div class="notifications-updated tw-content-center gt-mr-3">
+							<div class="notifications-updated tw-items-center gt-mr-3">
 								{{if .Issue}}
 									{{TimeSinceUnix .Issue.UpdatedUnix ctx.Locale}}
 								{{else}}
 									{{TimeSinceUnix .UpdatedUnix ctx.Locale}}
 								{{end}}
 							</div>
-							<div class="notifications-buttons tw-content-center tw-justify-end gt-gap-2 gt-px-2">
+							<div class="notifications-buttons tw-items-center tw-justify-end gt-gap-2 gt-px-2">
 								{{if ne .Status 3}}
 									<form action="{{AppSubUrl}}/notifications/status" method="post">
 										{{$.CsrfTokenHtml}}
diff --git a/templates/user/settings/applications_oauth2_list.tmpl b/templates/user/settings/applications_oauth2_list.tmpl
index 1125a66d47..bfbebb104d 100644
--- a/templates/user/settings/applications_oauth2_list.tmpl
+++ b/templates/user/settings/applications_oauth2_list.tmpl
@@ -4,7 +4,7 @@
 			{{ctx.Locale.Tr "settings.oauth2_application_create_description"}}
 		</div>
 		{{range .Applications}}
-			<div class="flex-item tw-content-center">
+			<div class="flex-item tw-items-center">
 				<div class="flex-item-leading">
 					{{svg "octicon-apps" 32}}
 				</div>
diff --git a/templates/user/settings/security/openid.tmpl b/templates/user/settings/security/openid.tmpl
index 63bc56ba9b..b0473c9df5 100644
--- a/templates/user/settings/security/openid.tmpl
+++ b/templates/user/settings/security/openid.tmpl
@@ -7,7 +7,7 @@
 			{{ctx.Locale.Tr "settings.openid_desc"}}
 		</div>
 		{{range .OpenIDs}}
-			<div class="flex-item tw-content-center">
+			<div class="flex-item tw-items-center">
 				<div class="flex-item-leading">
 					{{svg "fontawesome-openid" 20}}
 				</div>
diff --git a/templates/webhook/new.tmpl b/templates/webhook/new.tmpl
index b4126df341..0347455f2c 100644
--- a/templates/webhook/new.tmpl
+++ b/templates/webhook/new.tmpl
@@ -1,7 +1,7 @@
 <h4 class="ui top attached header">
 	{{.CustomHeaderTitle}}
 	<div class="ui right type dropdown">
-		<div class="text tw-flex tw-content-center">
+		<div class="text tw-flex tw-items-center">
 			{{template "shared/webhook/icon" (dict "Size" 20 "HookType" .ctxData.HookType)}}
 			{{ctx.Locale.Tr (print "repo.settings.web_hook_name_" .ctxData.HookType)}}
 		</div>
diff --git a/tests/integration/release_test.go b/tests/integration/release_test.go
index b1b08129bd..acc8cb68c2 100644
--- a/tests/integration/release_test.go
+++ b/tests/integration/release_test.go
@@ -285,7 +285,7 @@ func TestViewTagsList(t *testing.T) {
 
 	tagNames := make([]string, 0, 5)
 	tags.Each(func(i int, s *goquery.Selection) {
-		tagNames = append(tagNames, s.Find(".tag a.tw-flex.tw-content-center").Text())
+		tagNames = append(tagNames, s.Find(".tag a.tw-flex.tw-items-center").Text())
 	})
 
 	assert.EqualValues(t, []string{"v1.0", "delete-tag", "v1.1"}, tagNames)
diff --git a/web_src/js/components/ActionRunStatus.vue b/web_src/js/components/ActionRunStatus.vue
index 4eccddffdf..7ada543fea 100644
--- a/web_src/js/components/ActionRunStatus.vue
+++ b/web_src/js/components/ActionRunStatus.vue
@@ -28,7 +28,7 @@ export default {
 };
 </script>
 <template>
-  <span class="tw-flex tw-content-center" :data-tooltip-content="localeStatus" v-if="status">
+  <span class="tw-flex tw-items-center" :data-tooltip-content="localeStatus" v-if="status">
     <SvgIcon name="octicon-check-circle-fill" class="text green" :size="size" :class-name="className" v-if="status === 'success'"/>
     <SvgIcon name="octicon-skip" class="text grey" :size="size" :class-name="className" v-else-if="status === 'skipped'"/>
     <SvgIcon name="octicon-clock" class="text yellow" :size="size" :class-name="className" v-else-if="status === 'waiting'"/>
diff --git a/web_src/js/components/DashboardRepoList.vue b/web_src/js/components/DashboardRepoList.vue
index 4b1fe7c636..64de258f96 100644
--- a/web_src/js/components/DashboardRepoList.vue
+++ b/web_src/js/components/DashboardRepoList.vue
@@ -344,8 +344,8 @@ export default sfc; // activate the IDE's Vue plugin
       <a :class="{item: true, active: tab === 'organizations'}" @click="changeTab('organizations')">{{ textMyOrgs }} <span class="ui grey label gt-ml-3">{{ organizationsTotalCount }}</span></a>
     </div>
     <div v-show="tab === 'repos'" class="ui tab active list dashboard-repos">
-      <h4 v-if="isOrganization" class="ui top attached gt-mt-4 tw-flex tw-content-center">
-        <div class="tw-flex-1 tw-flex tw-content-center">
+      <h4 v-if="isOrganization" class="ui top attached gt-mt-4 tw-flex tw-items-center">
+        <div class="tw-flex-1 tw-flex tw-items-center">
           {{ textMyRepos }}
           <span class="ui grey label gt-ml-3">{{ reposTotalCount }}</span>
         </div>
@@ -407,7 +407,7 @@ export default sfc; // activate the IDE's Vue plugin
       </div>
       <div v-if="repos.length" class="ui attached table segment tw-rounded-b">
         <ul class="repo-owner-name-list">
-          <li class="tw-flex tw-content-center gt-py-3" v-for="repo, index in repos" :class="{'active': index === activeIndex}" :key="repo.id">
+          <li class="tw-flex tw-items-center gt-py-3" v-for="repo, index in repos" :class="{'active': index === activeIndex}" :key="repo.id">
             <a class="repo-list-link muted" :href="repo.link">
               <svg-icon :name="repoIcon(repo)" :size="16" class-name="repo-list-icon"/>
               <div class="text truncate">{{ repo.full_name }}</div>
@@ -415,7 +415,7 @@ export default sfc; // activate the IDE's Vue plugin
                 <svg-icon name="octicon-archive" :size="16"/>
               </div>
             </a>
-            <a class="tw-flex tw-content-center" v-if="repo.latest_commit_status" :href="repo.latest_commit_status.TargetLink" :data-tooltip-content="repo.locale_latest_commit_status.State">
+            <a class="tw-flex tw-items-center" v-if="repo.latest_commit_status" :href="repo.latest_commit_status.TargetLink" :data-tooltip-content="repo.locale_latest_commit_status.State">
               <!-- the commit status icon logic is taken from templates/repo/commit_status.tmpl -->
               <svg-icon :name="statusIcon(repo.latest_commit_status.State)" :class-name="'gt-ml-3 commit-status icon text ' + statusColor(repo.latest_commit_status.State)" :size="16"/>
             </a>
@@ -456,7 +456,7 @@ export default sfc; // activate the IDE's Vue plugin
     <div v-if="!isOrganization" v-show="tab === 'organizations'" class="ui tab active list dashboard-orgs">
       <div v-if="organizations.length" class="ui attached table segment tw-rounded-b">
         <ul class="repo-owner-name-list">
-          <li class="tw-flex tw-content-center gt-py-3" v-for="org in organizations" :key="org.name">
+          <li class="tw-flex tw-items-center gt-py-3" v-for="org in organizations" :key="org.name">
             <a class="repo-list-link muted" :href="subUrl + '/' + encodeURIComponent(org.name)">
               <svg-icon name="octicon-organization" :size="16" class-name="repo-list-icon"/>
               <div class="text truncate">{{ org.name }}</div>
@@ -466,7 +466,7 @@ export default sfc; // activate the IDE's Vue plugin
                 </span>
               </div>
             </a>
-            <div class="text light grey tw-flex tw-content-center gt-ml-3">
+            <div class="text light grey tw-flex tw-items-center gt-ml-3">
               {{ org.num_repos }}
               <svg-icon name="octicon-repo" :size="16" class-name="gt-ml-2 gt-mt-1"/>
             </div>
diff --git a/web_src/js/components/DiffFileList.vue b/web_src/js/components/DiffFileList.vue
index 64493b348a..3a0e287808 100644
--- a/web_src/js/components/DiffFileList.vue
+++ b/web_src/js/components/DiffFileList.vue
@@ -38,7 +38,7 @@ export default {
 <template>
   <ol class="diff-stats gt-m-0" ref="root" v-if="store.fileListIsVisible">
     <li v-for="file in store.files" :key="file.NameHash">
-      <div class="gt-font-semibold tw-flex tw-content-center pull-right">
+      <div class="gt-font-semibold tw-flex tw-items-center pull-right">
         <span v-if="file.IsBin" class="gt-ml-1 gt-mr-3">{{ store.binaryFileMessage }}</span>
         {{ file.IsBin ? '' : file.Addition + file.Deletion }}
         <span v-if="!file.IsBin" class="diff-stats-bar gt-mx-3" :data-tooltip-content="store.statisticsMessage.replace('%d', (file.Addition + file.Deletion)).replace('%d', file.Addition).replace('%d', file.Deletion)">
@@ -50,7 +50,7 @@ export default {
       <a class="file gt-mono" :href="'#diff-' + file.NameHash">{{ file.Name }}</a>
     </li>
     <li v-if="store.isIncomplete" class="gt-pt-2">
-      <span class="file tw-flex tw-content-center tw-justify-between">{{ store.tooManyFilesMessage }}
+      <span class="file tw-flex tw-items-center tw-justify-between">{{ store.tooManyFilesMessage }}
         <a :class="['ui', 'basic', 'tiny', 'button', store.isLoadingNewData ? 'disabled' : '']" @click.stop="loadMoreData">{{ store.showMoreMessage }}</a>
       </span>
     </li>
diff --git a/web_src/js/components/RepoBranchTagSelector.vue b/web_src/js/components/RepoBranchTagSelector.vue
index e3e0c13fb9..588c05990a 100644
--- a/web_src/js/components/RepoBranchTagSelector.vue
+++ b/web_src/js/components/RepoBranchTagSelector.vue
@@ -246,7 +246,7 @@ export default sfc; // activate IDE's Vue plugin
 <template>
   <div class="ui dropdown custom">
     <button class="branch-dropdown-button gt-ellipsis ui basic small compact button tw-flex gt-m-0" @click="menuVisible = !menuVisible" @keyup.enter="menuVisible = !menuVisible">
-      <span class="text tw-flex tw-content-center gt-mr-2">
+      <span class="text tw-flex tw-items-center gt-mr-2">
         <template v-if="release">{{ textReleaseCompare }}</template>
         <template v-else>
           <svg-icon v-if="isViewTag" name="octicon-tag"/>
diff --git a/web_src/js/components/RepoCodeFrequency.vue b/web_src/js/components/RepoCodeFrequency.vue
index 4e11d51a4a..f51dac0a6d 100644
--- a/web_src/js/components/RepoCodeFrequency.vue
+++ b/web_src/js/components/RepoCodeFrequency.vue
@@ -144,7 +144,7 @@ export default {
 </script>
 <template>
   <div>
-    <div class="ui header tw-flex tw-content-center tw-justify-between">
+    <div class="ui header tw-flex tw-items-center tw-justify-between">
       {{ isLoading ? locale.loadingTitle : errorText ? locale.loadingTitleFailed: `Code frequency over the history of ${repoLink.slice(1)}` }}
     </div>
     <div class="tw-flex ui segment main-graph">
diff --git a/web_src/js/components/RepoContributors.vue b/web_src/js/components/RepoContributors.vue
index 731b21bea3..adaf7f28f1 100644
--- a/web_src/js/components/RepoContributors.vue
+++ b/web_src/js/components/RepoContributors.vue
@@ -303,7 +303,7 @@ export default {
 </script>
 <template>
   <div>
-    <div class="ui header tw-flex tw-content-center tw-justify-between">
+    <div class="ui header tw-flex tw-items-center tw-justify-between">
       <div>
         <relative-time
           v-if="xAxisMin > 0"
diff --git a/web_src/js/components/RepoRecentCommits.vue b/web_src/js/components/RepoRecentCommits.vue
index 1818d57943..601252419a 100644
--- a/web_src/js/components/RepoRecentCommits.vue
+++ b/web_src/js/components/RepoRecentCommits.vue
@@ -121,7 +121,7 @@ export default {
 </script>
 <template>
   <div>
-    <div class="ui header tw-flex tw-content-center tw-justify-between">
+    <div class="ui header tw-flex tw-items-center tw-justify-between">
       {{ isLoading ? locale.loadingTitle : errorText ? locale.loadingTitleFailed: "Number of commits in the past year" }}
     </div>
     <div class="tw-flex ui segment main-graph">
diff --git a/web_src/js/features/repo-issue-content.js b/web_src/js/features/repo-issue-content.js
index 33ea55f027..e7768b066e 100644
--- a/web_src/js/features/repo-issue-content.js
+++ b/web_src/js/features/repo-issue-content.js
@@ -16,7 +16,7 @@ function showContentHistoryDetail(issueBaseUrl, commentId, historyId, itemTitleH
   $dialog = $(`
 <div class="ui modal content-history-detail-dialog">
   ${svg('octicon-x', 16, 'close icon inside')}
-  <div class="header tw-flex tw-content-center tw-justify-between">
+  <div class="header tw-flex tw-items-center tw-justify-between">
     <div>${itemTitleHtml}</div>
     <div class="ui dropdown dialog-header-options gt-mr-5 gt-hidden">
       ${i18nTextOptions}

From c3e9b8b6f9b49981c69fc5069d128ffc9cc67639 Mon Sep 17 00:00:00 2001
From: Yarden Shoham <git@yardenshoham.com>
Date: Fri, 22 Mar 2024 21:56:38 +0200
Subject: [PATCH 54/74] Remove jQuery `.attr` from the project page (#30004)

- Switched from jQuery `.attr` to plain javascript `.getAttribute`
- Tested the issue movement between columns, column background color
setting, and column deletion. It all 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>
(cherry picked from commit 5c91d7920f4aff08768e274269e211e926aa3d36)
---
 web_src/js/features/repo-projects.js | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/web_src/js/features/repo-projects.js b/web_src/js/features/repo-projects.js
index e95d875ec4..fc688bb695 100644
--- a/web_src/js/features/repo-projects.js
+++ b/web_src/js/features/repo-projects.js
@@ -33,7 +33,7 @@ async function moveIssue({item, from, to, oldIndex}) {
 
   const columnSorting = {
     issues: Array.from(columnCards, (card, i) => ({
-      issueID: parseInt($(card).attr('data-issue')),
+      issueID: parseInt(card.getAttribute('data-issue')),
       sorting: i,
     })),
   };
@@ -134,7 +134,7 @@ export function initRepoProject() {
         if ($projectColorInput.val()) {
           setLabelColor($projectHeader, $projectColorInput.val());
         }
-        $boardColumn.attr('style', `background: ${$projectColorInput.val()}!important`);
+        $boardColumn[0].style = `background: ${$projectColorInput.val()} !important`;
         $('.ui.modal').modal('hide');
       }
     });
@@ -159,9 +159,9 @@ export function initRepoProject() {
   });
 
   $('.show-delete-project-column-modal').each(function () {
-    const $deleteColumnModal = $(`${$(this).attr('data-modal')}`);
+    const $deleteColumnModal = $(`${this.getAttribute('data-modal')}`);
     const $deleteColumnButton = $deleteColumnModal.find('.actions > .ok.button');
-    const deleteUrl = $(this).attr('data-url');
+    const deleteUrl = this.getAttribute('data-url');
 
     $deleteColumnButton.on('click', async (e) => {
       e.preventDefault();

From bc31bf2d096d8c4e65c1ef75c0294699040bd91f Mon Sep 17 00:00:00 2001
From: Yarden Shoham <git@yardenshoham.com>
Date: Sat, 23 Mar 2024 01:17:28 +0200
Subject: [PATCH 55/74] Remove jQuery from the citation modal (except fomantic)
 (#30008)

- Switched to plain JavaScript
- Tested the citation modal functionality and it works as before

# Demo using JavaScript without jQuery

![demo](https://github.com/go-gitea/gitea/assets/20454870/65bba1eb-dd4c-477f-8a2d-08e65f1e9f42)

---------

Signed-off-by: Yarden Shoham <git@yardenshoham.com>
Co-authored-by: silverwind <me@silverwind.io>
(cherry picked from commit dade40407e333fbc6811bdd738c3d191b5dc0ef1)
---
 web_src/js/features/citation.js | 40 +++++++++++++++++----------------
 1 file changed, 21 insertions(+), 19 deletions(-)

diff --git a/web_src/js/features/citation.js b/web_src/js/features/citation.js
index 49992b225f..918a467136 100644
--- a/web_src/js/features/citation.js
+++ b/web_src/js/features/citation.js
@@ -1,8 +1,9 @@
 import $ from 'jquery';
+import {getCurrentLocale} from '../utils.js';
 
 const {pageData} = window.config;
 
-async function initInputCitationValue($citationCopyApa, $citationCopyBibtex) {
+async function initInputCitationValue(citationCopyApa, citationCopyBibtex) {
   const [{Cite, plugins}] = await Promise.all([
     import(/* webpackChunkName: "citation-js-core" */'@citation-js/core'),
     import(/* webpackChunkName: "citation-js-formats" */'@citation-js/plugin-software-formats'),
@@ -14,11 +15,11 @@ async function initInputCitationValue($citationCopyApa, $citationCopyBibtex) {
   config.constants.fieldTypes.doi = ['field', 'literal'];
   config.constants.fieldTypes.version = ['field', 'literal'];
   const citationFormatter = new Cite(citationFileContent);
-  const lang = document.documentElement.lang || 'en-US';
+  const lang = getCurrentLocale() || 'en-US';
   const apaOutput = citationFormatter.format('bibliography', {template: 'apa', lang});
   const bibtexOutput = citationFormatter.format('bibtex', {lang});
-  $citationCopyBibtex.attr('data-text', bibtexOutput);
-  $citationCopyApa.attr('data-text', apaOutput);
+  citationCopyBibtex.setAttribute('data-text', bibtexOutput);
+  citationCopyApa.setAttribute('data-text', apaOutput);
 }
 
 export async function initCitationFileCopyContent() {
@@ -26,44 +27,45 @@ export async function initCitationFileCopyContent() {
 
   if (!pageData.citationFileContent) return;
 
-  const $citationCopyApa = $('#citation-copy-apa');
-  const $citationCopyBibtex = $('#citation-copy-bibtex');
-  const $inputContent = $('#citation-copy-content');
+  const citationCopyApa = document.getElementById('citation-copy-apa');
+  const citationCopyBibtex = document.getElementById('citation-copy-bibtex');
+  const inputContent = document.getElementById('citation-copy-content');
+
+  if ((!citationCopyApa && !citationCopyBibtex) || !inputContent) return;
 
-  if ((!$citationCopyApa.length && !$citationCopyBibtex.length) || !$inputContent.length) return;
   const updateUi = () => {
     const isBibtex = (localStorage.getItem('citation-copy-format') || defaultCitationFormat) === 'bibtex';
-    const copyContent = (isBibtex ? $citationCopyBibtex : $citationCopyApa).attr('data-text');
-
-    $inputContent.val(copyContent);
-    $citationCopyBibtex.toggleClass('primary', isBibtex);
-    $citationCopyApa.toggleClass('primary', !isBibtex);
+    const copyContent = (isBibtex ? citationCopyBibtex : citationCopyApa).getAttribute('data-text');
+    inputContent.value = copyContent;
+    citationCopyBibtex.classList.toggle('primary', isBibtex);
+    citationCopyApa.classList.toggle('primary', !isBibtex);
   };
 
-  $('#cite-repo-button').on('click', async (e) => {
+  document.getElementById('cite-repo-button')?.addEventListener('click', async (e) => {
     const dropdownBtn = e.target.closest('.ui.dropdown.button');
     dropdownBtn.classList.add('is-loading');
 
     try {
       try {
-        await initInputCitationValue($citationCopyApa, $citationCopyBibtex);
+        await initInputCitationValue(citationCopyApa, citationCopyBibtex);
       } catch (e) {
         console.error(`initCitationFileCopyContent error: ${e}`, e);
         return;
       }
       updateUi();
 
-      $citationCopyApa.on('click', () => {
+      citationCopyApa.addEventListener('click', () => {
         localStorage.setItem('citation-copy-format', 'apa');
         updateUi();
       });
-      $citationCopyBibtex.on('click', () => {
+
+      citationCopyBibtex.addEventListener('click', () => {
         localStorage.setItem('citation-copy-format', 'bibtex');
         updateUi();
       });
 
-      $inputContent.on('click', () => {
-        $inputContent.trigger('select');
+      inputContent.addEventListener('click', () => {
+        inputContent.select();
       });
     } finally {
       dropdownBtn.classList.remove('is-loading');

From 598d7c030da5cc8f6ccadbcff41645b86fae2c29 Mon Sep 17 00:00:00 2001
From: Yarden Shoham <git@yardenshoham.com>
Date: Sat, 23 Mar 2024 01:22:01 +0200
Subject: [PATCH 56/74] Remove jQuery `.attr` from the commit graph (#30006)

Switched from jQuery `.attr` to plain javascript `.getAttribute` and
`.setAttribute`

---------

Signed-off-by: Yarden Shoham <git@yardenshoham.com>
(cherry picked from commit d4ac1bd26e3ebc8e3bc7e84313e566634b672477)
---
 web_src/js/features/repo-graph.js | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/web_src/js/features/repo-graph.js b/web_src/js/features/repo-graph.js
index 4fbf95b9d5..f2c0d78f34 100644
--- a/web_src/js/features/repo-graph.js
+++ b/web_src/js/features/repo-graph.js
@@ -18,13 +18,13 @@ export function initRepoGraphGit() {
       window.history.replaceState({}, '', window.location.pathname);
     }
     $('.pagination a').each((_, that) => {
-      const href = $(that).attr('href');
+      const href = that.getAttribute('href');
       if (!href) return;
       const url = new URL(href, window.location);
       const params = url.searchParams;
       params.set('mode', 'monochrome');
       url.search = `?${params.toString()}`;
-      $(that).attr('href', url.href);
+      that.setAttribute('href', url.href);
     });
   });
   $('#flow-color-colored').on('click', () => {
@@ -32,13 +32,13 @@ export function initRepoGraphGit() {
     $('#flow-color-monochrome').removeClass('active');
     $('#git-graph-container').addClass('colored').removeClass('monochrome');
     $('.pagination a').each((_, that) => {
-      const href = $(that).attr('href');
+      const href = that.getAttribute('href');
       if (!href) return;
       const url = new URL(href, window.location);
       const params = url.searchParams;
       params.delete('mode');
       url.search = `?${params.toString()}`;
-      $(that).attr('href', url.href);
+      that.setAttribute('href', url.href);
     });
     const params = new URLSearchParams(window.location.search);
     params.delete('mode');

From 7092ffc5dd6a9496977e46e2e280d8925ce994d0 Mon Sep 17 00:00:00 2001
From: Yarden Shoham <git@yardenshoham.com>
Date: Sat, 23 Mar 2024 01:26:56 +0200
Subject: [PATCH 57/74] Remove jQuery `.attr` from the branch/tag selector
 (#30010)

- Switched from jQuery `.attr` to plain javascript `.setAttribute`
- Tested the cherry-pick from the branch/tag selector and it works as
before

Signed-off-by: Yarden Shoham <git@yardenshoham.com>
(cherry picked from commit bc92478575d9c1e84aa4ba4052dffcdc109a0323)
---
 web_src/js/components/RepoBranchTagSelector.vue | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/web_src/js/components/RepoBranchTagSelector.vue b/web_src/js/components/RepoBranchTagSelector.vue
index 588c05990a..34e8859609 100644
--- a/web_src/js/components/RepoBranchTagSelector.vue
+++ b/web_src/js/components/RepoBranchTagSelector.vue
@@ -83,7 +83,7 @@ const sfc = {
         this.isViewBranch = false;
         this.$refs.dropdownRefName.textContent = item.name;
         if (this.setAction) {
-          $(`#${this.branchForm}`).attr('action', url);
+          document.getElementById(this.branchForm)?.setAttribute('action', url);
         } else {
           $(`#${this.branchForm} input[name="refURL"]`).val(url);
         }

From a39580681267afd909e48045829c3fcea81887ff Mon Sep 17 00:00:00 2001
From: Yarden Shoham <git@yardenshoham.com>
Date: Sat, 23 Mar 2024 13:51:34 +0200
Subject: [PATCH 58/74] Remove jQuery `.attr` from the image diff again
 (#30022)

- Follows https://github.com/go-gitea/gitea/pull/29917

Missed these

Signed-off-by: Yarden Shoham <git@yardenshoham.com>
(cherry picked from commit d0d7b4b6d124dd8fd420a5f3850e37794e09e302)
---
 web_src/js/features/imagediff.js | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/web_src/js/features/imagediff.js b/web_src/js/features/imagediff.js
index 7b77b30ccc..d567632f5f 100644
--- a/web_src/js/features/imagediff.js
+++ b/web_src/js/features/imagediff.js
@@ -98,8 +98,10 @@ export function initImageDiff() {
         const text = await resp.text();
         const bounds = getDefaultSvgBoundsIfUndefined(text, info.path);
         if (bounds) {
-          info.$images.attr('width', bounds.width);
-          info.$images.attr('height', bounds.height);
+          info.$images.each(function() {
+            this.setAttribute('width', bounds.width);
+            this.setAttribute('height', bounds.height);
+          });
           hideElem(info.$boundsInfo);
         }
       }

From 08a9a61faf87a4f50c9d132f0b6e851045ddec33 Mon Sep 17 00:00:00 2001
From: Yarden Shoham <git@yardenshoham.com>
Date: Sat, 23 Mar 2024 14:28:53 +0200
Subject: [PATCH 59/74] Remove jQuery `.attr` from the repository settings
 (#30018)

- Switched from jQuery `.attr` to plain javascript `getAttribute` and
`setAttribute`
- Tested the collaborator access mode change, team search box, and
branch protection form. They all work as before

---------

Signed-off-by: Yarden Shoham <git@yardenshoham.com>
(cherry picked from commit 26dbca741114587f4191050a76ee1a36282a2018)
---
 web_src/js/features/repo-settings.js | 30 +++++++++++++++-------------
 1 file changed, 16 insertions(+), 14 deletions(-)

diff --git a/web_src/js/features/repo-settings.js b/web_src/js/features/repo-settings.js
index 58b714fbb7..dc1db8ab29 100644
--- a/web_src/js/features/repo-settings.js
+++ b/web_src/js/features/repo-settings.js
@@ -8,22 +8,22 @@ const {appSubUrl, csrfToken} = window.config;
 
 export function initRepoSettingsCollaboration() {
   // Change collaborator access mode
-  $('.page-content.repository .ui.dropdown.access-mode').each((_, e) => {
-    const $dropdown = $(e);
+  $('.page-content.repository .ui.dropdown.access-mode').each((_, el) => {
+    const $dropdown = $(el);
     const $text = $dropdown.find('> .text');
     $dropdown.dropdown({
       async action(_text, value) {
-        const lastValue = $dropdown.attr('data-last-value');
+        const lastValue = el.getAttribute('data-last-value');
         try {
-          $dropdown.attr('data-last-value', value);
+          el.setAttribute('data-last-value', value);
           $dropdown.dropdown('hide');
           const data = new FormData();
-          data.append('uid', $dropdown.attr('data-uid'));
+          data.append('uid', el.getAttribute('data-uid'));
           data.append('mode', value);
-          await POST($dropdown.attr('data-url'), {data});
+          await POST(el.getAttribute('data-url'), {data});
         } catch {
           $text.text('(error)'); // prevent from misleading users when error occurs
-          $dropdown.attr('data-last-value', lastValue);
+          el.setAttribute('data-last-value', lastValue);
         }
       },
       onChange(_value, text, _$choice) {
@@ -32,9 +32,9 @@ export function initRepoSettingsCollaboration() {
       onHide() {
         // set to the really selected value, defer to next tick to make sure `action` has finished its work because the calling order might be onHide -> action
         setTimeout(() => {
-          const $item = $dropdown.dropdown('get item', $dropdown.attr('data-last-value'));
+          const $item = $dropdown.dropdown('get item', el.getAttribute('data-last-value'));
           if ($item) {
-            $dropdown.dropdown('set selected', $dropdown.attr('data-last-value'));
+            $dropdown.dropdown('set selected', el.getAttribute('data-last-value'));
           } else {
             $text.text('(none)'); // prevent from misleading users when the access mode is undefined
           }
@@ -45,11 +45,13 @@ export function initRepoSettingsCollaboration() {
 }
 
 export function initRepoSettingSearchTeamBox() {
-  const $searchTeamBox = $('#search-team-box');
-  $searchTeamBox.search({
+  const searchTeamBox = document.getElementById('search-team-box');
+  if (!searchTeamBox) return;
+
+  $(searchTeamBox).search({
     minCharacters: 2,
     apiSettings: {
-      url: `${appSubUrl}/org/${$searchTeamBox.attr('data-org-name')}/teams/-/search?q={query}`,
+      url: `${appSubUrl}/org/${searchTeamBox.getAttribute('data-org-name')}/teams/-/search?q={query}`,
       headers: {'X-Csrf-Token': csrfToken},
       onResponse(response) {
         const items = [];
@@ -77,11 +79,11 @@ export function initRepoSettingGitHook() {
 export function initRepoSettingBranches() {
   if (!$('.repository.settings.branches').length) return;
   $('.toggle-target-enabled').on('change', function () {
-    const $target = $($(this).attr('data-target'));
+    const $target = $(this.getAttribute('data-target'));
     $target.toggleClass('disabled', !this.checked);
   });
   $('.toggle-target-disabled').on('change', function () {
-    const $target = $($(this).attr('data-target'));
+    const $target = $(this.getAttribute('data-target'));
     if (this.checked) $target.addClass('disabled'); // only disable, do not auto enable
   });
   $('#dismiss_stale_approvals').on('change', function () {

From d64b9757e8a4728135885e029f3932ff8f71dad3 Mon Sep 17 00:00:00 2001
From: Yarden Shoham <git@yardenshoham.com>
Date: Sat, 23 Mar 2024 14:37:18 +0200
Subject: [PATCH 60/74] Remove jQuery `.attr` from the diff page (#30021)

- Switched from jQuery `.attr` to plain javascript `getAttribute` and
`setAttribute`
- Tested the review box counter and Previous/Next code review
conversation buttons. They work as before

---------

Signed-off-by: Yarden Shoham <git@yardenshoham.com>
Co-authored-by: silverwind <me@silverwind.io>
(cherry picked from commit 74c1378dfb5f1831ca2bf1f0b18ab150134f4beb)
---
 web_src/js/features/repo-diff.js | 23 +++++++++++++----------
 1 file changed, 13 insertions(+), 10 deletions(-)

diff --git a/web_src/js/features/repo-diff.js b/web_src/js/features/repo-diff.js
index ffe3fada85..2feb2597e4 100644
--- a/web_src/js/features/repo-diff.js
+++ b/web_src/js/features/repo-diff.js
@@ -13,16 +13,20 @@ import {POST, GET} from '../modules/fetch.js';
 const {pageData, i18n} = window.config;
 
 function initRepoDiffReviewButton() {
-  const $reviewBox = $('#review-box');
-  const $counter = $reviewBox.find('.review-comments-counter');
+  const reviewBox = document.getElementById('review-box');
+  if (!reviewBox) return;
+
+  const $reviewBox = $(reviewBox);
+  const counter = reviewBox.querySelector('.review-comments-counter');
+  if (!counter) return;
 
   $(document).on('click', 'button[name="pending_review"]', (e) => {
     const $form = $(e.target).closest('form');
     // Watch for the form's submit event.
     $form.on('submit', () => {
-      const num = parseInt($counter.attr('data-pending-comment-number')) + 1 || 1;
-      $counter.attr('data-pending-comment-number', num);
-      $counter.text(num);
+      const num = parseInt(counter.getAttribute('data-pending-comment-number')) + 1 || 1;
+      counter.setAttribute('data-pending-comment-number', num);
+      counter.textContent = num;
       // Force the browser to reflow the DOM. This is to ensure that the browser replay the animation
       $reviewBox.removeClass('pulse');
       $reviewBox.width();
@@ -67,7 +71,7 @@ function initRepoDiffConversationForm() {
         formData.append(submitter.name, submitter.value);
       }
 
-      const response = await POST($form.attr('action'), {data: formData});
+      const response = await POST(e.target.getAttribute('action'), {data: formData});
       const $newConversationHolder = $(await response.text());
       const {path, side, idx} = $newConversationHolder.data();
 
@@ -120,7 +124,7 @@ export function initRepoDiffConversationNav() {
     const index = $conversations.index($conversation);
     const previousIndex = index > 0 ? index - 1 : $conversations.length - 1;
     const $previousConversation = $conversations.eq(previousIndex);
-    const anchor = $previousConversation.find('.comment').first().attr('id');
+    const anchor = $previousConversation.find('.comment').first()[0].getAttribute('id');
     window.location.href = `#${anchor}`;
   });
   $(document).on('click', '.next-conversation', (e) => {
@@ -129,7 +133,7 @@ export function initRepoDiffConversationNav() {
     const index = $conversations.index($conversation);
     const nextIndex = index < $conversations.length - 1 ? index + 1 : 0;
     const $nextConversation = $conversations.eq(nextIndex);
-    const anchor = $nextConversation.find('.comment').first().attr('id');
+    const anchor = $nextConversation.find('.comment').first()[0].getAttribute('id');
     window.location.href = `#${anchor}`;
   });
 }
@@ -175,8 +179,7 @@ function initRepoDiffShowMore() {
   $(document).on('click', 'a#diff-show-more-files', (e) => {
     e.preventDefault();
 
-    const $target = $(e.target);
-    const linkLoadMore = $target.attr('data-href');
+    const linkLoadMore = e.target.getAttribute('data-href');
     loadMoreFiles(linkLoadMore);
   });
 

From b73117127ca9a77da2bfb79109702bbe538d8a16 Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Sat, 23 Mar 2024 21:07:47 +0800
Subject: [PATCH 61/74] Escape paths for find file correctly (#30026)

Fix #30020

(cherry picked from commit 1cdc6c3a4ea28396788b2697f9cf257df161ff9a)
---
 routers/web/repo/find.go | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/routers/web/repo/find.go b/routers/web/repo/find.go
index 07b3722798..9da4237c1e 100644
--- a/routers/web/repo/find.go
+++ b/routers/web/repo/find.go
@@ -7,6 +7,7 @@ import (
 	"net/http"
 
 	"code.gitea.io/gitea/modules/base"
+	"code.gitea.io/gitea/modules/util"
 	"code.gitea.io/gitea/services/context"
 )
 
@@ -17,7 +18,7 @@ const (
 // FindFiles render the page to find repository files
 func FindFiles(ctx *context.Context) {
 	path := ctx.Params("*")
-	ctx.Data["TreeLink"] = ctx.Repo.RepoLink + "/src/" + path
-	ctx.Data["DataLink"] = ctx.Repo.RepoLink + "/tree-list/" + path
+	ctx.Data["TreeLink"] = ctx.Repo.RepoLink + "/src/" + util.PathEscapeSegments(path)
+	ctx.Data["DataLink"] = ctx.Repo.RepoLink + "/tree-list/" + util.PathEscapeSegments(path)
 	ctx.HTML(http.StatusOK, tplFindFiles)
 }

From ab5f0b7558229b3ab5c3946a51e58b4caae775b0 Mon Sep 17 00:00:00 2001
From: 6543 <6543@obermui.de>
Date: Sat, 23 Mar 2024 16:45:13 +0100
Subject: [PATCH 62/74] Determine fuzziness of bleve indexer by keyword length
 (#29706)

also bleve did match on fuzzy search and the other way around. this also fix that bug.

(cherry picked from commit b9c57fb78e8e0d80d786d8e1da433b6c7ebf2f1c)

Conflicts:
	tests/integration/repo_search_test.go
	simple conflict resolution in the tests
---
 modules/indexer/code/bleve/bleve.go     | 15 +++++++--------
 modules/indexer/internal/bleve/query.go | 10 ++--------
 modules/indexer/issues/bleve/bleve.go   | 25 +++++++++++++------------
 tests/integration/repo_search_test.go   | 16 +++++++---------
 4 files changed, 29 insertions(+), 37 deletions(-)

diff --git a/modules/indexer/code/bleve/bleve.go b/modules/indexer/code/bleve/bleve.go
index d7f735e957..c607d780ef 100644
--- a/modules/indexer/code/bleve/bleve.go
+++ b/modules/indexer/code/bleve/bleve.go
@@ -39,6 +39,8 @@ import (
 const (
 	unicodeNormalizeName = "unicodeNormalize"
 	maxBatchSize         = 16
+	// fuzzyDenominator determines the levenshtein distance per each character of a keyword
+	fuzzyDenominator = 4
 )
 
 func addUnicodeNormalizeTokenFilter(m *mapping.IndexMappingImpl) error {
@@ -239,15 +241,12 @@ func (b *Indexer) Search(ctx context.Context, opts *internal.SearchOptions) (int
 		keywordQuery query.Query
 	)
 
+	phraseQuery := bleve.NewMatchPhraseQuery(opts.Keyword)
+	phraseQuery.FieldVal = "Content"
+	phraseQuery.Analyzer = repoIndexerAnalyzer
+	keywordQuery = phraseQuery
 	if opts.IsKeywordFuzzy {
-		phraseQuery := bleve.NewMatchPhraseQuery(opts.Keyword)
-		phraseQuery.FieldVal = "Content"
-		phraseQuery.Analyzer = repoIndexerAnalyzer
-		keywordQuery = phraseQuery
-	} else {
-		prefixQuery := bleve.NewPrefixQuery(opts.Keyword)
-		prefixQuery.FieldVal = "Content"
-		keywordQuery = prefixQuery
+		phraseQuery.Fuzziness = len(opts.Keyword) / fuzzyDenominator
 	}
 
 	if len(opts.RepoIDs) > 0 {
diff --git a/modules/indexer/internal/bleve/query.go b/modules/indexer/internal/bleve/query.go
index b96875343e..21422b281c 100644
--- a/modules/indexer/internal/bleve/query.go
+++ b/modules/indexer/internal/bleve/query.go
@@ -20,17 +20,11 @@ func NumericEqualityQuery(value int64, field string) *query.NumericRangeQuery {
 }
 
 // MatchPhraseQuery generates a match phrase query for the given phrase, field and analyzer
-func MatchPhraseQuery(matchPhrase, field, analyzer string) *query.MatchPhraseQuery {
+func MatchPhraseQuery(matchPhrase, field, analyzer string, fuzziness int) *query.MatchPhraseQuery {
 	q := bleve.NewMatchPhraseQuery(matchPhrase)
 	q.FieldVal = field
 	q.Analyzer = analyzer
-	return q
-}
-
-// PrefixQuery generates a match prefix query for the given prefix and field
-func PrefixQuery(matchPrefix, field string) *query.PrefixQuery {
-	q := bleve.NewPrefixQuery(matchPrefix)
-	q.FieldVal = field
+	q.Fuzziness = fuzziness
 	return q
 }
 
diff --git a/modules/indexer/issues/bleve/bleve.go b/modules/indexer/issues/bleve/bleve.go
index 927ad58cd4..1f54be721b 100644
--- a/modules/indexer/issues/bleve/bleve.go
+++ b/modules/indexer/issues/bleve/bleve.go
@@ -35,7 +35,11 @@ func addUnicodeNormalizeTokenFilter(m *mapping.IndexMappingImpl) error {
 	})
 }
 
-const maxBatchSize = 16
+const (
+	maxBatchSize = 16
+	// fuzzyDenominator determines the levenshtein distance per each character of a keyword
+	fuzzyDenominator = 4
+)
 
 // IndexerData an update to the issue indexer
 type IndexerData internal.IndexerData
@@ -156,19 +160,16 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) (
 	var queries []query.Query
 
 	if options.Keyword != "" {
+		fuzziness := 0
 		if options.IsFuzzyKeyword {
-			queries = append(queries, bleve.NewDisjunctionQuery([]query.Query{
-				inner_bleve.MatchPhraseQuery(options.Keyword, "title", issueIndexerAnalyzer),
-				inner_bleve.MatchPhraseQuery(options.Keyword, "content", issueIndexerAnalyzer),
-				inner_bleve.MatchPhraseQuery(options.Keyword, "comments", issueIndexerAnalyzer),
-			}...))
-		} else {
-			queries = append(queries, bleve.NewDisjunctionQuery([]query.Query{
-				inner_bleve.PrefixQuery(options.Keyword, "title"),
-				inner_bleve.PrefixQuery(options.Keyword, "content"),
-				inner_bleve.PrefixQuery(options.Keyword, "comments"),
-			}...))
+			fuzziness = len(options.Keyword) / fuzzyDenominator
 		}
+
+		queries = append(queries, bleve.NewDisjunctionQuery([]query.Query{
+			inner_bleve.MatchPhraseQuery(options.Keyword, "title", issueIndexerAnalyzer, fuzziness),
+			inner_bleve.MatchPhraseQuery(options.Keyword, "content", issueIndexerAnalyzer, fuzziness),
+			inner_bleve.MatchPhraseQuery(options.Keyword, "comments", issueIndexerAnalyzer, fuzziness),
+		}...))
 	}
 
 	if len(options.RepoIDs) > 0 || options.AllPublic {
diff --git a/tests/integration/repo_search_test.go b/tests/integration/repo_search_test.go
index e5ee334ce8..aecf75025d 100644
--- a/tests/integration/repo_search_test.go
+++ b/tests/integration/repo_search_test.go
@@ -46,7 +46,7 @@ func testSearchRepo(t *testing.T, useExternalIndexer bool) {
 
 	if useExternalIndexer {
 		gitReference = "/commit/"
-		executeIndexer(t, repo, code_indexer.UpdateRepoIndexer)
+		code_indexer.UpdateRepoIndexer(repo)
 	}
 
 	testSearch(t, "/user2/repo1/search?q=Description&page=1", gitReference, []string{"README.md"})
@@ -58,12 +58,14 @@ func testSearchRepo(t *testing.T, useExternalIndexer bool) {
 		repo, err = repo_model.GetRepositoryByOwnerAndName(db.DefaultContext, "user2", "glob")
 		assert.NoError(t, err)
 
-		executeIndexer(t, repo, code_indexer.UpdateRepoIndexer)
+		code_indexer.UpdateRepoIndexer(repo)
 
 		testSearch(t, "/user2/glob/search?q=loren&page=1", gitReference, []string{"a.txt"})
-		testSearch(t, "/user2/glob/search?q=file3&page=1", gitReference, []string{"x/b.txt"})
-		testSearch(t, "/user2/glob/search?q=file4&page=1", gitReference, []string{})
-		testSearch(t, "/user2/glob/search?q=file5&page=1", gitReference, []string{})
+		testSearch(t, "/user2/glob/search?q=loren&page=1&t=match", gitReference, []string{"a.txt"})
+		testSearch(t, "/user2/glob/search?q=file3&page=1", gitReference, []string{"x/b.txt", "a.txt"})
+		testSearch(t, "/user2/glob/search?q=file3&page=1&t=match", gitReference, []string{"x/b.txt", "a.txt"})
+		testSearch(t, "/user2/glob/search?q=file4&page=1&t=match", gitReference, []string{"x/b.txt", "a.txt"})
+		testSearch(t, "/user2/glob/search?q=file5&page=1&t=match", gitReference, []string{"x/b.txt", "a.txt"})
 	}
 }
 
@@ -88,7 +90,3 @@ func testSearch(t *testing.T, url, gitRef string, expected []string) {
 
 	checkResultLinks(t, gitRef, doc)
 }
-
-func executeIndexer(t *testing.T, repo *repo_model.Repository, op func(*repo_model.Repository)) {
-	op(repo)
-}

From 9968139b6bf2afc567f82d2d830efcd953b10a90 Mon Sep 17 00:00:00 2001
From: Yarden Shoham <git@yardenshoham.com>
Date: Sat, 23 Mar 2024 20:18:45 +0200
Subject: [PATCH 63/74] Remove jQuery from the issue "go to" button (#30028)

- Switched to plain JavaScript
- Tested the "go to" button functionality and it works as before

![demo](https://github.com/go-gitea/gitea/assets/20454870/76add18f-3294-4117-98b7-a97f576370e2)

Signed-off-by: Yarden Shoham <git@yardenshoham.com>
(cherry picked from commit d9e33959b38d1463f69f6f8807bc50095cf6dbdb)

Conflicts:
	web_src/js/features/common-issue-list.js
	because "Make submit event code work with both jQuery event
	and native event (#29223)" was not cherry-picked. The change
	it introduced in this file is reverted by this commit so it
	does not matter.
---
 web_src/js/features/common-issue-list.js | 33 +++++++++++-------------
 1 file changed, 15 insertions(+), 18 deletions(-)

diff --git a/web_src/js/features/common-issue-list.js b/web_src/js/features/common-issue-list.js
index 317c11219b..0c0f6c563d 100644
--- a/web_src/js/features/common-issue-list.js
+++ b/web_src/js/features/common-issue-list.js
@@ -1,4 +1,3 @@
-import $ from 'jquery';
 import {isElemHidden, onInputDebounce, submitEventSubmitter, toggleElem} from '../utils/dom.js';
 import {GET} from '../modules/fetch.js';
 
@@ -30,42 +29,40 @@ export function parseIssueListQuickGotoLink(repoLink, searchText) {
 }
 
 export function initCommonIssueListQuickGoto() {
-  const $goto = $('#issue-list-quick-goto');
-  if (!$goto.length) return;
+  const goto = document.getElementById('issue-list-quick-goto');
+  if (!goto) return;
 
-  const $form = $goto.closest('form');
-  const $input = $form.find('input[name=q]');
-  const repoLink = $goto.attr('data-repo-link');
+  const form = goto.closest('form');
+  const input = form.querySelector('input[name=q]');
+  const repoLink = goto.getAttribute('data-repo-link');
 
-  $form.on('submit', (e) => {
+  form.addEventListener('submit', (e) => {
     // if there is no goto button, or the form is submitted by non-quick-goto elements, submit the form directly
-    let doQuickGoto = !isElemHidden($goto);
-    const submitter = submitEventSubmitter(e.originalEvent);
-    if (submitter !== $form[0] && submitter !== $input[0] && submitter !== $goto[0]) doQuickGoto = false;
+    let doQuickGoto = !isElemHidden(goto);
+    const submitter = submitEventSubmitter(e);
+    if (submitter !== form && submitter !== input && submitter !== goto) doQuickGoto = false;
     if (!doQuickGoto) return;
 
     // if there is a goto button, use its link
     e.preventDefault();
-    window.location.href = $goto.attr('data-issue-goto-link');
+    window.location.href = goto.getAttribute('data-issue-goto-link');
   });
 
   const onInput = async () => {
-    const searchText = $input.val();
-
+    const searchText = input.value;
     // try to check whether the parsed goto link is valid
     let targetUrl = parseIssueListQuickGotoLink(repoLink, searchText);
     if (targetUrl) {
       const res = await GET(`${targetUrl}/info`);
       if (res.status !== 200) targetUrl = '';
     }
-
     // if the input value has changed, then ignore the result
-    if ($input.val() !== searchText) return;
+    if (input.value !== searchText) return;
 
-    toggleElem($goto, Boolean(targetUrl));
-    $goto.attr('data-issue-goto-link', targetUrl);
+    toggleElem(goto, Boolean(targetUrl));
+    goto.setAttribute('data-issue-goto-link', targetUrl);
   };
 
-  $input.on('input', onInputDebounce(onInput));
+  input.addEventListener('input', onInputDebounce(onInput));
   onInput();
 }

From 76315e0919ece6ce38352c64293ff33e43d6f6df Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Sat, 23 Mar 2024 19:45:11 +0100
Subject: [PATCH 64/74] Migrate font-weight helpers to tailwind (#30027)

Commands ran:

```sh
perl -p -i -e 's#gt-font-light#tw-font-light#g' web_src/js/**/* templates/**/*
perl -p -i -e 's#gt-font-normal#tw-font-normal#g' web_src/js/**/* templates/**/*
perl -p -i -e 's#gt-font-medium#tw-font-medium#g' web_src/js/**/* templates/**/*
perl -p -i -e 's#gt-font-semibold#tw-font-semibold#g' web_src/js/**/* templates/**/*
perl -p -i -e 's#gt-font-bold#tw-font-bold#g' web_src/js/**/* templates/**/*
```

(cherry picked from commit fabe01478ab449cc4870b7e2a9a1db3911bb14bd)

Conflicts:
	templates/repo/header.tmpl
	prefered Forgejo file & re-ran the commands to ensure the
	migration is done
---
 tailwind.config.js                              | 7 +++++++
 templates/repo/diff/box.tmpl                    | 2 +-
 templates/repo/diff/comments.tmpl               | 2 +-
 templates/repo/header.tmpl                      | 2 +-
 templates/repo/issue/view_content.tmpl          | 2 +-
 templates/repo/issue/view_content/comments.tmpl | 4 ++--
 templates/repo/pulls/tab_menu.tmpl              | 2 +-
 templates/repo/sub_menu.tmpl                    | 2 +-
 templates/shared/user/authorlink.tmpl           | 2 +-
 templates/user/settings/account.tmpl            | 2 +-
 web_src/css/helpers.css                         | 6 ------
 web_src/js/components/DiffFileList.vue          | 2 +-
 12 files changed, 18 insertions(+), 17 deletions(-)

diff --git a/tailwind.config.js b/tailwind.config.js
index e2e8f23656..01fc9ee24c 100644
--- a/tailwind.config.js
+++ b/tailwind.config.js
@@ -66,5 +66,12 @@ export default {
       '3xl': '24px',
       'full': 'var(--border-radius-circle)', // 50%
     },
+    fontWeight: {
+      light: 'var(--font-weight-light)',
+      normal: 'var(--font-weight-normal)',
+      medium: 'var(--font-weight-medium)',
+      semibold: 'var(--font-weight-semibold)',
+      bold: 'var(--font-weight-bold)',
+    },
   },
 };
diff --git a/templates/repo/diff/box.tmpl b/templates/repo/diff/box.tmpl
index 34194ebbdc..c1d0c4fe8e 100644
--- a/templates/repo/diff/box.tmpl
+++ b/templates/repo/diff/box.tmpl
@@ -119,7 +119,7 @@
 										{{svg "octicon-chevron-down" 18}}
 									{{end}}
 								</button>
-								<div class="gt-font-semibold tw-flex tw-items-center gt-mono">
+								<div class="tw-font-semibold tw-flex tw-items-center gt-mono">
 									{{if $file.IsBin}}
 										<span class="gt-ml-1 gt-mr-3">
 											{{ctx.Locale.Tr "repo.diff.bin"}}
diff --git a/templates/repo/diff/comments.tmpl b/templates/repo/diff/comments.tmpl
index b561f094c5..bcf91f74e8 100644
--- a/templates/repo/diff/comments.tmpl
+++ b/templates/repo/diff/comments.tmpl
@@ -11,7 +11,7 @@
 		<div class="ui top attached header comment-header tw-flex tw-items-center tw-justify-between">
 			<div class="comment-header-left tw-flex tw-items-center">
 				{{if .OriginalAuthor}}
-					<span class="text black gt-font-semibold gt-mr-2">
+					<span class="text black tw-font-semibold gt-mr-2">
 						{{svg (MigrationIcon $.root.Repository.GetOriginalURLHostname)}}
 						{{.OriginalAuthor}}
 					</span>
diff --git a/templates/repo/header.tmpl b/templates/repo/header.tmpl
index b6f5ed4996..a5bfb5f25b 100644
--- a/templates/repo/header.tmpl
+++ b/templates/repo/header.tmpl
@@ -6,7 +6,7 @@
 				<div class="flex-item-leading">{{template "repo/icon" .}}</div>
 				<div class="flex-item-main">
 					<div class="flex-item-title gt-font-18">
-						<a class="muted gt-font-normal" href="{{.Owner.HomeLink}}">{{.Owner.Name}}</a>/
+						<a class="muted tw-font-normal" href="{{.Owner.HomeLink}}">{{.Owner.Name}}</a>/
 						<a class="muted" href="{{$.RepoLink}}">{{.Name}}</a></div>
 				</div>
 				<div class="flex-item-trailing">
diff --git a/templates/repo/issue/view_content.tmpl b/templates/repo/issue/view_content.tmpl
index d885b47908..c885f2e93a 100644
--- a/templates/repo/issue/view_content.tmpl
+++ b/templates/repo/issue/view_content.tmpl
@@ -23,7 +23,7 @@
 					<div class="ui top attached header comment-header tw-flex tw-items-center tw-justify-between" role="heading" aria-level="3">
 						<div class="comment-header-left tw-flex tw-items-center">
 							{{if .Issue.OriginalAuthor}}
-								<span class="text black gt-font-semibold">
+								<span class="text black tw-font-semibold">
 									{{svg (MigrationIcon .Repository.GetOriginalURLHostname)}}
 									{{.Issue.OriginalAuthor}}
 								</span>
diff --git a/templates/repo/issue/view_content/comments.tmpl b/templates/repo/issue/view_content/comments.tmpl
index a6d0b801f0..a48ae1947b 100644
--- a/templates/repo/issue/view_content/comments.tmpl
+++ b/templates/repo/issue/view_content/comments.tmpl
@@ -28,7 +28,7 @@
 					<div class="ui top attached header comment-header tw-flex tw-items-center tw-justify-between" role="heading" aria-level="3">
 						<div class="comment-header-left tw-flex tw-items-center">
 							{{if .OriginalAuthor}}
-								<span class="text black gt-font-semibold gt-mr-2">
+								<span class="text black tw-font-semibold gt-mr-2">
 									{{svg (MigrationIcon $.Repository.GetOriginalURLHostname)}}
 									{{.OriginalAuthor}}
 								</span>
@@ -412,7 +412,7 @@
 								{{end}}
 								<span class="text grey muted-links">
 									{{if .OriginalAuthor}}
-										<span class="text black gt-font-semibold">
+										<span class="text black tw-font-semibold">
 											{{svg (MigrationIcon $.Repository.GetOriginalURLHostname)}}
 											{{.OriginalAuthor}}
 										</span>
diff --git a/templates/repo/pulls/tab_menu.tmpl b/templates/repo/pulls/tab_menu.tmpl
index 2a653c7c69..7f4c460b8e 100644
--- a/templates/repo/pulls/tab_menu.tmpl
+++ b/templates/repo/pulls/tab_menu.tmpl
@@ -15,7 +15,7 @@
 			{{ctx.Locale.Tr "repo.pulls.tab_files"}}
 			<span class="ui small label">{{if .NumFiles}}{{.NumFiles}}{{else}}-{{end}}</span>
 		</a>
-		<span class="item tw-ml-auto gt-pr-0 gt-font-bold tw-flex tw-items-center gt-gap-3">
+		<span class="item tw-ml-auto gt-pr-0 tw-font-bold tw-flex tw-items-center gt-gap-3">
 			<span><span class="text green">{{if .Diff.TotalAddition}}+{{.Diff.TotalAddition}}{{end}}</span> <span class="text red">{{if .Diff.TotalDeletion}}-{{.Diff.TotalDeletion}}{{end}}</span></span>
 			<span class="diff-stats-bar">
 				<div class="diff-stats-add-bar" style="width: {{Eval 100 "*" .Diff.TotalAddition "/" "(" .Diff.TotalAddition "+" .Diff.TotalDeletion "+" 0.0 ")"}}%"></div>
diff --git a/templates/repo/sub_menu.tmpl b/templates/repo/sub_menu.tmpl
index 8edb0c1516..654a65fa5c 100644
--- a/templates/repo/sub_menu.tmpl
+++ b/templates/repo/sub_menu.tmpl
@@ -25,7 +25,7 @@
 		{{range .LanguageStats}}
 		<div class="item">
 			<i class="color-icon" style="background-color: {{.Color}}"></i>
-			<span class="gt-font-semibold">
+			<span class="tw-font-semibold">
 				{{if eq .Language "other"}}
 					{{ctx.Locale.Tr "repo.language_other"}}
 				{{else}}
diff --git a/templates/shared/user/authorlink.tmpl b/templates/shared/user/authorlink.tmpl
index 64ccc62cd0..4d8ad736be 100644
--- a/templates/shared/user/authorlink.tmpl
+++ b/templates/shared/user/authorlink.tmpl
@@ -1 +1 @@
-<a class="author text black gt-font-semibold muted"{{if gt .ID 0}} href="{{.HomeLink}}"{{end}}>{{.GetDisplayName}}</a>{{if .IsBot}}<span class="ui basic label gt-p-2">bot</span>{{end}}
+<a class="author text black tw-font-semibold muted"{{if gt .ID 0}} href="{{.HomeLink}}"{{end}}>{{.GetDisplayName}}</a>{{if .IsBot}}<span class="ui basic label gt-p-2">bot</span>{{end}}
diff --git a/templates/user/settings/account.tmpl b/templates/user/settings/account.tmpl
index 6b3d3957cb..24a7d236fe 100644
--- a/templates/user/settings/account.tmpl
+++ b/templates/user/settings/account.tmpl
@@ -136,7 +136,7 @@
 			<div class="ui red message">
 				<p class="text left">{{svg "octicon-alert"}} {{ctx.Locale.Tr "settings.delete_prompt"}}</p>
 				{{if .UserDeleteWithComments}}
-				<p class="text left gt-font-semibold">{{ctx.Locale.Tr "settings.delete_with_all_comments" .UserDeleteWithCommentsMaxTime}}</p>
+				<p class="text left tw-font-semibold">{{ctx.Locale.Tr "settings.delete_with_all_comments" .UserDeleteWithCommentsMaxTime}}</p>
 				{{end}}
 			</div>
 			<form class="ui form ignore-dirty" id="delete-form" action="{{AppSubUrl}}/user/settings/account/delete" method="post">
diff --git a/web_src/css/helpers.css b/web_src/css/helpers.css
index 7a328aeb20..346184f04b 100644
--- a/web_src/css/helpers.css
+++ b/web_src/css/helpers.css
@@ -36,12 +36,6 @@ Gitea's private styles use `g-` prefix.
   text-overflow: ellipsis;
 }
 
-.gt-font-light { font-weight: var(--font-weight-light) !important; }
-.gt-font-normal { font-weight: var(--font-weight-normal) !important; }
-.gt-font-medium { font-weight: var(--font-weight-medium) !important; }
-.gt-font-semibold { font-weight: var(--font-weight-semibold) !important; }
-.gt-font-bold { font-weight: var(--font-weight-bold) !important; }
-
 .interact-fg { color: inherit !important; }
 .interact-fg:hover { color: var(--color-primary) !important; }
 .interact-fg:active { color: var(--color-primary-active) !important; }
diff --git a/web_src/js/components/DiffFileList.vue b/web_src/js/components/DiffFileList.vue
index 3a0e287808..66fe49c50b 100644
--- a/web_src/js/components/DiffFileList.vue
+++ b/web_src/js/components/DiffFileList.vue
@@ -38,7 +38,7 @@ export default {
 <template>
   <ol class="diff-stats gt-m-0" ref="root" v-if="store.fileListIsVisible">
     <li v-for="file in store.files" :key="file.NameHash">
-      <div class="gt-font-semibold tw-flex tw-items-center pull-right">
+      <div class="tw-font-semibold tw-flex tw-items-center pull-right">
         <span v-if="file.IsBin" class="gt-ml-1 gt-mr-3">{{ store.binaryFileMessage }}</span>
         {{ file.IsBin ? '' : file.Addition + file.Deletion }}
         <span v-if="!file.IsBin" class="diff-stats-bar gt-mx-3" :data-tooltip-content="store.statisticsMessage.replace('%d', (file.Addition + file.Deletion)).replace('%d', file.Addition).replace('%d', file.Deletion)">

From eaac4af245a997a442ac0b506f8d027db67ee14e Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Sat, 23 Mar 2024 21:11:15 +0100
Subject: [PATCH 65/74] Replace all simple inline styles with tailwind (#30032)

These should be all simple inline styles that were left in the
templates.

Co-authored-by: Giteabot <teabot@gitea.io>
(cherry picked from commit 0bef9a2775af0e27a0754207fc87537b96c2792e)
---
 templates/admin/notice.tmpl                                   | 2 +-
 templates/devtest/flex-list.tmpl                              | 2 +-
 templates/devtest/tmplerr.tmpl                                | 4 ++--
 templates/repo/diff/box.tmpl                                  | 2 +-
 templates/repo/issue/view_content/reference_issue_dialog.tmpl | 2 +-
 templates/repo/settings/deploy_keys.tmpl                      | 2 +-
 templates/repo/settings/options.tmpl                          | 4 ++--
 templates/user/auth/webauthn.tmpl                             | 2 +-
 web_src/css/review.css                                        | 4 ++++
 9 files changed, 14 insertions(+), 10 deletions(-)

diff --git a/templates/admin/notice.tmpl b/templates/admin/notice.tmpl
index c788ebc602..5ea003e5ec 100644
--- a/templates/admin/notice.tmpl
+++ b/templates/admin/notice.tmpl
@@ -20,7 +20,7 @@
 						<td><div class="ui checkbox tw-flex" data-id="{{.ID}}"><input type="checkbox"></div></td>
 						<td>{{.ID}}</td>
 						<td>{{ctx.Locale.Tr .TrStr}}</td>
-						<td class="view-detail auto-ellipsis" style="width: 80%;"><span class="notice-description">{{.Description}}</span></td>
+						<td class="view-detail auto-ellipsis tw-w-4/5"><span class="notice-description">{{.Description}}</span></td>
 						<td nowrap>{{DateTime "short" .CreatedUnix}}</td>
 						<td class="view-detail"><a href="#">{{svg "octicon-note" 16}}</a></td>
 					</tr>
diff --git a/templates/devtest/flex-list.tmpl b/templates/devtest/flex-list.tmpl
index 0c7b27cd84..fdb9eb6b39 100644
--- a/templates/devtest/flex-list.tmpl
+++ b/templates/devtest/flex-list.tmpl
@@ -73,7 +73,7 @@
 						</div>
 						<div class="flex-item-trailing">
 							<a class="muted" href="{{$.Link}}">
-								<span class="flex-text-inline"><i class="color-icon gt-mr-3" style="background-color: aqua"></i>Go</span>
+								<span class="flex-text-inline"><i class="color-icon gt-mr-3 tw-bg-blue"></i>Go</span>
 							</a>
 							<a class="text grey flex-text-inline" href="{{$.Link}}">{{svg "octicon-star" 16}}45000</a>
 							<a class="text grey flex-text-inline" href="{{$.Link}}">{{svg "octicon-git-branch" 16}}1234</a>
diff --git a/templates/devtest/tmplerr.tmpl b/templates/devtest/tmplerr.tmpl
index 09cf05fc1f..dd938c895e 100644
--- a/templates/devtest/tmplerr.tmpl
+++ b/templates/devtest/tmplerr.tmpl
@@ -1,10 +1,10 @@
 {{template "base/head" .}}
 <div class="page-content devtest">
 	<div class="tw-flex">
-		<div style="width: 80%; ">
+		<div class="tw-w-4/5">
 			hello hello hello hello hello hello hello hello hello hello
 		</div>
-		<div style="width: 20%;">
+		<div class="tw-w-1/5">
 			{{template "devtest/tmplerr-sub" .}}
 		</div>
 	</div>
diff --git a/templates/repo/diff/box.tmpl b/templates/repo/diff/box.tmpl
index c1d0c4fe8e..6dce6be514 100644
--- a/templates/repo/diff/box.tmpl
+++ b/templates/repo/diff/box.tmpl
@@ -181,7 +181,7 @@
 						<div class="diff-file-body ui attached unstackable table segment" {{if and $file.IsViewed $.IsShowingAllCommits}}data-folded="true"{{end}}>
 							<div id="diff-source-{{$file.NameHash}}" class="file-body file-code unicode-escaped code-diff{{if $.IsSplitStyle}} code-diff-split{{else}} code-diff-unified{{end}}{{if $showFileViewToggle}} gt-hidden{{end}}">
 								{{if or $file.IsIncomplete $file.IsBin}}
-									<div class="diff-file-body binary" style="padding: 5px 10px;">
+									<div class="diff-file-body binary">
 										{{if $file.IsIncomplete}}
 											{{if $file.IsIncompleteLineTooLong}}
 												{{ctx.Locale.Tr "repo.diff.file_suppressed_line_too_long"}}
diff --git a/templates/repo/issue/view_content/reference_issue_dialog.tmpl b/templates/repo/issue/view_content/reference_issue_dialog.tmpl
index b771e08909..5f338f6768 100644
--- a/templates/repo/issue/view_content/reference_issue_dialog.tmpl
+++ b/templates/repo/issue/view_content/reference_issue_dialog.tmpl
@@ -2,7 +2,7 @@
 	<div class="header">
 		{{ctx.Locale.Tr "repo.issues.context.reference_issue"}}
 	</div>
-	<div class="content" style="text-align:left">
+	<div class="content tw-text-left">
 		<form class="ui form form-fetch-action" action="{{printf "%s/issues/new" .Repository.Link}}" method="post">
 			{{.CsrfTokenHtml}}
 			<div class="ui segment content">
diff --git a/templates/repo/settings/deploy_keys.tmpl b/templates/repo/settings/deploy_keys.tmpl
index a79a196825..f66b94c332 100644
--- a/templates/repo/settings/deploy_keys.tmpl
+++ b/templates/repo/settings/deploy_keys.tmpl
@@ -31,7 +31,7 @@
 							<label for="is_writable">
 								{{ctx.Locale.Tr "repo.settings.is_writable"}}
 							</label>
-							<small style="padding-left: 26px;">{{ctx.Locale.Tr "repo.settings.is_writable_info"}}</small>
+							<small class="tw-pl-[26px]">{{ctx.Locale.Tr "repo.settings.is_writable_info"}}</small>
 						</div>
 					</div>
 					<button class="ui primary button">
diff --git a/templates/repo/settings/options.tmpl b/templates/repo/settings/options.tmpl
index a28d430f85..7827f0b63b 100644
--- a/templates/repo/settings/options.tmpl
+++ b/templates/repo/settings/options.tmpl
@@ -110,7 +110,7 @@
 					<table class="ui table">
 						<thead>
 							<tr>
-								<th style="width:40%">{{ctx.Locale.Tr "repo.settings.mirror_settings.mirrored_repository"}}</th>
+								<th class="tw-w-2/5">{{ctx.Locale.Tr "repo.settings.mirror_settings.mirrored_repository"}}</th>
 								<th>{{ctx.Locale.Tr "repo.settings.mirror_settings.direction"}}</th>
 								<th>{{ctx.Locale.Tr "repo.settings.mirror_settings.last_update"}}</th>
 								<th></th>
@@ -207,7 +207,7 @@
 					<table class="ui table">
 						<thead>
 							<tr>
-								<th style="width:40%">{{ctx.Locale.Tr "repo.settings.mirror_settings.pushed_repository"}}</th>
+								<th class="tw-w-2/5">{{ctx.Locale.Tr "repo.settings.mirror_settings.pushed_repository"}}</th>
 								<th>{{ctx.Locale.Tr "repo.settings.mirror_settings.direction"}}</th>
 								<th>{{ctx.Locale.Tr "repo.settings.mirror_settings.last_update"}}</th>
 								<th></th>
diff --git a/templates/user/auth/webauthn.tmpl b/templates/user/auth/webauthn.tmpl
index 7c543cb16d..867248718d 100644
--- a/templates/user/auth/webauthn.tmpl
+++ b/templates/user/auth/webauthn.tmpl
@@ -11,7 +11,7 @@
 				<p>{{ctx.Locale.Tr "webauthn_sign_in"}}</p>
 			</div>
 			<div class="ui attached segment tw-flex tw-items-center tw-justify-center gt-gap-2 gt-py-3">
-				<div class="is-loading" style="width: 40px; height: 40px"></div>
+				<div class="is-loading tw-w-[40px] tw-h-[40px]"></div>
 				{{ctx.Locale.Tr "webauthn_press_button"}}
 			</div>
 			{{if .HasTwoFactor}}
diff --git a/web_src/css/review.css b/web_src/css/review.css
index 62da25b209..d5b675a650 100644
--- a/web_src/css/review.css
+++ b/web_src/css/review.css
@@ -198,6 +198,10 @@
   margin: 0 0 0 3em;
 }
 
+.diff-file-body.binary {
+  padding: 5px 10px;
+}
+
 .file-comment {
   color: var(--color-text);
 }

From 66feafb3de2313c002c8041c817fb82dae7b95b2 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Sat, 23 Mar 2024 22:22:15 +0100
Subject: [PATCH 66/74] Migrate font-size helpers to tailwind (#30029)

Migrate `gt-font-*` to `tw-text-*` All tailwind-original class names are
also available and render like they would with 16px root font size.

We currently have root font size at 14px, but I would like to eventually
migrate us to 16px so that the tailwind docs apply to us unchangend and
because 16px is the recommended root font size for web pages in general.
Also the number 16 is much better dividable than 14 so will result in
more integers.

(cherry picked from commit 75e2e5c736687ae1897cf760a432b572feed56f5)

Conflicts:
	templates/repo/header.tmpl
	trivial context conflict
---
 tailwind.config.js                             | 18 ++++++++++++++++++
 templates/org/header.tmpl                      |  2 +-
 templates/repo/header.tmpl                     |  2 +-
 templates/repo/home.tmpl                       |  4 ++--
 templates/user/dashboard/feeds.tmpl            |  2 +-
 .../user/notification/notification_div.tmpl    |  4 ++--
 web_src/css/helpers.css                        |  8 --------
 web_src/js/components/RepoContributors.vue     |  2 +-
 8 files changed, 26 insertions(+), 16 deletions(-)

diff --git a/tailwind.config.js b/tailwind.config.js
index 01fc9ee24c..0754ab3631 100644
--- a/tailwind.config.js
+++ b/tailwind.config.js
@@ -73,5 +73,23 @@ export default {
       semibold: 'var(--font-weight-semibold)',
       bold: 'var(--font-weight-bold)',
     },
+    fontSize: { // not using `rem` units because our root is currently 14px
+      'xs': '12px',
+      'sm': '14px',
+      'base': '16px',
+      'lg': '18px',
+      'xl': '20px',
+      '2xl': '24px',
+      '3xl': '30px',
+      '4xl': '36px',
+      '5xl': '48px',
+      '6xl': '60px',
+      '7xl': '72px',
+      '8xl': '96px',
+      '9xl': '128px',
+      ...Object.fromEntries(Array.from({length: 100}, (_, i) => {
+        return [`${i}`, `${i === 0 ? '0' : `${i}px`}`];
+      })),
+    },
   },
 };
diff --git a/templates/org/header.tmpl b/templates/org/header.tmpl
index 204ba7e3c1..81373aa75c 100644
--- a/templates/org/header.tmpl
+++ b/templates/org/header.tmpl
@@ -7,7 +7,7 @@
 				{{if .Org.Visibility.IsLimited}}<span class="ui large basic horizontal label">{{ctx.Locale.Tr "org.settings.visibility.limited_shortname"}}</span>{{end}}
 				{{if .Org.Visibility.IsPrivate}}<span class="ui large basic horizontal label">{{ctx.Locale.Tr "org.settings.visibility.private_shortname"}}</span>{{end}}
 			</span>
-			<span class="tw-flex tw-items-center gt-gap-2 tw-ml-auto gt-font-16 tw-whitespace-nowrap">
+			<span class="tw-flex tw-items-center gt-gap-2 tw-ml-auto tw-text-16 tw-whitespace-nowrap">
 				{{if .EnableFeed}}
 					<a class="ui basic label button gt-mr-0" href="{{.Org.HomeLink}}.rss" data-tooltip-content="{{ctx.Locale.Tr "rss_feed"}}">
 						{{svg "octicon-rss" 24}}
diff --git a/templates/repo/header.tmpl b/templates/repo/header.tmpl
index a5bfb5f25b..53ce17dbcc 100644
--- a/templates/repo/header.tmpl
+++ b/templates/repo/header.tmpl
@@ -5,7 +5,7 @@
 			<div class="flex-item tw-items-center">
 				<div class="flex-item-leading">{{template "repo/icon" .}}</div>
 				<div class="flex-item-main">
-					<div class="flex-item-title gt-font-18">
+					<div class="flex-item-title tw-text-18">
 						<a class="muted tw-font-normal" href="{{.Owner.HomeLink}}">{{.Owner.Name}}</a>/
 						<a class="muted" href="{{$.RepoLink}}">{{.Name}}</a></div>
 				</div>
diff --git a/templates/repo/home.tmpl b/templates/repo/home.tmpl
index 60be13b132..503e97a928 100644
--- a/templates/repo/home.tmpl
+++ b/templates/repo/home.tmpl
@@ -6,7 +6,7 @@
 		{{template "repo/code/recently_pushed_new_branches" .}}
 		{{if and (not .HideRepoInfo) (not .IsBlame)}}
 		<div class="ui repo-description gt-word-break">
-			<div id="repo-desc" class="gt-font-16">
+			<div id="repo-desc" class="tw-text-16">
 				{{$description := .Repository.DescriptionHTML $.Context}}
 				{{if $description}}<span class="description">{{$description | RenderCodeBlock}}</span>{{else if .IsRepositoryAdmin}}<span class="no-description text-italic">{{ctx.Locale.Tr "repo.no_desc"}}</span>{{end}}
 				<a class="link" href="{{.Repository.Website}}">{{.Repository.Website}}</a>
@@ -29,7 +29,7 @@
 		</div>
 		<div class="tw-flex tw-items-center tw-flex-wrap gt-gap-2" id="repo-topics">
 			{{range .Topics}}<a class="ui repo-topic large label topic gt-m-0" href="{{AppSubUrl}}/explore/repos?q={{.Name}}&topic=1">{{.Name}}</a>{{end}}
-			{{if and .Permission.IsAdmin (not .Repository.IsArchived)}}<button id="manage_topic" class="btn interact-fg gt-font-12">{{ctx.Locale.Tr "repo.topic.manage_topics"}}</button>{{end}}
+			{{if and .Permission.IsAdmin (not .Repository.IsArchived)}}<button id="manage_topic" class="btn interact-fg tw-text-12">{{ctx.Locale.Tr "repo.topic.manage_topics"}}</button>{{end}}
 		</div>
 		{{end}}
 		{{if and .Permission.IsAdmin (not .Repository.IsArchived)}}
diff --git a/templates/user/dashboard/feeds.tmpl b/templates/user/dashboard/feeds.tmpl
index 382b0d4542..c58d7e22d7 100644
--- a/templates/user/dashboard/feeds.tmpl
+++ b/templates/user/dashboard/feeds.tmpl
@@ -107,7 +107,7 @@
 					<a href="{{.GetCommentLink ctx}}" class="text truncate issue title">{{(.GetIssueTitle ctx) | RenderEmoji $.Context | RenderCodeBlock}}</a>
 					{{$comment := index .GetIssueInfos 1}}
 					{{if $comment}}
-						<div class="markup gt-font-14">{{RenderMarkdownToHtml ctx $comment}}</div>
+						<div class="markup tw-text-14">{{RenderMarkdownToHtml ctx $comment}}</div>
 					{{end}}
 				{{else if .GetOpType.InActions "merge_pull_request"}}
 					<div class="flex-item-body text black">{{index .GetIssueInfos 1}}</div>
diff --git a/templates/user/notification/notification_div.tmpl b/templates/user/notification/notification_div.tmpl
index 371da129ce..da5a920fd1 100644
--- a/templates/user/notification/notification_div.tmpl
+++ b/templates/user/notification/notification_div.tmpl
@@ -44,13 +44,13 @@
 								{{end}}
 							</div>
 							<a class="notifications-link tw-flex tw-flex-1 tw-flex-col silenced" href="{{.Link ctx}}">
-								<div class="notifications-top-row gt-font-13">
+								<div class="notifications-top-row tw-text-13">
 									{{.Repository.FullName}} {{if .Issue}}<span class="text light-3">#{{.Issue.Index}}</span>{{end}}
 									{{if eq .Status 3}}
 										{{svg "octicon-pin" 13 "text blue gt-mt-1 gt-ml-2"}}
 									{{end}}
 								</div>
-								<div class="notifications-bottom-row gt-font-16 gt-py-1">
+								<div class="notifications-bottom-row tw-text-16 gt-py-1">
 									<span class="issue-title">
 										{{if .Issue}}
 											{{.Issue.Title | RenderEmoji $.Context | RenderCodeBlock}}
diff --git a/web_src/css/helpers.css b/web_src/css/helpers.css
index 346184f04b..848e17ebe7 100644
--- a/web_src/css/helpers.css
+++ b/web_src/css/helpers.css
@@ -163,14 +163,6 @@ Gitea's private styles use `g-` prefix.
 .gt-gap-y-4 { row-gap: 1rem !important; }
 .gt-gap-y-5 { row-gap: 2rem !important; }
 
-.gt-font-12 { font-size: 12px !important }
-.gt-font-13 { font-size: 13px !important }
-.gt-font-14 { font-size: 14px !important }
-.gt-font-15 { font-size: 15px !important }
-.gt-font-16 { font-size: 16px !important }
-.gt-font-17 { font-size: 17px !important }
-.gt-font-18 { font-size: 18px !important }
-
 /*
 gt-hidden must win all other "display: xxx !important" classes to get the chance to "hide" an element.
 do not use:
diff --git a/web_src/js/components/RepoContributors.vue b/web_src/js/components/RepoContributors.vue
index adaf7f28f1..6de68a4aec 100644
--- a/web_src/js/components/RepoContributors.vue
+++ b/web_src/js/components/RepoContributors.vue
@@ -384,7 +384,7 @@ export default {
             <h4 v-else class="contributor-name">
               {{ contributor.name }}
             </h4>
-            <p class="gt-font-12 tw-flex gt-gap-2">
+            <p class="tw-text-12 tw-flex 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">

From 0dc6919e2dfa9d4fee29c77980e3a873dd5a6431 Mon Sep 17 00:00:00 2001
From: Yarden Shoham <git@yardenshoham.com>
Date: Sat, 23 Mar 2024 23:31:19 +0200
Subject: [PATCH 67/74] Remove jQuery `.attr` from the common global functions
 (#30023)

- Switched from jQuery `.attr` to plain javascript `getAttribute`
- Tested the show/hide modal buttons, they work as before

Signed-off-by: Yarden Shoham <git@yardenshoham.com>
(cherry picked from commit 900dd79d8adaf2569df0f1346b6e6e91ed4b5ad3)
---
 web_src/js/features/common-global.js | 13 ++++++-------
 1 file changed, 6 insertions(+), 7 deletions(-)

diff --git a/web_src/js/features/common-global.js b/web_src/js/features/common-global.js
index 2469361c6e..e27935a86e 100644
--- a/web_src/js/features/common-global.js
+++ b/web_src/js/features/common-global.js
@@ -301,8 +301,8 @@ export function initGlobalLinkActions() {
     const $this = $(this);
     const dataArray = $this.data();
     let filter = '';
-    if ($this.attr('data-modal-id')) {
-      filter += `#${$this.attr('data-modal-id')}`;
+    if (this.getAttribute('data-modal-id')) {
+      filter += `#${this.getAttribute('data-modal-id')}`;
     }
 
     const $dialog = $(`.delete.modal${filter}`);
@@ -352,8 +352,7 @@ function initGlobalShowModal() {
   // If there is a ".{attr}" part like "data-modal-form.action", then the form's "action" attribute will be set.
   $('.show-modal').on('click', function (e) {
     e.preventDefault();
-    const $el = $(this);
-    const modalSelector = $el.attr('data-modal');
+    const modalSelector = this.getAttribute('data-modal');
     const $modal = $(modalSelector);
     if (!$modal.length) {
       throw new Error('no modal for this action');
@@ -406,7 +405,7 @@ export function initGlobalButtons() {
     // a '.show-panel' element can show a panel, by `data-panel="selector"`
     // if it has "toggle" class, it toggles the panel
     e.preventDefault();
-    const sel = $(this).attr('data-panel');
+    const sel = this.getAttribute('data-panel');
     if (this.classList.contains('toggle')) {
       toggleElem(sel);
     } else {
@@ -417,12 +416,12 @@ export function initGlobalButtons() {
   $('.hide-panel').on('click', function (e) {
     // a `.hide-panel` element can hide a panel, by `data-panel="selector"` or `data-panel-closest="selector"`
     e.preventDefault();
-    let sel = $(this).attr('data-panel');
+    let sel = this.getAttribute('data-panel');
     if (sel) {
       hideElem($(sel));
       return;
     }
-    sel = $(this).attr('data-panel-closest');
+    sel = this.getAttribute('data-panel-closest');
     if (sel) {
       hideElem($(this).closest(sel));
       return;

From a9a2f5200b6d6ac0626a45e0098b6b06bafffd03 Mon Sep 17 00:00:00 2001
From: DrMaxNix <mail@drmaxnix.de>
Date: Sat, 23 Mar 2024 22:59:58 +0100
Subject: [PATCH 68/74] Respect DEFAULT_ORG_MEMBER_VISIBLE setting when adding
 creator to org (#30013)

This PR adds `setting.Service.DefaultOrgMemberVisible` value to dataset
of user when the initial org creator is being added to the created org.

Fixes #30012.

(cherry picked from commit e3e08dcc5184cdbdac5023fabaafba123a995c3e)
---
 models/organization/org.go | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/models/organization/org.go b/models/organization/org.go
index b4919defb4..47230bdc36 100644
--- a/models/organization/org.go
+++ b/models/organization/org.go
@@ -319,8 +319,9 @@ func CreateOrganization(ctx context.Context, org *Organization, owner *user_mode
 
 	// Add initial creator to organization and owner team.
 	if err = db.Insert(ctx, &OrgUser{
-		UID:   owner.ID,
-		OrgID: org.ID,
+		UID:      owner.ID,
+		OrgID:    org.ID,
+		IsPublic: setting.Service.DefaultOrgMemberVisible,
 	}); err != nil {
 		return fmt.Errorf("insert org-user relation: %w", err)
 	}

From 1ac099cf0f3077a7147b500ec7ecc837e0652308 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Sun, 24 Mar 2024 03:01:57 +0100
Subject: [PATCH 69/74] Remove remaining jQuery .css code (#30015)

The linter missed these because they were set on a object. Tested and I
also renamed those properties to add `$` indicating a jQuery selection.

---------

Co-authored-by: delvh <dev.lh@web.de>
Co-authored-by: Giteabot <teabot@gitea.io>
(cherry picked from commit e4a481e0ca7e952efdf7a96ccd33f80f527d8c86)
---
 web_src/js/features/imagediff.js | 89 ++++++++++++++++----------------
 1 file changed, 45 insertions(+), 44 deletions(-)

diff --git a/web_src/js/features/imagediff.js b/web_src/js/features/imagediff.js
index d567632f5f..53bf2109ba 100644
--- a/web_src/js/features/imagediff.js
+++ b/web_src/js/features/imagediff.js
@@ -54,8 +54,8 @@ export function initImageDiff() {
     };
 
     return {
-      image1: $(image1),
-      image2: $(image2),
+      $image1: $(image1),
+      $image2: $(image2),
       size1,
       size2,
       max,
@@ -124,18 +124,18 @@ export function initImageDiff() {
         factor = (diffContainerWidth - 24) / 2 / sizes.max.width;
       }
 
-      const widthChanged = sizes.image1.length !== 0 && sizes.image2.length !== 0 && sizes.image1[0].naturalWidth !== sizes.image2[0].naturalWidth;
-      const heightChanged = sizes.image1.length !== 0 && sizes.image2.length !== 0 && sizes.image1[0].naturalHeight !== sizes.image2[0].naturalHeight;
-      if (sizes.image1.length !== 0) {
-        $container.find('.bounds-info-after .bounds-info-width').text(`${sizes.image1[0].naturalWidth}px`).addClass(widthChanged ? 'green' : '');
-        $container.find('.bounds-info-after .bounds-info-height').text(`${sizes.image1[0].naturalHeight}px`).addClass(heightChanged ? 'green' : '');
+      const widthChanged = sizes.$image1.length !== 0 && sizes.$image2.length !== 0 && sizes.$image1[0].naturalWidth !== sizes.$image2[0].naturalWidth;
+      const heightChanged = sizes.$image1.length !== 0 && sizes.$image2.length !== 0 && sizes.$image1[0].naturalHeight !== sizes.$image2[0].naturalHeight;
+      if (sizes.$image1.length !== 0) {
+        $container.find('.bounds-info-after .bounds-info-width').text(`${sizes.$image1[0].naturalWidth}px`).addClass(widthChanged ? 'green' : '');
+        $container.find('.bounds-info-after .bounds-info-height').text(`${sizes.$image1[0].naturalHeight}px`).addClass(heightChanged ? 'green' : '');
       }
-      if (sizes.image2.length !== 0) {
-        $container.find('.bounds-info-before .bounds-info-width').text(`${sizes.image2[0].naturalWidth}px`).addClass(widthChanged ? 'red' : '');
-        $container.find('.bounds-info-before .bounds-info-height').text(`${sizes.image2[0].naturalHeight}px`).addClass(heightChanged ? 'red' : '');
+      if (sizes.$image2.length !== 0) {
+        $container.find('.bounds-info-before .bounds-info-width').text(`${sizes.$image2[0].naturalWidth}px`).addClass(widthChanged ? 'red' : '');
+        $container.find('.bounds-info-before .bounds-info-height').text(`${sizes.$image2[0].naturalHeight}px`).addClass(heightChanged ? 'red' : '');
       }
 
-      const image1 = sizes.image1[0];
+      const image1 = sizes.$image1[0];
       if (image1) {
         const container = image1.parentNode;
         image1.style.width = `${sizes.size1.width * factor}px`;
@@ -145,7 +145,7 @@ export function initImageDiff() {
         container.style.height = `${sizes.size1.height * factor + 2}px`;
       }
 
-      const image2 = sizes.image2[0];
+      const image2 = sizes.$image2[0];
       if (image2) {
         const container = image2.parentNode;
         image2.style.width = `${sizes.size2.width * factor}px`;
@@ -162,7 +162,7 @@ export function initImageDiff() {
         factor = (diffContainerWidth - 12) / sizes.max.width;
       }
 
-      const image1 = sizes.image1[0];
+      const image1 = sizes.$image1[0];
       if (image1) {
         const container = image1.parentNode;
         const swipeFrame = container.parentNode;
@@ -175,7 +175,7 @@ export function initImageDiff() {
         swipeFrame.style.width = `${sizes.max.width * factor + 2}px`;
       }
 
-      const image2 = sizes.image2[0];
+      const image2 = sizes.$image2[0];
       if (image2) {
         const container = image2.parentNode;
         const swipeFrame = container.parentNode;
@@ -222,38 +222,39 @@ export function initImageDiff() {
         factor = (diffContainerWidth - 12) / sizes.max.width;
       }
 
-      sizes.image1.css({
-        width: sizes.size1.width * factor,
-        height: sizes.size1.height * factor,
-      });
-      sizes.image2.css({
-        width: sizes.size2.width * factor,
-        height: sizes.size2.height * factor,
-      });
-      sizes.image1.parent().css({
-        margin: `${sizes.ratio[1] * factor}px ${sizes.ratio[0] * factor}px`,
-        width: sizes.size1.width * factor + 2,
-        height: sizes.size1.height * factor + 2,
-      });
-      sizes.image2.parent().css({
-        margin: `${sizes.ratio[3] * factor}px ${sizes.ratio[2] * factor}px`,
-        width: sizes.size2.width * factor + 2,
-        height: sizes.size2.height * factor + 2,
-      });
+      const image1 = sizes.$image1[0];
+      if (image1) {
+        const container = image1.parentNode;
+        image1.style.width = `${sizes.size1.width * factor}px`;
+        image1.style.height = `${sizes.size1.height * factor}px`;
+        container.style.margin = `${sizes.ratio[1] * factor}px ${sizes.ratio[0] * factor}px`;
+        container.style.width = `${sizes.size1.width * factor + 2}px`;
+        container.style.height = `${sizes.size1.height * factor + 2}px`;
+      }
 
-      // some inner elements are `position: absolute`, so the container's height must be large enough
-      // the "css(width, height)" is somewhat hacky and not easy to understand, it could be improved in the future
-      sizes.image2.parent().parent().css({
-        width: sizes.max.width * factor + 2,
-        height: sizes.max.height * factor + 2,
-      });
+      const image2 = sizes.$image2[0];
+      if (image2) {
+        const container = image2.parentNode;
+        const overlayFrame = container.parentNode;
+        image2.style.width = `${sizes.size2.width * factor}px`;
+        image2.style.height = `${sizes.size2.height * factor}px`;
+        container.style.margin = `${sizes.ratio[3] * factor}px ${sizes.ratio[2] * factor}px`;
+        container.style.width = `${sizes.size2.width * factor + 2}px`;
+        container.style.height = `${sizes.size2.height * factor + 2}px`;
 
-      const $range = $container.find("input[type='range']");
-      const onInput = () => sizes.image1.parent().css({
-        opacity: $range.val() / 100,
-      });
-      $range.on('input', onInput);
-      onInput();
+        // some inner elements are `position: absolute`, so the container's height must be large enough
+        overlayFrame.style.width = `${sizes.max.width * factor + 2}px`;
+        overlayFrame.style.height = `${sizes.max.height * factor + 2}px`;
+      }
+
+      const rangeInput = $container[0].querySelector('input[type="range"]');
+      function updateOpacity() {
+        if (sizes?.$image1?.[0]) {
+          sizes.$image1[0].parentNode.style.opacity = `${rangeInput.value / 100}`;
+        }
+      }
+      rangeInput?.addEventListener('input', updateOpacity);
+      updateOpacity();
     }
   });
 }

From 42c22b886b74ec0f9813d037be8568ef17ccf110 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Sun, 24 Mar 2024 08:14:48 +0100
Subject: [PATCH 70/74] Enable a few stylelint rules (#30038)

No violations but still good to have them.

(cherry picked from commit 9c6e6f4d1b7acf6adc4b201da2dbc51d092c9a17)
---
 .stylelintrc.yaml | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/.stylelintrc.yaml b/.stylelintrc.yaml
index c7725159f1..60cce7dbf7 100644
--- a/.stylelintrc.yaml
+++ b/.stylelintrc.yaml
@@ -30,7 +30,7 @@ rules:
   "@stylistic/block-opening-brace-newline-after": null
   "@stylistic/block-opening-brace-newline-before": null
   "@stylistic/block-opening-brace-space-after": null
-  "@stylistic/block-opening-brace-space-before": null
+  "@stylistic/block-opening-brace-space-before": always
   "@stylistic/color-hex-case": lower
   "@stylistic/declaration-bang-space-after": never
   "@stylistic/declaration-bang-space-before": null
@@ -140,7 +140,7 @@ rules:
   function-disallowed-list: null
   function-linear-gradient-no-nonstandard-direction: true
   function-name-case: lower
-  function-no-unknown: null
+  function-no-unknown: true
   function-url-no-scheme-relative: null
   function-url-quotes: always
   function-url-scheme-allowed-list: null
@@ -168,7 +168,7 @@ rules:
   no-duplicate-selectors: true
   no-empty-source: true
   no-invalid-double-slash-comments: true
-  no-invalid-position-at-import-rule: null
+  no-invalid-position-at-import-rule: [true, ignoreAtRules: [tailwind]]
   no-irregular-whitespace: true
   no-unknown-animations: null
   no-unknown-custom-properties: null
@@ -181,6 +181,7 @@ rules:
   rule-empty-line-before: null
   rule-selector-property-disallowed-list: null
   scale-unlimited/declaration-strict-value: [[/color$/, font-weight], {ignoreValues: /^(inherit|transparent|unset|initial|currentcolor|none)$/, ignoreFunctions: false, disableFix: true, expandShorthand: true}]
+  selector-anb-no-unmatchable: true
   selector-attribute-name-disallowed-list: null
   selector-attribute-operator-allowed-list: null
   selector-attribute-operator-disallowed-list: null

From 8f2b95012d2e23c99c3a8a9579daa4acd8532b19 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Sun, 24 Mar 2024 14:38:10 +0100
Subject: [PATCH 71/74] Dont show expansion for empty actions steps (#29977)

This hides the chevron icon and makes the step header unclickable for
skipped steps because there is no content to expand on those.

Before:

<img width="272" alt="Screenshot 2024-03-21 at 20 06 47"
src="https://github.com/go-gitea/gitea/assets/115237/9bb328d1-6f74-48a9-af19-de9b351e3707">

After:
<img width="295" alt="Screenshot 2024-03-21 at 20 03 07"
src="https://github.com/go-gitea/gitea/assets/115237/72a26e14-5a28-4606-8c3c-184b405872c8">

---------

Co-authored-by: Giteabot <teabot@gitea.io>
(cherry picked from commit 5bd0773741be8f9476be0c76e8733f8c9dd65d19)
---
 web_src/js/components/RepoActionView.vue | 24 +++++++++++++++---------
 1 file changed, 15 insertions(+), 9 deletions(-)

diff --git a/web_src/js/components/RepoActionView.vue b/web_src/js/components/RepoActionView.vue
index a855574c6c..02e1dcd4df 100644
--- a/web_src/js/components/RepoActionView.vue
+++ b/web_src/js/components/RepoActionView.vue
@@ -271,6 +271,10 @@ const sfc = {
       return ['success', 'skipped', 'failure', 'cancelled'].includes(status);
     },
 
+    isExpandable(status) {
+      return ['success', 'running', 'failure', 'cancelled'].includes(status);
+    },
+
     closeDropdown() {
       if (this.menuVisible) this.menuVisible = false;
     },
@@ -468,12 +472,12 @@ export function initRepositoryActionView() {
         </div>
         <div class="job-step-container" ref="steps" v-if="currentJob.steps.length">
           <div class="job-step-section" v-for="(jobStep, i) in currentJob.steps" :key="i">
-            <div class="job-step-summary" @click.stop="toggleStepLogs(i)" :class="currentJobStepsStates[i].expanded ? 'selected' : ''">
+            <div class="job-step-summary" @click.stop="jobStep.status !== 'skipped' && toggleStepLogs(i)" :class="[currentJobStepsStates[i].expanded ? 'selected' : '', isExpandable(jobStep.status) && 'step-expandable']">
               <!-- If the job is done and the job step log is loaded for the first time, show the loading icon
                 currentJobStepsStates[i].cursor === null means the log is loaded for the first time
               -->
               <SvgIcon v-if="isDone(run.status) && currentJobStepsStates[i].expanded && currentJobStepsStates[i].cursor === null" name="octicon-sync" class="gt-mr-3 job-status-rotate"/>
-              <SvgIcon v-else :name="currentJobStepsStates[i].expanded ? 'octicon-chevron-down': 'octicon-chevron-right'" class="gt-mr-3"/>
+              <SvgIcon v-else :name="currentJobStepsStates[i].expanded ? 'octicon-chevron-down': 'octicon-chevron-right'" :class="['gt-mr-3', !isExpandable(jobStep.status) && 'tw-invisible']"/>
               <ActionRunStatus :status="jobStep.status" class="gt-mr-3"/>
 
               <span class="step-summary-msg gt-ellipsis">{{ jobStep.summary }}</span>
@@ -724,13 +728,21 @@ export function initRepositoryActionView() {
 }
 
 .job-step-container .job-step-summary {
-  cursor: pointer;
   padding: 5px 10px;
   display: flex;
   align-items: center;
   border-radius: var(--border-radius);
 }
 
+.job-step-container .job-step-summary.step-expandable {
+  cursor: pointer;
+}
+
+.job-step-container .job-step-summary.step-expandable:hover {
+  color: var(--color-console-fg);
+  background-color: var(--color-console-hover-bg);
+}
+
 .job-step-container .job-step-summary .step-summary-msg {
   flex: 1;
 }
@@ -739,12 +751,6 @@ export function initRepositoryActionView() {
   margin-left: 16px;
 }
 
-.job-step-container .job-step-summary:hover {
-  color: var(--color-console-fg);
-  background-color: var(--color-console-hover-bg);
-
-}
-
 .job-step-container .job-step-summary.selected {
   color: var(--color-console-fg);
   background-color: var(--color-console-active-bg);

From 5940dc17fda731b32bcb71b1347653edb918e6c5 Mon Sep 17 00:00:00 2001
From: silverwind <me@silverwind.io>
Date: Sun, 24 Mar 2024 15:31:35 +0100
Subject: [PATCH 72/74] Migrate `gap` helpers to tailwind (#30034)

Commands ran:

```sh
perl -p -i -e 's#gt-gap-0#tw-gap-0#g'   web_src/js/**/* templates/**/*
perl -p -i -e 's#gt-gap-1#tw-gap-0.5#g' web_src/js/**/* templates/**/*
perl -p -i -e 's#gt-gap-2#tw-gap-1#g'   web_src/js/**/* templates/**/*
perl -p -i -e 's#gt-gap-3#tw-gap-2#g'   web_src/js/**/* templates/**/*
perl -p -i -e 's#gt-gap-4#tw-gap-4#g'   web_src/js/**/* templates/**/*
perl -p -i -e 's#gt-gap-5#tw-gap-8#g'   web_src/js/**/* templates/**/*

perl -p -i -e 's#gt-gap-x-0#tw-gap-x-0#g'   web_src/js/**/* templates/**/*
perl -p -i -e 's#gt-gap-x-1#tw-gap-x-0.5#g' web_src/js/**/* templates/**/*
perl -p -i -e 's#gt-gap-x-2#tw-gap-x-1#g'   web_src/js/**/* templates/**/*
perl -p -i -e 's#gt-gap-x-3#tw-gap-x-2#g'   web_src/js/**/* templates/**/*
perl -p -i -e 's#gt-gap-x-4#tw-gap-x-4#g'   web_src/js/**/* templates/**/*
perl -p -i -e 's#gt-gap-x-5#tw-gap-x-8#g'   web_src/js/**/* templates/**/*

perl -p -i -e 's#gt-gap-y-0#tw-gap-y-0#g'   web_src/js/**/* templates/**/*
perl -p -i -e 's#gt-gap-y-1#tw-gap-y-0.5#g' web_src/js/**/* templates/**/*
perl -p -i -e 's#gt-gap-y-2#tw-gap-y-1#g'   web_src/js/**/* templates/**/*
perl -p -i -e 's#gt-gap-y-3#tw-gap-y-2#g'   web_src/js/**/* templates/**/*
perl -p -i -e 's#gt-gap-y-4#tw-gap-y-4#g'   web_src/js/**/* templates/**/*
perl -p -i -e 's#gt-gap-y-5#tw-gap-y-8#g'   web_src/js/**/* templates/**/*

(cherry picked from commit 90a4f9a49eecc4b672df0c29f5034be25244191c)

Conflicts:
	templates/repo/diff/box.tmpl
	templates/repo/diff/conversation.tmpl
	prefered Forgejo files & re-ran the commands to ensure all are applied
---
 templates/admin/user/list.tmpl                |  2 +-
 templates/org/header.tmpl                     |  2 +-
 templates/org/settings/blocked_users.tmpl     |  2 +-
 templates/org/team/members.tmpl               |  2 +-
 templates/org/team/repositories.tmpl          |  2 +-
 templates/repo/diff/box.tmpl                  |  6 +++---
 templates/repo/diff/conversation.tmpl         |  4 ++--
 templates/repo/home.tmpl                      |  4 ++--
 templates/repo/issue/milestone_issues.tmpl    |  4 ++--
 .../repo/issue/view_content/sidebar.tmpl      |  6 +++---
 templates/repo/pulls/tab_menu.tmpl            |  2 +-
 templates/user/auth/signin_inner.tmpl         |  2 +-
 templates/user/auth/signup_inner.tmpl         |  2 +-
 templates/user/auth/webauthn.tmpl             |  2 +-
 templates/user/dashboard/feeds.tmpl           |  4 ++--
 .../user/notification/notification_div.tmpl   |  4 ++--
 templates/user/settings/account.tmpl          |  2 +-
 web_src/css/helpers.css                       | 21 -------------------
 web_src/js/components/DiffCommitSelector.vue  |  2 +-
 web_src/js/components/RepoContributors.vue    |  2 +-
 20 files changed, 28 insertions(+), 49 deletions(-)

diff --git a/templates/admin/user/list.tmpl b/templates/admin/user/list.tmpl
index dc557e8598..e5d429952f 100644
--- a/templates/admin/user/list.tmpl
+++ b/templates/admin/user/list.tmpl
@@ -103,7 +103,7 @@
 								<td><span>{{ctx.Locale.Tr "admin.users.never_login"}}</span></td>
 							{{end}}
 							<td>
-								<div class="tw-flex gt-gap-3">
+								<div class="tw-flex tw-gap-2">
 									<a href="{{$.Link}}/{{.ID}}" data-tooltip-content="{{ctx.Locale.Tr "admin.users.details"}}">{{svg "octicon-person"}}</a>
 									<a href="{{$.Link}}/{{.ID}}/edit" data-tooltip-content="{{ctx.Locale.Tr "edit"}}">{{svg "octicon-pencil"}}</a>
 								</div>
diff --git a/templates/org/header.tmpl b/templates/org/header.tmpl
index 81373aa75c..6eb7feb33f 100644
--- a/templates/org/header.tmpl
+++ b/templates/org/header.tmpl
@@ -7,7 +7,7 @@
 				{{if .Org.Visibility.IsLimited}}<span class="ui large basic horizontal label">{{ctx.Locale.Tr "org.settings.visibility.limited_shortname"}}</span>{{end}}
 				{{if .Org.Visibility.IsPrivate}}<span class="ui large basic horizontal label">{{ctx.Locale.Tr "org.settings.visibility.private_shortname"}}</span>{{end}}
 			</span>
-			<span class="tw-flex tw-items-center gt-gap-2 tw-ml-auto tw-text-16 tw-whitespace-nowrap">
+			<span class="tw-flex tw-items-center tw-gap-1 tw-ml-auto tw-text-16 tw-whitespace-nowrap">
 				{{if .EnableFeed}}
 					<a class="ui basic label button gt-mr-0" href="{{.Org.HomeLink}}.rss" data-tooltip-content="{{ctx.Locale.Tr "rss_feed"}}">
 						{{svg "octicon-rss" 24}}
diff --git a/templates/org/settings/blocked_users.tmpl b/templates/org/settings/blocked_users.tmpl
index 4701e696ad..f685a1b998 100644
--- a/templates/org/settings/blocked_users.tmpl
+++ b/templates/org/settings/blocked_users.tmpl
@@ -1,7 +1,7 @@
 {{template "org/settings/layout_head" (dict "ctxData" . "pageClass" "organization settings blocked-users")}}
 <div class="org-setting-content">
 	<div class="ui attached segment">
-		<form class="ui form ignore-dirty tw-flex tw-flex-wrap gt-gap-3" action="{{$.Link}}/block" method="post">
+		<form class="ui form ignore-dirty tw-flex tw-flex-wrap tw-gap-2" action="{{$.Link}}/block" method="post">
 			{{.CsrfTokenHtml}}
 			<input type="hidden" name="uid" value="">
 			<div class="ui left">
diff --git a/templates/org/team/members.tmpl b/templates/org/team/members.tmpl
index 65430cbda3..aa358841da 100644
--- a/templates/org/team/members.tmpl
+++ b/templates/org/team/members.tmpl
@@ -9,7 +9,7 @@
 				{{template "org/team/navbar" .}}
 				{{if .IsOrganizationOwner}}
 					<div class="ui attached segment">
-						<form class="ui form ignore-dirty tw-flex tw-flex-wrap gt-gap-3" action="{{$.OrgLink}}/teams/{{$.Team.LowerName | PathEscape}}/action/add" method="post">
+						<form class="ui form ignore-dirty tw-flex tw-flex-wrap tw-gap-2" action="{{$.OrgLink}}/teams/{{$.Team.LowerName | PathEscape}}/action/add" method="post">
 							{{.CsrfTokenHtml}}
 							<input type="hidden" name="uid" value="{{.SignedUser.ID}}">
 							<div id="search-user-box" class="ui search gt-mr-3"{{if .IsEmailInviteEnabled}} data-allow-email="true" data-allow-email-description="{{ctx.Locale.Tr "org.teams.invite_team_member" $.Team.Name}}"{{end}}>
diff --git a/templates/org/team/repositories.tmpl b/templates/org/team/repositories.tmpl
index 0c59eafbe1..202279240b 100644
--- a/templates/org/team/repositories.tmpl
+++ b/templates/org/team/repositories.tmpl
@@ -9,7 +9,7 @@
 				{{template "org/team/navbar" .}}
 				{{$canAddRemove := and $.IsOrganizationOwner (not $.Team.IncludesAllRepositories)}}
 				{{if $canAddRemove}}
-					<div class="ui attached segment tw-flex tw-flex-wrap gt-gap-3">
+					<div class="ui attached segment tw-flex tw-flex-wrap tw-gap-2">
 						<form class="ui form ignore-dirty tw-flex-1 tw-flex" action="{{$.OrgLink}}/teams/{{$.Team.LowerName | PathEscape}}/action/repo/add" method="post">
 							{{.CsrfTokenHtml}}
 							<div id="search-repo-box" data-uid="{{.Org.ID}}" class="ui search">
diff --git a/templates/repo/diff/box.tmpl b/templates/repo/diff/box.tmpl
index 6dce6be514..672d1254d0 100644
--- a/templates/repo/diff/box.tmpl
+++ b/templates/repo/diff/box.tmpl
@@ -1,7 +1,7 @@
 {{$showFileTree := (and (not .DiffNotAvailable) (gt .Diff.NumFiles 1))}}
 <div>
 	<div class="diff-detail-box diff-box">
-		<div class="tw-flex tw-items-center tw-flex-wrap gt-gap-3 gt-ml-1">
+		<div class="tw-flex tw-items-center tw-flex-wrap tw-gap-2 gt-ml-1">
 			{{if $showFileTree}}
 				<button class="diff-toggle-file-tree-button not-mobile btn interact-fg" data-show-text="{{ctx.Locale.Tr "repo.diff.show_file_tree"}}" data-hide-text="{{ctx.Locale.Tr "repo.diff.hide_file_tree"}}">
 					{{/* the icon meaning is reversed here, "octicon-sidebar-collapse" means show the file tree */}}
@@ -111,7 +111,7 @@
 					{{$isReviewFile := and $.IsSigned $.PageIsPullFiles (not $.IsArchived) $.IsShowingAllCommits}}
 					<div class="diff-file-box diff-box file-content {{TabSizeClass $.Editorconfig $file.Name}} gt-mt-0" id="diff-{{$file.NameHash}}" data-old-filename="{{$file.OldName}}" data-new-filename="{{$file.Name}}" {{if or ($file.ShouldBeHidden) (not $isExpandable)}}data-folded="true"{{end}}>
 						<h4 class="diff-file-header sticky-2nd-row ui top attached normal header tw-flex tw-items-center tw-justify-between tw-flex-wrap">
-							<div class="diff-file-name tw-flex tw-items-center gt-gap-2 tw-flex-wrap">
+							<div class="diff-file-name tw-flex tw-items-center tw-gap-1 tw-flex-wrap">
 								<button class="fold-file btn interact-bg gt-p-2{{if not $isExpandable}} tw-invisible{{end}}">
 									{{if $file.ShouldBeHidden}}
 										{{svg "octicon-chevron-right" 18}}
@@ -144,7 +144,7 @@
 									<span class="gt-ml-4 gt-mono">{{ctx.Locale.Tr ($file.ModeTranslationKey $file.Mode)}}</span>
 								{{end}}
 							</div>
-							<div class="diff-file-header-actions tw-flex tw-items-center gt-gap-2 tw-flex-wrap">
+							<div class="diff-file-header-actions tw-flex tw-items-center tw-gap-1 tw-flex-wrap">
 								{{if $showFileViewToggle}}
 									<div class="ui compact icon buttons">
 										<button class="ui tiny basic button file-view-toggle" data-toggle-selector="#diff-source-{{$file.NameHash}}" data-tooltip-content="{{ctx.Locale.Tr "repo.file_view_source"}}">{{svg "octicon-code"}}</button>
diff --git a/templates/repo/diff/conversation.tmpl b/templates/repo/diff/conversation.tmpl
index a453c6bd09..8510f94101 100644
--- a/templates/repo/diff/conversation.tmpl
+++ b/templates/repo/diff/conversation.tmpl
@@ -6,7 +6,7 @@
 <div class="conversation-holder" data-path="{{(index .comments 0).TreePath}}" data-side="{{if lt (index .comments 0).Line 0}}left{{else}}right{{end}}" data-idx="{{(index .comments 0).UnsignedLine}}">
 	{{if $resolved}}
 		<div class="ui attached header resolved-placeholder tw-flex tw-items-center tw-justify-between">
-			<div class="ui grey text tw-flex tw-items-center tw-flex-wrap gt-gap-2">
+			<div class="ui grey text tw-flex tw-items-center tw-flex-wrap tw-gap-1">
 				{{svg "octicon-check" 16 "icon gt-mr-2"}}
 				<b>{{$resolveDoer.Name}}</b> {{ctx.Locale.Tr "repo.issues.review.resolved_by"}}
 				{{if $invalid}}
@@ -19,7 +19,7 @@
 					</a>
 				{{end}}
 			</div>
-			<div class="tw-flex tw-items-center gt-gap-3">
+			<div class="tw-flex tw-items-center tw-gap-2">
 				<button id="show-outdated-{{(index .comments 0).ID}}" data-comment="{{(index .comments 0).ID}}" class="ui tiny labeled button show-outdated tw-flex tw-items-center">
 					{{svg "octicon-unfold" 16 "gt-mr-3"}}
 					{{ctx.Locale.Tr "repo.issues.review.show_resolved"}}
diff --git a/templates/repo/home.tmpl b/templates/repo/home.tmpl
index 503e97a928..bfb5ab5232 100644
--- a/templates/repo/home.tmpl
+++ b/templates/repo/home.tmpl
@@ -27,7 +27,7 @@
 				</form>
 			</div>
 		</div>
-		<div class="tw-flex tw-items-center tw-flex-wrap gt-gap-2" id="repo-topics">
+		<div class="tw-flex tw-items-center tw-flex-wrap tw-gap-1" id="repo-topics">
 			{{range .Topics}}<a class="ui repo-topic large label topic gt-m-0" href="{{AppSubUrl}}/explore/repos?q={{.Name}}&topic=1">{{.Name}}</a>{{end}}
 			{{if and .Permission.IsAdmin (not .Repository.IsArchived)}}<button id="manage_topic" class="btn interact-fg tw-text-12">{{ctx.Locale.Tr "repo.topic.manage_topics"}}</button>{{end}}
 		</div>
@@ -69,7 +69,7 @@
 		{{end}}
 		{{template "repo/sub_menu" .}}
 		<div class="repo-button-row">
-			<div class="tw-flex tw-items-center tw-flex-wrap gt-gap-y-3">
+			<div class="tw-flex tw-items-center tw-flex-wrap tw-gap-y-2">
 				{{template "repo/branch_dropdown" dict "root" . "ContainerClasses" "gt-mr-2"}}
 				{{if and .CanCompareOrPull .IsViewBranch (not .Repository.IsArchived)}}
 					{{$cmpBranch := ""}}
diff --git a/templates/repo/issue/milestone_issues.tmpl b/templates/repo/issue/milestone_issues.tmpl
index 2028375c03..8ba7eecf4d 100644
--- a/templates/repo/issue/milestone_issues.tmpl
+++ b/templates/repo/issue/milestone_issues.tmpl
@@ -26,9 +26,9 @@
 				{{.Milestone.RenderedContent}}
 		</div>
 		{{end}}
-		<div class="tw-flex tw-flex-col gt-gap-3">
+		<div class="tw-flex tw-flex-col tw-gap-2">
 			<progress class="milestone-progress-big" value="{{.Milestone.Completeness}}" max="100"></progress>
-			<div class="tw-flex gt-gap-4">
+			<div class="tw-flex tw-gap-4">
 				<div classs="tw-flex tw-items-center">
 					{{$closedDate:= TimeSinceUnix .Milestone.ClosedDateUnix ctx.Locale}}
 					{{if .IsClosed}}
diff --git a/templates/repo/issue/view_content/sidebar.tmpl b/templates/repo/issue/view_content/sidebar.tmpl
index 561fe8c0ad..1414ac45ee 100644
--- a/templates/repo/issue/view_content/sidebar.tmpl
+++ b/templates/repo/issue/view_content/sidebar.tmpl
@@ -58,7 +58,7 @@
 								<span class="text">{{svg "octicon-people" 20 "gt-mr-3"}}{{$.Issue.Repo.OwnerName}}/{{.Team.Name}}</span>
 							{{end}}
 						</div>
-						<div class="tw-flex tw-items-center gt-gap-3">
+						<div class="tw-flex tw-items-center tw-gap-2">
 							{{if (and $.Permission.IsAdmin (or (eq .Review.Type 1) (eq .Review.Type 3)) (not $.Issue.IsClosed))}}
 								<a href="#" class="ui muted icon tw-flex tw-items-center show-modal" data-tooltip-content="{{ctx.Locale.Tr "repo.issues.dismiss_review"}}" data-modal="#dismiss-review-modal-{{.Review.ID}}">
 									{{svg "octicon-x" 20}}
@@ -106,7 +106,7 @@
 								{{.OriginalAuthor}}
 							</a>
 						</div>
-						<div class="tw-flex tw-items-center gt-gap-3">
+						<div class="tw-flex tw-items-center tw-gap-2">
 							{{svg (printf "octicon-%s" .Type.Icon) 16 (printf "text %s" (.HTMLTypeColorName))}}
 						</div>
 					</div>
@@ -317,7 +317,7 @@
 						<div class="ui mini modal issue-start-time-modal">
 							<div class="header">{{ctx.Locale.Tr "repo.issues.add_time"}}</div>
 							<div class="content">
-								<form method="post" id="add_time_manual_form" action="{{.Issue.Link}}/times/add" class="ui input fluid gt-gap-3">
+								<form method="post" id="add_time_manual_form" action="{{.Issue.Link}}/times/add" class="ui input fluid tw-gap-2">
 									{{$.CsrfTokenHtml}}
 									<input placeholder='{{ctx.Locale.Tr "repo.issues.add_time_hours"}}' type="number" name="hours">
 									<input placeholder='{{ctx.Locale.Tr "repo.issues.add_time_minutes"}}' type="number" name="minutes" class="ui compact">
diff --git a/templates/repo/pulls/tab_menu.tmpl b/templates/repo/pulls/tab_menu.tmpl
index 7f4c460b8e..0ddb17a934 100644
--- a/templates/repo/pulls/tab_menu.tmpl
+++ b/templates/repo/pulls/tab_menu.tmpl
@@ -15,7 +15,7 @@
 			{{ctx.Locale.Tr "repo.pulls.tab_files"}}
 			<span class="ui small label">{{if .NumFiles}}{{.NumFiles}}{{else}}-{{end}}</span>
 		</a>
-		<span class="item tw-ml-auto gt-pr-0 tw-font-bold tw-flex tw-items-center gt-gap-3">
+		<span class="item tw-ml-auto gt-pr-0 tw-font-bold tw-flex tw-items-center tw-gap-2">
 			<span><span class="text green">{{if .Diff.TotalAddition}}+{{.Diff.TotalAddition}}{{end}}</span> <span class="text red">{{if .Diff.TotalDeletion}}-{{.Diff.TotalDeletion}}{{end}}</span></span>
 			<span class="diff-stats-bar">
 				<div class="diff-stats-add-bar" style="width: {{Eval 100 "*" .Diff.TotalAddition "/" "(" .Diff.TotalAddition "+" .Diff.TotalDeletion "+" 0.0 ")"}}%"></div>
diff --git a/templates/user/auth/signin_inner.tmpl b/templates/user/auth/signin_inner.tmpl
index 1950f750ce..ad86b0b881 100644
--- a/templates/user/auth/signin_inner.tmpl
+++ b/templates/user/auth/signin_inner.tmpl
@@ -55,7 +55,7 @@
 	</div>
 	<div id="oauth2-login-navigator" class="gt-py-2">
 		<div class="tw-flex tw-flex-col tw-justify-center">
-			<div id="oauth2-login-navigator-inner" class="tw-flex tw-flex-col tw-flex-wrap tw-items-center gt-gap-3">
+			<div id="oauth2-login-navigator-inner" class="tw-flex tw-flex-col tw-flex-wrap tw-items-center tw-gap-2">
 				{{range $provider := .OAuth2Providers}}
 					<a class="{{$provider.Name}} ui button tw-flex tw-items-center tw-justify-center gt-py-3 tw-w-full oauth-login-link" href="{{AppSubUrl}}/user/oauth2/{{$provider.DisplayName}}">
 						{{$provider.IconHTML 28}}
diff --git a/templates/user/auth/signup_inner.tmpl b/templates/user/auth/signup_inner.tmpl
index b96e9bfb02..26d9091b68 100644
--- a/templates/user/auth/signup_inner.tmpl
+++ b/templates/user/auth/signup_inner.tmpl
@@ -60,7 +60,7 @@
 			</div>
 			<div id="oauth2-login-navigator" class="gt-py-2">
 				<div class="tw-flex tw-flex-col tw-justify-center">
-					<div id="oauth2-login-navigator-inner" class="tw-flex tw-flex-col tw-flex-wrap tw-items-center gt-gap-3">
+					<div id="oauth2-login-navigator-inner" class="tw-flex tw-flex-col tw-flex-wrap tw-items-center tw-gap-2">
 						{{range $provider := .OAuth2Providers}}
 							<a class="{{$provider.Name}} ui button tw-flex tw-items-center tw-justify-center gt-py-3 tw-w-full oauth-login-link" href="{{AppSubUrl}}/user/oauth2/{{$provider.DisplayName}}">
 								{{$provider.IconHTML 28}}
diff --git a/templates/user/auth/webauthn.tmpl b/templates/user/auth/webauthn.tmpl
index 867248718d..24dd75eed5 100644
--- a/templates/user/auth/webauthn.tmpl
+++ b/templates/user/auth/webauthn.tmpl
@@ -10,7 +10,7 @@
 				{{template "base/alert" .}}
 				<p>{{ctx.Locale.Tr "webauthn_sign_in"}}</p>
 			</div>
-			<div class="ui attached segment tw-flex tw-items-center tw-justify-center gt-gap-2 gt-py-3">
+			<div class="ui attached segment tw-flex tw-items-center tw-justify-center tw-gap-1 gt-py-3">
 				<div class="is-loading tw-w-[40px] tw-h-[40px]"></div>
 				{{ctx.Locale.Tr "webauthn_press_button"}}
 			</div>
diff --git a/templates/user/dashboard/feeds.tmpl b/templates/user/dashboard/feeds.tmpl
index c58d7e22d7..fbe151607c 100644
--- a/templates/user/dashboard/feeds.tmpl
+++ b/templates/user/dashboard/feeds.tmpl
@@ -4,7 +4,7 @@
 			<div class="flex-item-leading">
 				{{ctx.AvatarUtils.AvatarByAction .}}
 			</div>
-			<div class="flex-item-main gt-gap-3">
+			<div class="flex-item-main tw-gap-2">
 				<div>
 					{{if gt .ActUser.ID 0}}
 						<a href="{{AppSubUrl}}/{{(.GetActUserName ctx) | PathEscape}}" title="{{.GetActDisplayNameTitle ctx}}">{{.GetActDisplayName ctx}}</a>
@@ -84,7 +84,7 @@
 					{{$push := ActionContent2Commits .}}
 					{{$repoLink := (.GetRepoLink ctx)}}
 					{{$repo := .Repo}}
-					<div class="tw-flex tw-flex-col gt-gap-2">
+					<div class="tw-flex tw-flex-col tw-gap-1">
 						{{range $push.Commits}}
 							{{$commitLink := printf "%s/commit/%s" $repoLink .Sha1}}
 							<div class="flex-text-block">
diff --git a/templates/user/notification/notification_div.tmpl b/templates/user/notification/notification_div.tmpl
index da5a920fd1..f0fe6ac6f4 100644
--- a/templates/user/notification/notification_div.tmpl
+++ b/templates/user/notification/notification_div.tmpl
@@ -35,7 +35,7 @@
 					</div>
 				{{else}}
 					{{range $notification := .Notifications}}
-						<div class="notifications-item tw-flex tw-items-center tw-flex-wrap gt-gap-3 gt-p-3" id="notification_{{.ID}}" data-status="{{.Status}}">
+						<div class="notifications-item tw-flex tw-items-center tw-flex-wrap tw-gap-2 gt-p-3" id="notification_{{.ID}}" data-status="{{.Status}}">
 							<div class="notifications-icon gt-ml-3 gt-mr-2 tw-self-start gt-mt-2">
 								{{if .Issue}}
 									{{template "shared/issueicon" .Issue}}
@@ -67,7 +67,7 @@
 									{{TimeSinceUnix .UpdatedUnix ctx.Locale}}
 								{{end}}
 							</div>
-							<div class="notifications-buttons tw-items-center tw-justify-end gt-gap-2 gt-px-2">
+							<div class="notifications-buttons tw-items-center tw-justify-end tw-gap-1 gt-px-2">
 								{{if ne .Status 3}}
 									<form action="{{AppSubUrl}}/notifications/status" method="post">
 										{{$.CsrfTokenHtml}}
diff --git a/templates/user/settings/account.tmpl b/templates/user/settings/account.tmpl
index 24a7d236fe..63f490991a 100644
--- a/templates/user/settings/account.tmpl
+++ b/templates/user/settings/account.tmpl
@@ -46,7 +46,7 @@
 					<form action="{{AppSubUrl}}/user/settings/account/email" class="ui form" method="post">
 						{{$.CsrfTokenHtml}}
 						<input name="_method" type="hidden" value="NOTIFICATION">
-						<div class="tw-flex tw-flex-wrap gt-gap-3">
+						<div class="tw-flex tw-flex-wrap tw-gap-2">
 							<div class="ui selection dropdown">
 								<input name="preference" type="hidden" value="{{.EmailNotificationsPreference}}">
 								{{svg "octicon-triangle-down" 14 "dropdown icon"}}
diff --git a/web_src/css/helpers.css b/web_src/css/helpers.css
index 848e17ebe7..b7742b5cf9 100644
--- a/web_src/css/helpers.css
+++ b/web_src/css/helpers.css
@@ -142,27 +142,6 @@ Gitea's private styles use `g-` prefix.
 .gt-py-4 { padding-top: 1rem !important; padding-bottom: 1rem !important; }
 .gt-py-5 { padding-top: 2rem !important; padding-bottom: 2rem !important; }
 
-.gt-gap-0 { gap: 0 !important; }
-.gt-gap-1 { gap: .125rem !important; }
-.gt-gap-2 { gap: .25rem !important; }
-.gt-gap-3 { gap: .5rem !important; }
-.gt-gap-4 { gap: 1rem !important; }
-.gt-gap-5 { gap: 2rem !important; }
-
-.gt-gap-x-0 { column-gap: 0 !important; }
-.gt-gap-x-1 { column-gap: .125rem !important; }
-.gt-gap-x-2 { column-gap: .25rem !important; }
-.gt-gap-x-3 { column-gap: .5rem !important; }
-.gt-gap-x-4 { column-gap: 1rem !important; }
-.gt-gap-x-5 { column-gap: 2rem !important; }
-
-.gt-gap-y-0 { row-gap: 0 !important; }
-.gt-gap-y-1 { row-gap: .125rem !important; }
-.gt-gap-y-2 { row-gap: .25rem !important; }
-.gt-gap-y-3 { row-gap: .5rem !important; }
-.gt-gap-y-4 { row-gap: 1rem !important; }
-.gt-gap-y-5 { row-gap: 2rem !important; }
-
 /*
 gt-hidden must win all other "display: xxx !important" classes to get the chance to "hide" an element.
 do not use:
diff --git a/web_src/js/components/DiffCommitSelector.vue b/web_src/js/components/DiffCommitSelector.vue
index df712a2cb4..35245f2190 100644
--- a/web_src/js/components/DiffCommitSelector.vue
+++ b/web_src/js/components/DiffCommitSelector.vue
@@ -240,7 +240,7 @@ export default {
           @click.meta.exact="commitClicked(commit.id, true)"
           @click.shift.exact.stop.prevent="commitClickedShift(commit)"
         >
-          <div class="tw-flex-1 tw-flex tw-flex-col gt-gap-2">
+          <div class="tw-flex-1 tw-flex tw-flex-col tw-gap-1">
             <div class="gt-ellipsis commit-list-summary">
               {{ commit.summary }}
             </div>
diff --git a/web_src/js/components/RepoContributors.vue b/web_src/js/components/RepoContributors.vue
index 6de68a4aec..02db9e3e3e 100644
--- a/web_src/js/components/RepoContributors.vue
+++ b/web_src/js/components/RepoContributors.vue
@@ -384,7 +384,7 @@ export default {
             <h4 v-else class="contributor-name">
               {{ contributor.name }}
             </h4>
-            <p class="tw-text-12 tw-flex gt-gap-2">
+            <p class="tw-text-12 tw-flex tw-gap-1">
               <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">

From 522eb73ec9c28cb90dfa8bf822fd1071f48fdec9 Mon Sep 17 00:00:00 2001
From: Earl Warren <contact@earl-warren.org>
Date: Sun, 24 Mar 2024 22:54:16 +0100
Subject: [PATCH 73/74] lint: apply lint-frontend-fix

---
 web_src/css/modules/button.css           | 2 +-
 web_src/js/standalone/forgejo-swagger.js | 6 +++---
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/web_src/css/modules/button.css b/web_src/css/modules/button.css
index 3c80eb99c0..a06cab4465 100644
--- a/web_src/css/modules/button.css
+++ b/web_src/css/modules/button.css
@@ -154,7 +154,7 @@ It needs some tricks to tweak the left/right borders with active state */
 }
 
 .ui.basic.primary.buttons .button,
-.ui.basic.primary.button{
+.ui.basic.primary.button {
   color: var(--color-primary);
   border-color: var(--color-primary);
 }
diff --git a/web_src/js/standalone/forgejo-swagger.js b/web_src/js/standalone/forgejo-swagger.js
index 2419bdb3c6..896a1364c3 100644
--- a/web_src/js/standalone/forgejo-swagger.js
+++ b/web_src/js/standalone/forgejo-swagger.js
@@ -11,11 +11,11 @@ window.addEventListener('load', async () => {
     docExpansion: 'none',
     defaultModelRendering: 'model', // don't show examples by default, because they may be incomplete
     presets: [
-      SwaggerUI.presets.apis
+      SwaggerUI.presets.apis,
     ],
     plugins: [
-      SwaggerUI.plugins.DownloadUrl
-    ]
+      SwaggerUI.plugins.DownloadUrl,
+    ],
   });
 
   window.ui = ui;

From 62439738994d78b662b1dfbb15aef6b48d6b03ef Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Wed, 20 Mar 2024 22:05:34 +0800
Subject: [PATCH 74/74] Fix comment review avatar alignment (#29935)

Fix #29934

(cherry picked from commit bc55a80693aded26efd856812097536e2402d491)

Conflicts:
	templates/repo/issue/view_content/comments.tmpl
	trivial context conflict
---
 templates/repo/issue/view_content/comments.tmpl | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/templates/repo/issue/view_content/comments.tmpl b/templates/repo/issue/view_content/comments.tmpl
index a48ae1947b..2038eb7c48 100644
--- a/templates/repo/issue/view_content/comments.tmpl
+++ b/templates/repo/issue/view_content/comments.tmpl
@@ -376,10 +376,9 @@
 			<div class="timeline-item-group" id="{{.HashTag}}">
 				<div class="timeline-item event">
 					{{if not .OriginalAuthor}}
-					{{/* Some timeline avatars need a offset to correctly align with their speech
-							bubble. The condition depends on review type and for positive reviews whether
-							there is a comment element or not */}}
-					<a class="timeline-avatar{{if or (and (eq .Review.Type 1) (or .Content .Attachments)) (and (eq .Review.Type 2) (or .Content .Attachments)) (eq .Review.Type 3)}} timeline-avatar-offset{{end}}"{{if gt .Poster.ID 0}} href="{{.Poster.HomeLink}}"{{end}}>
+					{{/* Some timeline avatars need a offset to correctly align with their speech bubble.
+						The condition depends on whether the comment has contents/attachments or reviews */}}
+					<a class="timeline-avatar{{if or .Content .Attachments (and .Review .Review.CodeComments)}} timeline-avatar-offset{{end}}"{{if gt .Poster.ID 0}} href="{{.Poster.HomeLink}}"{{end}}>
 						{{ctx.AvatarUtils.Avatar .Poster 40}}
 					</a>
 					{{end}}