loading up the forgejo repo on tangled to test page performance
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge pull request '[gitea] week 12 cherry-pick' (#2679) from algernon/forgejo:wcp/week-12 into forgejo

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/2679
Reviewed-by: Otto <otto@codeberg.org>
Reviewed-by: oliverpool <oliverpool@noreply.codeberg.org>
Reviewed-by: Earl Warren <earl-warren@noreply.codeberg.org>

+3141 -2891
+1 -1
.deadcode-out
··· 84 84 func (*releaseSorter).Swap 85 85 func SortReleases 86 86 func FindReposMapByIDs 87 - func RepositoryListOfMap 88 87 func (SearchOrderBy).String 89 88 func IsErrTopicNotExist 90 89 func (ErrTopicNotExist).Error ··· 178 177 func (ErrExecTimeout).Error 179 178 func (ErrUnsupportedVersion).Error 180 179 func SetUpdateHook 180 + func GetObjectFormatOfRepo 181 181 func openRepositoryWithDefaultContext 182 182 func IsTagExist 183 183 func ToEntryMode
+6 -6
.eslintrc.yaml
··· 283 283 i/unambiguous: [0] 284 284 init-declarations: [0] 285 285 jquery/no-ajax-events: [2] 286 - jquery/no-ajax: [0] 286 + jquery/no-ajax: [2] 287 287 jquery/no-animate: [2] 288 288 jquery/no-attr: [0] 289 289 jquery/no-bind: [2] ··· 315 315 jquery/no-parent: [0] 316 316 jquery/no-parents: [0] 317 317 jquery/no-parse-html: [2] 318 - jquery/no-prop: [0] 318 + jquery/no-prop: [2] 319 319 jquery/no-proxy: [2] 320 320 jquery/no-ready: [2] 321 321 jquery/no-serialize: [2] ··· 396 396 no-irregular-whitespace: [2] 397 397 no-iterator: [2] 398 398 no-jquery/no-ajax-events: [2] 399 - no-jquery/no-ajax: [0] 399 + no-jquery/no-ajax: [2] 400 400 no-jquery/no-and-self: [2] 401 401 no-jquery/no-animate-toggle: [2] 402 402 no-jquery/no-animate: [2] 403 - no-jquery/no-append-html: [0] 403 + no-jquery/no-append-html: [2] 404 404 no-jquery/no-attr: [0] 405 405 no-jquery/no-bind: [2] 406 406 no-jquery/no-box-model: [2] ··· 466 466 no-jquery/no-parse-html: [2] 467 467 no-jquery/no-parse-json: [2] 468 468 no-jquery/no-parse-xml: [2] 469 - no-jquery/no-prop: [0] 469 + no-jquery/no-prop: [2] 470 470 no-jquery/no-proxy: [2] 471 471 no-jquery/no-ready-shorthand: [2] 472 472 no-jquery/no-ready: [2] ··· 487 487 no-jquery/no-visibility: [2] 488 488 no-jquery/no-when: [2] 489 489 no-jquery/no-wrap: [2] 490 - no-jquery/variable-pattern: [0] 490 + no-jquery/variable-pattern: [2] 491 491 no-label-var: [2] 492 492 no-labels: [0] # handled by no-restricted-syntax 493 493 no-lone-blocks: [2]
-4
Makefile
··· 881 881 release-docs: | $(DIST_DIRS) docs 882 882 tar -czf $(DIST)/release/gitea-docs-$(VERSION).tar.gz -C ./docs . 883 883 884 - .PHONY: docs 885 - docs: 886 - cd docs; bash scripts/trans-copy.sh; 887 - 888 884 .PHONY: deps 889 885 deps: deps-frontend deps-backend deps-tools deps-py 890 886
+1 -1
docs/content/administration/mail-templates.en-us.md
··· 163 163 164 164 If the template fails to render, it will be noticed only at the moment the mail is sent. 165 165 A default subject is used if the subject template fails, and whatever was rendered successfully 166 - from the the _mail body_ is used, disregarding the rest. 166 + from the _mail body_ is used, disregarding the rest. 167 167 168 168 Please check [Gitea's logs](administration/logging-config.md) for error messages in case of trouble. 169 169
+1 -6
docs/content/development/hacking-on-gitea.en-us.md
··· 333 333 can test your changes to ensure that they pass continuous integration using: 334 334 335 335 ```bash 336 - # from the docs directory within Gitea 337 - make trans-copy clean build 336 + make lint-md 338 337 ``` 339 - 340 - You will require a copy of [Hugo](https://gohugo.io/) to run this task. Please 341 - note: this may generate a number of untracked Git objects, which will need to 342 - be cleaned up. 343 338 344 339 ## Visual Studio Code 345 340
+1 -5
docs/content/development/hacking-on-gitea.zh-cn.md
··· 307 307 该网站的文档位于 `docs/` 中。如果你改变了文档内容,你可以使用以下测试方法进行持续集成: 308 308 309 309 ```bash 310 - # 来自 Gitea 中的 docs 目录 311 - make trans-copy clean build 310 + make lint-md 312 311 ``` 313 - 314 - 运行此任务依赖于 [Hugo](https://gohugo.io/)。请注意:这可能会生成一些未跟踪的 Git 对象, 315 - 需要被清理干净。 316 312 317 313 ## Visual Studio Code 318 314
-34
docs/scripts/trans-copy.sh
··· 1 - #!/usr/bin/env bash 2 - set -e 3 - 4 - # 5 - # This script is used to copy the en-US content to our available locales as a 6 - # fallback to always show all pages when displaying a specific locale that is 7 - # missing some documents to be translated. 8 - # 9 - # Just execute the script without any argument and you will get the missing 10 - # files copied into the content folder. We are calling this script within the CI 11 - # server simply by `make trans-copy`. 12 - # 13 - 14 - declare -a LOCALES=( 15 - "fr-fr" 16 - "nl-nl" 17 - "pt-br" 18 - "zh-cn" 19 - "zh-tw" 20 - ) 21 - 22 - ROOT=$(realpath $(dirname $0)/..) 23 - 24 - for SOURCE in $(find ${ROOT}/content -type f -iname *.en-us.md); do 25 - for LOCALE in "${LOCALES[@]}"; do 26 - DEST="${SOURCE%.en-us.md}.${LOCALE}.md" 27 - 28 - if [[ ! -f ${DEST} ]]; then 29 - cp ${SOURCE} ${DEST} 30 - sed -i.bak "s/en\-us/${LOCALE}/g" ${DEST} 31 - rm ${DEST}.bak 32 - fi 33 - done 34 - done
+2 -2
go.mod
··· 17 17 github.com/42wim/sshsig v0.0.0-20211121163825-841cf5bbc121 18 18 github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 19 19 github.com/PuerkitoBio/goquery v1.8.1 20 - github.com/alecthomas/chroma/v2 v2.12.0 20 + github.com/alecthomas/chroma/v2 v2.13.0 21 21 github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb 22 22 github.com/blevesearch/bleve/v2 v2.3.10 23 23 github.com/buildkite/terminal-to-html/v3 v3.10.1 ··· 169 169 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect 170 170 github.com/davidmz/go-pageant v1.0.2 // indirect 171 171 github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect 172 - github.com/dlclark/regexp2 v1.10.0 // indirect 172 + github.com/dlclark/regexp2 v1.11.0 // indirect 173 173 github.com/emersion/go-sasl v0.0.0-20231106173351-e73c9f7bad43 // indirect 174 174 github.com/fatih/color v1.16.0 // indirect 175 175 github.com/felixge/httpsnoop v1.0.4 // indirect
+8 -8
go.sum
··· 103 103 github.com/PuerkitoBio/goquery v1.8.1/go.mod h1:Q8ICL1kNUJ2sXGoAhPGUdYDJvgQgHzJsnnd3H7Ho5jQ= 104 104 github.com/RoaringBitmap/roaring v1.7.0 h1:OZF303tJCER1Tj3x+aArx/S5X7hrT186ri6JjrGvG68= 105 105 github.com/RoaringBitmap/roaring v1.7.0/go.mod h1:6AXUsoIEzDTFFQCe1RbGA6uFONMhvejWj5rqITANK90= 106 - github.com/alecthomas/assert/v2 v2.2.1 h1:XivOgYcduV98QCahG8T5XTezV5bylXe+lBxLG2K2ink= 107 - github.com/alecthomas/assert/v2 v2.2.1/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ= 106 + github.com/alecthomas/assert/v2 v2.6.0 h1:o3WJwILtexrEUk3cUVal3oiQY2tfgr/FHWiz/v2n4FU= 107 + github.com/alecthomas/assert/v2 v2.6.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k= 108 108 github.com/alecthomas/chroma/v2 v2.2.0/go.mod h1:vf4zrexSH54oEjJ7EdB65tGNHmH3pGZmVkgTP5RHvAs= 109 - github.com/alecthomas/chroma/v2 v2.12.0 h1:Wh8qLEgMMsN7mgyG8/qIpegky2Hvzr4By6gEF7cmWgw= 110 - github.com/alecthomas/chroma/v2 v2.12.0/go.mod h1:4TQu7gdfuPjSh76j78ietmqh9LiurGF0EpseFXdKMBw= 109 + github.com/alecthomas/chroma/v2 v2.13.0 h1:VP72+99Fb2zEcYM0MeaWJmV+xQvz5v5cxRHd+ooU1lI= 110 + github.com/alecthomas/chroma/v2 v2.13.0/go.mod h1:BUGjjsD+ndS6eX37YgTchSEG+Jg9Jv1GiZs9sqPqztk= 111 111 github.com/alecthomas/repr v0.0.0-20220113201626-b1b626ac65ae/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8= 112 - github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk= 113 - github.com/alecthomas/repr v0.2.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= 112 + github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc= 113 + github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= 114 114 github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74 h1:Kk6a4nehpJ3UuJRqlA3JxYxBZEqCeOmATOvrbT4p9RA= 115 115 github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4= 116 116 github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= ··· 235 235 github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= 236 236 github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= 237 237 github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= 238 - github.com/dlclark/regexp2 v1.10.0 h1:+/GIL799phkJqYW+3YbOd8LCcbHzT0Pbo8zl70MHsq0= 239 - github.com/dlclark/regexp2 v1.10.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= 238 + github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI= 239 + github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= 240 240 github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= 241 241 github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 h1:iFaUwBSo5Svw6L7HYpRu/0lE3e0BaElwnNO1qkNQxBY= 242 242 github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s=
+53 -53
models/activities/action.go
··· 150 150 Repo *repo_model.Repository `xorm:"-"` 151 151 CommentID int64 `xorm:"INDEX"` 152 152 Comment *issues_model.Comment `xorm:"-"` 153 + Issue *issues_model.Issue `xorm:"-"` // get the issue id from content 153 154 IsDeleted bool `xorm:"NOT NULL DEFAULT false"` 154 155 RefName string 155 156 IsPrivate bool `xorm:"NOT NULL DEFAULT false"` ··· 292 293 return setting.AppURL + url.PathEscape(a.GetRepoUserName(ctx)) + "/" + url.PathEscape(a.GetRepoName(ctx)) 293 294 } 294 295 295 - // GetCommentHTMLURL returns link to action comment. 296 - func (a *Action) GetCommentHTMLURL(ctx context.Context) string { 297 - return a.getCommentHTMLURL(ctx) 298 - } 299 - 300 296 func (a *Action) loadComment(ctx context.Context) (err error) { 301 297 if a.CommentID == 0 || a.Comment != nil { 302 298 return nil ··· 305 301 return err 306 302 } 307 303 308 - func (a *Action) getCommentHTMLURL(ctx context.Context) string { 304 + // GetCommentHTMLURL returns link to action comment. 305 + func (a *Action) GetCommentHTMLURL(ctx context.Context) string { 309 306 if a == nil { 310 307 return "#" 311 308 } ··· 313 310 if a.Comment != nil { 314 311 return a.Comment.HTMLURL(ctx) 315 312 } 316 - if len(a.GetIssueInfos()) == 0 { 317 - return "#" 318 - } 319 - // Return link to issue 320 - issueIDString := a.GetIssueInfos()[0] 321 - issueID, err := strconv.ParseInt(issueIDString, 10, 64) 322 - if err != nil { 323 - return "#" 324 - } 325 313 326 - issue, err := issues_model.GetIssueByID(ctx, issueID) 327 - if err != nil { 314 + if err := a.LoadIssue(ctx); err != nil || a.Issue == nil { 328 315 return "#" 329 316 } 330 - 331 - if err = issue.LoadRepo(ctx); err != nil { 317 + if err := a.Issue.LoadRepo(ctx); err != nil { 332 318 return "#" 333 319 } 334 320 335 - return issue.HTMLURL() 321 + return a.Issue.HTMLURL() 336 322 } 337 323 338 324 // GetCommentLink returns link to action comment. 339 325 func (a *Action) GetCommentLink(ctx context.Context) string { 340 - return a.getCommentLink(ctx) 341 - } 342 - 343 - func (a *Action) getCommentLink(ctx context.Context) string { 344 326 if a == nil { 345 327 return "#" 346 328 } ··· 348 330 if a.Comment != nil { 349 331 return a.Comment.Link(ctx) 350 332 } 351 - if len(a.GetIssueInfos()) == 0 { 352 - return "#" 353 - } 354 - // Return link to issue 355 - issueIDString := a.GetIssueInfos()[0] 356 - issueID, err := strconv.ParseInt(issueIDString, 10, 64) 357 - if err != nil { 358 - return "#" 359 - } 360 333 361 - issue, err := issues_model.GetIssueByID(ctx, issueID) 362 - if err != nil { 334 + if err := a.LoadIssue(ctx); err != nil || a.Issue == nil { 363 335 return "#" 364 336 } 365 - 366 - if err = issue.LoadRepo(ctx); err != nil { 337 + if err := a.Issue.LoadRepo(ctx); err != nil { 367 338 return "#" 368 339 } 369 340 370 - return issue.Link() 341 + return a.Issue.Link() 371 342 } 372 343 373 344 // GetBranch returns the action's repository branch. ··· 395 366 return a.CreatedUnix.AsTime() 396 367 } 397 368 369 + func (a *Action) IsIssueEvent() bool { 370 + return a.OpType.InActions("comment_issue", "approve_pull_request", "reject_pull_request", "comment_pull", "merge_pull_request") 371 + } 372 + 398 373 // GetIssueInfos returns a list of associated information with the action. 399 374 func (a *Action) GetIssueInfos() []string { 400 375 // make sure it always returns 3 elements, because there are some access to the a[1] and a[2] without checking the length ··· 405 380 return ret 406 381 } 407 382 383 + func (a *Action) getIssueIndex() int64 { 384 + infos := a.GetIssueInfos() 385 + if len(infos) == 0 { 386 + return 0 387 + } 388 + index, _ := strconv.ParseInt(infos[0], 10, 64) 389 + return index 390 + } 391 + 392 + func (a *Action) LoadIssue(ctx context.Context) error { 393 + if a.Issue != nil { 394 + return nil 395 + } 396 + if index := a.getIssueIndex(); index > 0 { 397 + issue, err := issues_model.GetIssueByIndex(ctx, a.RepoID, index) 398 + if err != nil { 399 + return err 400 + } 401 + a.Issue = issue 402 + a.Issue.Repo = a.Repo 403 + } 404 + return nil 405 + } 406 + 408 407 // GetIssueTitle returns the title of first issue associated with the action. 409 408 func (a *Action) GetIssueTitle(ctx context.Context) string { 410 - index, _ := strconv.ParseInt(a.GetIssueInfos()[0], 10, 64) 411 - issue, err := issues_model.GetIssueByIndex(ctx, a.RepoID, index) 412 - if err != nil { 413 - log.Error("GetIssueByIndex: %v", err) 414 - return "500 when get issue" 409 + if err := a.LoadIssue(ctx); err != nil { 410 + log.Error("LoadIssue: %v", err) 411 + return "<500 when get issue>" 412 + } 413 + if a.Issue == nil { 414 + return "<Issue not found>" 415 415 } 416 - return issue.Title 416 + return a.Issue.Title 417 417 } 418 418 419 - // GetIssueContent returns the content of first issue associated with 420 - // this action. 419 + // GetIssueContent returns the content of first issue associated with this action. 421 420 func (a *Action) GetIssueContent(ctx context.Context) string { 422 - index, _ := strconv.ParseInt(a.GetIssueInfos()[0], 10, 64) 423 - issue, err := issues_model.GetIssueByIndex(ctx, a.RepoID, index) 424 - if err != nil { 425 - log.Error("GetIssueByIndex: %v", err) 426 - return "500 when get issue" 421 + if err := a.LoadIssue(ctx); err != nil { 422 + log.Error("LoadIssue: %v", err) 423 + return "<500 when get issue>" 427 424 } 428 - return issue.Content 425 + if a.Issue == nil { 426 + return "<Content not found>" 427 + } 428 + return a.Issue.Content 429 429 } 430 430 431 431 // GetFeedsOptions options for retrieving feeds ··· 465 465 return nil, 0, fmt.Errorf("FindAndCount: %w", err) 466 466 } 467 467 468 - if err := ActionList(actions).loadAttributes(ctx); err != nil { 468 + if err := ActionList(actions).LoadAttributes(ctx); err != nil { 469 469 return nil, 0, fmt.Errorf("LoadAttributes: %w", err) 470 470 } 471 471
+113 -21
models/activities/action_list.go
··· 6 6 import ( 7 7 "context" 8 8 "fmt" 9 + "strconv" 9 10 10 11 "code.gitea.io/gitea/models/db" 12 + issues_model "code.gitea.io/gitea/models/issues" 11 13 repo_model "code.gitea.io/gitea/models/repo" 12 14 user_model "code.gitea.io/gitea/models/user" 13 15 "code.gitea.io/gitea/modules/container" 16 + "code.gitea.io/gitea/modules/util" 17 + 18 + "xorm.io/builder" 14 19 ) 15 20 16 21 // ActionList defines a list of actions ··· 24 29 return userIDs.Values() 25 30 } 26 31 27 - func (actions ActionList) loadUsers(ctx context.Context) (map[int64]*user_model.User, error) { 32 + func (actions ActionList) LoadActUsers(ctx context.Context) (map[int64]*user_model.User, error) { 28 33 if len(actions) == 0 { 29 34 return nil, nil 30 35 } ··· 52 57 return repoIDs.Values() 53 58 } 54 59 55 - func (actions ActionList) loadRepositories(ctx context.Context) error { 60 + func (actions ActionList) LoadRepositories(ctx context.Context) error { 56 61 if len(actions) == 0 { 57 62 return nil 58 63 } ··· 63 68 if err != nil { 64 69 return fmt.Errorf("find repository: %w", err) 65 70 } 66 - 67 71 for _, action := range actions { 68 72 action.Repo = repoMaps[action.RepoID] 69 73 } 70 - return nil 74 + repos := repo_model.RepositoryList(util.ValuesOfMap(repoMaps)) 75 + return repos.LoadUnits(ctx) 71 76 } 72 77 73 78 func (actions ActionList) loadRepoOwner(ctx context.Context, userMap map[int64]*user_model.User) (err error) { ··· 75 80 userMap = make(map[int64]*user_model.User) 76 81 } 77 82 83 + userSet := make(container.Set[int64], len(actions)) 78 84 for _, action := range actions { 79 85 if action.Repo == nil { 80 86 continue 81 87 } 82 - repoOwner, ok := userMap[action.Repo.OwnerID] 83 - if !ok { 84 - repoOwner, err = user_model.GetUserByID(ctx, action.Repo.OwnerID) 85 - if err != nil { 86 - if user_model.IsErrUserNotExist(err) { 87 - continue 88 - } 89 - return err 90 - } 91 - userMap[repoOwner.ID] = repoOwner 88 + if _, ok := userMap[action.Repo.OwnerID]; !ok { 89 + userSet.Add(action.Repo.OwnerID) 90 + } 91 + } 92 + 93 + if err := db.GetEngine(ctx). 94 + In("id", userSet.Values()). 95 + Find(&userMap); err != nil { 96 + return fmt.Errorf("find user: %w", err) 97 + } 98 + 99 + for _, action := range actions { 100 + if action.Repo != nil { 101 + action.Repo.Owner = userMap[action.Repo.OwnerID] 92 102 } 93 - action.Repo.Owner = repoOwner 94 103 } 95 104 96 105 return nil 97 106 } 98 107 99 - // loadAttributes loads all attributes 100 - func (actions ActionList) loadAttributes(ctx context.Context) error { 101 - userMap, err := actions.loadUsers(ctx) 108 + // LoadAttributes loads all attributes 109 + func (actions ActionList) LoadAttributes(ctx context.Context) error { 110 + // the load sequence cannot be changed because of the dependencies 111 + userMap, err := actions.LoadActUsers(ctx) 102 112 if err != nil { 103 113 return err 104 114 } 105 - 106 - if err := actions.loadRepositories(ctx); err != nil { 115 + if err := actions.LoadRepositories(ctx); err != nil { 116 + return err 117 + } 118 + if err := actions.loadRepoOwner(ctx, userMap); err != nil { 119 + return err 120 + } 121 + if err := actions.LoadIssues(ctx); err != nil { 107 122 return err 108 123 } 124 + return actions.LoadComments(ctx) 125 + } 109 126 110 - return actions.loadRepoOwner(ctx, userMap) 127 + func (actions ActionList) LoadComments(ctx context.Context) error { 128 + if len(actions) == 0 { 129 + return nil 130 + } 131 + 132 + commentIDs := make([]int64, 0, len(actions)) 133 + for _, action := range actions { 134 + if action.CommentID > 0 { 135 + commentIDs = append(commentIDs, action.CommentID) 136 + } 137 + } 138 + 139 + commentsMap := make(map[int64]*issues_model.Comment, len(commentIDs)) 140 + if err := db.GetEngine(ctx).In("id", commentIDs).Find(&commentsMap); err != nil { 141 + return fmt.Errorf("find comment: %w", err) 142 + } 143 + 144 + for _, action := range actions { 145 + if action.CommentID > 0 { 146 + action.Comment = commentsMap[action.CommentID] 147 + if action.Comment != nil { 148 + action.Comment.Issue = action.Issue 149 + } 150 + } 151 + } 152 + return nil 153 + } 154 + 155 + func (actions ActionList) LoadIssues(ctx context.Context) error { 156 + if len(actions) == 0 { 157 + return nil 158 + } 159 + 160 + conditions := builder.NewCond() 161 + issueNum := 0 162 + for _, action := range actions { 163 + if action.IsIssueEvent() { 164 + infos := action.GetIssueInfos() 165 + if len(infos) == 0 { 166 + continue 167 + } 168 + index, _ := strconv.ParseInt(infos[0], 10, 64) 169 + if index > 0 { 170 + conditions = conditions.Or(builder.Eq{ 171 + "repo_id": action.RepoID, 172 + "`index`": index, 173 + }) 174 + issueNum++ 175 + } 176 + } 177 + } 178 + if !conditions.IsValid() { 179 + return nil 180 + } 181 + 182 + issuesMap := make(map[string]*issues_model.Issue, issueNum) 183 + issues := make([]*issues_model.Issue, 0, issueNum) 184 + if err := db.GetEngine(ctx).Where(conditions).Find(&issues); err != nil { 185 + return fmt.Errorf("find issue: %w", err) 186 + } 187 + for _, issue := range issues { 188 + issuesMap[fmt.Sprintf("%d-%d", issue.RepoID, issue.Index)] = issue 189 + } 190 + 191 + for _, action := range actions { 192 + if !action.IsIssueEvent() { 193 + continue 194 + } 195 + if index := action.getIssueIndex(); index > 0 { 196 + if issue, ok := issuesMap[fmt.Sprintf("%d-%d", action.RepoID, index)]; ok { 197 + action.Issue = issue 198 + action.Issue.Repo = action.Repo 199 + } 200 + } 201 + } 202 + return nil 111 203 }
+6 -1
models/activities/statistic.go
··· 9 9 asymkey_model "code.gitea.io/gitea/models/asymkey" 10 10 "code.gitea.io/gitea/models/auth" 11 11 "code.gitea.io/gitea/models/db" 12 + git_model "code.gitea.io/gitea/models/git" 12 13 issues_model "code.gitea.io/gitea/models/issues" 13 14 "code.gitea.io/gitea/models/organization" 14 15 access_model "code.gitea.io/gitea/models/perm/access" ··· 29 30 Mirror, Release, AuthSource, Webhook, 30 31 Milestone, Label, HookTask, 31 32 Team, UpdateTask, Project, 32 - ProjectBoard, Attachment int64 33 + ProjectBoard, Attachment, 34 + Branches, Tags, CommitStatus int64 33 35 IssueByLabel []IssueByLabelCount 34 36 IssueByRepository []IssueByRepositoryCount 35 37 } ··· 58 60 stats.Counter.Watch, _ = e.Count(new(repo_model.Watch)) 59 61 stats.Counter.Star, _ = e.Count(new(repo_model.Star)) 60 62 stats.Counter.Access, _ = e.Count(new(access_model.Access)) 63 + stats.Counter.Branches, _ = e.Count(new(git_model.Branch)) 64 + stats.Counter.Tags, _ = e.Where("is_draft=?", false).Count(new(repo_model.Release)) 65 + stats.Counter.CommitStatus, _ = e.Count(new(git_model.CommitStatus)) 61 66 62 67 type IssueCount struct { 63 68 Count int64
+10
models/issues/issue_list.go
··· 476 476 } 477 477 trackedTimes := make(map[int64]int64, len(issues)) 478 478 479 + reposMap := make(map[int64]*repo_model.Repository, len(issues)) 480 + for _, issue := range issues { 481 + reposMap[issue.RepoID] = issue.Repo 482 + } 483 + repos := repo_model.RepositoryListOfMap(reposMap) 484 + 485 + if err := repos.LoadUnits(ctx); err != nil { 486 + return err 487 + } 488 + 479 489 ids := make([]int64, 0, len(issues)) 480 490 for _, issue := range issues { 481 491 if issue.Repo.IsTimetrackerEnabled(ctx) {
+1 -1
models/issues/issue_search.go
··· 393 393 394 394 func applyReviewedCondition(sess *xorm.Session, reviewedID int64) *xorm.Session { 395 395 // Query for pull requests where you are a reviewer or commenter, excluding 396 - // any pull requests already returned by the the review requested filter. 396 + // any pull requests already returned by the review requested filter. 397 397 notPoster := builder.Neq{"issue.poster_id": reviewedID} 398 398 reviewed := builder.In("issue.id", builder. 399 399 Select("issue_id").
+3
models/repo/repo.go
··· 553 553 return nil 554 554 } 555 555 556 + if repo.BaseRepo != nil { 557 + return nil 558 + } 556 559 repo.BaseRepo, err = GetRepositoryByID(ctx, repo.ForkID) 557 560 return err 558 561 }
+35
models/repo/repo_list.go
··· 63 63 return RepositoryList(ValuesRepository(repoMap)) 64 64 } 65 65 66 + func (repos RepositoryList) LoadUnits(ctx context.Context) error { 67 + if len(repos) == 0 { 68 + return nil 69 + } 70 + 71 + // Load units. 72 + units := make([]*RepoUnit, 0, len(repos)*6) 73 + if err := db.GetEngine(ctx). 74 + In("repo_id", repos.IDs()). 75 + Find(&units); err != nil { 76 + return fmt.Errorf("find units: %w", err) 77 + } 78 + 79 + unitsMap := make(map[int64][]*RepoUnit, len(repos)) 80 + for _, unit := range units { 81 + if !unit.Type.UnitGlobalDisabled() { 82 + unitsMap[unit.RepoID] = append(unitsMap[unit.RepoID], unit) 83 + } 84 + } 85 + 86 + for _, repo := range repos { 87 + repo.Units = unitsMap[repo.ID] 88 + } 89 + 90 + return nil 91 + } 92 + 93 + func (repos RepositoryList) IDs() []int64 { 94 + repoIDs := make([]int64, len(repos)) 95 + for i := range repos { 96 + repoIDs[i] = repos[i].ID 97 + } 98 + return repoIDs 99 + } 100 + 66 101 // LoadAttributes loads the attributes for the given RepositoryList 67 102 func (repos RepositoryList) LoadAttributes(ctx context.Context) error { 68 103 if len(repos) == 0 {
+8 -8
models/user/email_address.go
··· 548 548 549 549 // validateEmailDomain checks whether the email domain is allowed or blocked 550 550 func validateEmailDomain(email string) error { 551 - // if there is no allow list, then check email against block list 552 - if len(setting.Service.EmailDomainAllowList) == 0 && 553 - validation.IsEmailDomainListed(setting.Service.EmailDomainBlockList, email) { 551 + if !IsEmailDomainAllowed(email) { 554 552 return ErrEmailInvalid{email} 555 553 } 556 554 557 - // if there is an allow list, then check email against allow list 558 - if len(setting.Service.EmailDomainAllowList) > 0 && 559 - !validation.IsEmailDomainListed(setting.Service.EmailDomainAllowList, email) { 560 - return ErrEmailInvalid{email} 555 + return nil 556 + } 557 + 558 + func IsEmailDomainAllowed(email string) bool { 559 + if len(setting.Service.EmailDomainAllowList) == 0 { 560 + return !validation.IsEmailDomainListed(setting.Service.EmailDomainBlockList, email) 561 561 } 562 562 563 - return nil 563 + return validation.IsEmailDomainListed(setting.Service.EmailDomainAllowList, email) 564 564 }
+1 -1
models/user/user.go
··· 436 436 return u.Name 437 437 } 438 438 439 - // GetCompleteName returns the the full name and username in the form of 439 + // GetCompleteName returns the full name and username in the form of 440 440 // "Full Name (username)" if full name is not empty, otherwise it returns 441 441 // "username". 442 442 func (u *User) GetCompleteName() string {
+3 -2
modules/git/blame_sha256_test.go
··· 120 120 }, 121 121 } 122 122 123 + objectFormat, err := repo.GetObjectFormat() 124 + assert.NoError(t, err) 123 125 for _, c := range cases { 124 126 commit, err := repo.GetCommit(c.CommitID) 125 127 assert.NoError(t, err) 126 - 127 - blameReader, err := CreateBlameReader(ctx, repo.objectFormat, "./tests/repos/repo6_blame_sha256", commit, "blame.txt", c.Bypass) 128 + blameReader, err := CreateBlameReader(ctx, objectFormat, "./tests/repos/repo6_blame_sha256", commit, "blame.txt", c.Bypass) 128 129 assert.NoError(t, err) 129 130 assert.NotNil(t, blameReader) 130 131 defer blameReader.Close()
+3 -1
modules/git/blame_test.go
··· 118 118 }, 119 119 } 120 120 121 + objectFormat, err := repo.GetObjectFormat() 122 + assert.NoError(t, err) 121 123 for _, c := range cases { 122 124 commit, err := repo.GetCommit(c.CommitID) 123 125 assert.NoError(t, err) 124 126 125 - blameReader, err := CreateBlameReader(ctx, repo.objectFormat, "./tests/repos/repo6_blame", commit, "blame.txt", c.Bypass) 127 + blameReader, err := CreateBlameReader(ctx, objectFormat, "./tests/repos/repo6_blame", commit, "blame.txt", c.Bypass) 126 128 assert.NoError(t, err) 127 129 assert.NotNil(t, blameReader) 128 130 defer blameReader.Close()
+1 -1
modules/git/commit.go
··· 311 311 return c.repo.GetFilesChangedBetween(pastCommit, c.ID.String()) 312 312 } 313 313 314 - // FileChangedSinceCommit Returns true if the file given has changed since the the past commit 314 + // FileChangedSinceCommit Returns true if the file given has changed since the past commit 315 315 // YOU MUST ENSURE THAT pastCommit is a valid commit ID. 316 316 func (c *Commit) FileChangedSinceCommit(filename, pastCommit string) (bool, error) { 317 317 return c.repo.FileChangedBetweenCommits(filename, pastCommit, c.ID.String())
+5 -2
modules/git/commit_sha256_test.go
··· 152 152 commit, err := repo.GetCommit("f004f41359117d319dedd0eaab8c5259ee2263da839dcba33637997458627fdc") 153 153 assert.NoError(t, err) 154 154 155 + objectFormat, err := repo.GetObjectFormat() 156 + assert.NoError(t, err) 157 + 155 158 parentSHA := MustIDFromString("b0ec7af4547047f12d5093e37ef8f1b3b5415ed8ee17894d43a34d7d34212e9c") 156 159 notParentSHA := MustIDFromString("42e334efd04cd36eea6da0599913333c26116e1a537ca76e5b6e4af4dda00236") 157 - assert.Equal(t, repo.objectFormat, parentSHA.Type()) 158 - assert.Equal(t, repo.objectFormat.Name(), "sha256") 160 + assert.Equal(t, objectFormat, parentSHA.Type()) 161 + assert.Equal(t, objectFormat.Name(), "sha256") 159 162 160 163 haz, err := commit.HasPreviousCommit(parentSHA) 161 164 assert.NoError(t, err)
-5
modules/git/repo_base_nogogit.go
··· 71 71 repo.batchWriter, repo.batchReader, repo.batchCancel = CatFileBatch(ctx, repoPath) 72 72 repo.checkWriter, repo.checkReader, repo.checkCancel = CatFileBatchCheck(ctx, repoPath) 73 73 74 - repo.objectFormat, err = repo.GetObjectFormat() 75 - if err != nil { 76 - return nil, err 77 - } 78 - 79 74 return repo, nil 80 75 } 81 76
+6 -1
modules/git/repo_commit.go
··· 246 246 } 247 247 }() 248 248 249 - len := repo.objectFormat.FullLength() 249 + objectFormat, err := repo.GetObjectFormat() 250 + if err != nil { 251 + return nil, err 252 + } 253 + 254 + len := objectFormat.FullLength() 250 255 commits := []*Commit{} 251 256 shaline := make([]byte, len+1) 252 257 for {
+4 -1
modules/git/repo_commit_gogit.go
··· 41 41 42 42 // ConvertToHash returns a Hash object from a potential ID string 43 43 func (repo *Repository) ConvertToGitID(commitID string) (ObjectID, error) { 44 - objectFormat := repo.objectFormat 44 + objectFormat, err := repo.GetObjectFormat() 45 + if err != nil { 46 + return nil, err 47 + } 45 48 if len(commitID) == hash.HexSize && objectFormat.IsValid(commitID) { 46 49 ID, err := NewIDFromString(commitID) 47 50 if err == nil {
+6 -3
modules/git/repo_commit_nogogit.go
··· 132 132 133 133 // ConvertToGitID returns a GitHash object from a potential ID string 134 134 func (repo *Repository) ConvertToGitID(commitID string) (ObjectID, error) { 135 - IDType := repo.objectFormat 136 - if len(commitID) == IDType.FullLength() && IDType.IsValid(commitID) { 135 + objectFormat, err := repo.GetObjectFormat() 136 + if err != nil { 137 + return nil, err 138 + } 139 + if len(commitID) == objectFormat.FullLength() && objectFormat.IsValid(commitID) { 137 140 ID, err := NewIDFromString(commitID) 138 141 if err == nil { 139 142 return ID, nil ··· 142 145 143 146 wr, rd, cancel := repo.CatFileBatchCheck(repo.Ctx) 144 147 defer cancel() 145 - _, err := wr.Write([]byte(commitID + "\n")) 148 + _, err = wr.Write([]byte(commitID + "\n")) 146 149 if err != nil { 147 150 return nil, err 148 151 }
+5 -1
modules/git/repo_compare.go
··· 283 283 // If base is undefined empty SHA (zeros), it only returns the files changed in the head commit 284 284 // If base is the SHA of an empty tree (EmptyTreeSHA), it returns the files changes from the initial commit to the head commit 285 285 func (repo *Repository) GetFilesChangedBetween(base, head string) ([]string, error) { 286 + objectFormat, err := repo.GetObjectFormat() 287 + if err != nil { 288 + return nil, err 289 + } 286 290 cmd := NewCommand(repo.Ctx, "diff-tree", "--name-only", "--root", "--no-commit-id", "-r", "-z") 287 - if base == repo.objectFormat.EmptyObjectID().String() { 291 + if base == objectFormat.EmptyObjectID().String() { 288 292 cmd.AddDynamicArguments(head) 289 293 } else { 290 294 cmd.AddDynamicArguments(base, head)
+6 -3
modules/git/repo_compare_test.go
··· 126 126 assert.NoError(t, err) 127 127 defer repo.Close() 128 128 129 + objectFormat, err := repo.GetObjectFormat() 130 + assert.NoError(t, err) 131 + 129 132 testCases := []struct { 130 133 base, head string 131 134 files []string 132 135 }{ 133 136 { 134 - repo.objectFormat.EmptyObjectID().String(), 137 + objectFormat.EmptyObjectID().String(), 135 138 "95bb4d39648ee7e325106df01a621c530863a653", 136 139 []string{"file1.txt"}, 137 140 }, 138 141 { 139 - repo.objectFormat.EmptyObjectID().String(), 142 + objectFormat.EmptyObjectID().String(), 140 143 "8d92fc957a4d7cfd98bc375f0b7bb189a0d6c9f2", 141 144 []string{"file2.txt"}, 142 145 }, ··· 146 149 []string{"file2.txt"}, 147 150 }, 148 151 { 149 - repo.objectFormat.EmptyTree().String(), 152 + objectFormat.EmptyTree().String(), 150 153 "8d92fc957a4d7cfd98bc375f0b7bb189a0d6c9f2", 151 154 []string{"file1.txt", "file2.txt"}, 152 155 },
+5 -1
modules/git/repo_index.go
··· 94 94 95 95 // RemoveFilesFromIndex removes given filenames from the index - it does not check whether they are present. 96 96 func (repo *Repository) RemoveFilesFromIndex(filenames ...string) error { 97 + objectFormat, err := repo.GetObjectFormat() 98 + if err != nil { 99 + return err 100 + } 97 101 cmd := NewCommand(repo.Ctx, "update-index", "--remove", "-z", "--index-info") 98 102 stdout := new(bytes.Buffer) 99 103 stderr := new(bytes.Buffer) ··· 101 105 for _, file := range filenames { 102 106 if file != "" { 103 107 buffer.WriteString("0 ") 104 - buffer.WriteString(repo.objectFormat.EmptyObjectID().String()) 108 + buffer.WriteString(objectFormat.EmptyObjectID().String()) 105 109 buffer.WriteByte('\t') 106 110 buffer.WriteString(file) 107 111 buffer.WriteByte('\000')
+2 -2
modules/git/repo_tag.go
··· 141 141 break 142 142 } 143 143 144 - tag, err := parseTagRef(repo.objectFormat, ref) 144 + tag, err := parseTagRef(ref) 145 145 if err != nil { 146 146 return nil, 0, fmt.Errorf("GetTagInfos: parse tag: %w", err) 147 147 } ··· 161 161 } 162 162 163 163 // parseTagRef parses a tag from a 'git for-each-ref'-produced reference. 164 - func parseTagRef(objectFormat ObjectFormat, ref map[string]string) (tag *Tag, err error) { 164 + func parseTagRef(ref map[string]string) (tag *Tag, err error) { 165 165 tag = &Tag{ 166 166 Type: ref["objecttype"], 167 167 Name: ref["refname:lstrip=2"],
+1 -2
modules/git/repo_tag_test.go
··· 194 194 } 195 195 196 196 func TestRepository_parseTagRef(t *testing.T) { 197 - sha1 := Sha1ObjectFormat 198 197 tests := []struct { 199 198 name string 200 199 ··· 351 350 for _, test := range tests { 352 351 tc := test // don't close over loop variable 353 352 t.Run(tc.name, func(t *testing.T) { 354 - got, err := parseTagRef(sha1, tc.givenRef) 353 + got, err := parseTagRef(tc.givenRef) 355 354 356 355 if tc.wantErr { 357 356 require.Error(t, err)
+6 -1
modules/git/repo_tree_gogit.go
··· 21 21 22 22 // GetTree find the tree object in the repository. 23 23 func (repo *Repository) GetTree(idStr string) (*Tree, error) { 24 - if len(idStr) != repo.objectFormat.FullLength() { 24 + objectFormat, err := repo.GetObjectFormat() 25 + if err != nil { 26 + return nil, err 27 + } 28 + 29 + if len(idStr) != objectFormat.FullLength() { 25 30 res, _, err := NewCommand(repo.Ctx, "rev-parse", "--verify").AddDynamicArguments(idStr).RunStdString(&RunOpts{Dir: repo.Path}) 26 31 if err != nil { 27 32 return nil, err
+10 -2
modules/git/repo_tree_nogogit.go
··· 51 51 case "tree": 52 52 tree := NewTree(repo, id) 53 53 tree.ResolvedID = id 54 - tree.entries, err = catBatchParseTreeEntries(repo.objectFormat, tree, rd, size) 54 + objectFormat, err := repo.GetObjectFormat() 55 + if err != nil { 56 + return nil, err 57 + } 58 + tree.entries, err = catBatchParseTreeEntries(objectFormat, tree, rd, size) 55 59 if err != nil { 56 60 return nil, err 57 61 } ··· 69 73 70 74 // GetTree find the tree object in the repository. 71 75 func (repo *Repository) GetTree(idStr string) (*Tree, error) { 72 - if len(idStr) != repo.objectFormat.FullLength() { 76 + objectFormat, err := repo.GetObjectFormat() 77 + if err != nil { 78 + return nil, err 79 + } 80 + if len(idStr) != objectFormat.FullLength() { 73 81 res, err := repo.GetRefCommitID(idStr) 74 82 if err != nil { 75 83 return nil, err
+10 -4
modules/git/tree_nogogit.go
··· 77 77 return nil, runErr 78 78 } 79 79 80 - var err error 81 - t.entries, err = parseTreeEntries(t.repo.objectFormat, stdout, t) 80 + objectFormat, err := t.repo.GetObjectFormat() 81 + if err != nil { 82 + return nil, err 83 + } 84 + t.entries, err = parseTreeEntries(objectFormat, stdout, t) 82 85 if err == nil { 83 86 t.entriesParsed = true 84 87 } ··· 101 104 return nil, runErr 102 105 } 103 106 104 - var err error 105 - t.entriesRecursive, err = parseTreeEntries(t.repo.objectFormat, stdout, t) 107 + objectFormat, err := t.repo.GetObjectFormat() 108 + if err != nil { 109 + return nil, err 110 + } 111 + t.entriesRecursive, err = parseTreeEntries(objectFormat, stdout, t) 106 112 if err == nil { 107 113 t.entriesRecursiveParsed = true 108 114 }
+4 -1
modules/graceful/manager.go
··· 233 233 // At the moment the total number of servers (numberOfServersToCreate) are pre-defined as a const before global init, 234 234 // so this function MUST be called if a server is not used. 235 235 func (g *Manager) InformCleanup() { 236 - g.createServerWaitGroup.Done() 236 + g.createServerCond.L.Lock() 237 + defer g.createServerCond.L.Unlock() 238 + g.createdServer++ 239 + g.createServerCond.Signal() 237 240 } 238 241 239 242 // Done allows the manager to be viewed as a context.Context, it returns a channel that is closed when the server is finished terminating
+3 -2
modules/graceful/manager_common.go
··· 42 42 terminateCtxCancel context.CancelFunc 43 43 managerCtxCancel context.CancelFunc 44 44 runningServerWaitGroup sync.WaitGroup 45 - createServerWaitGroup sync.WaitGroup 46 45 terminateWaitGroup sync.WaitGroup 46 + createServerCond sync.Cond 47 + createdServer int 47 48 shutdownRequested chan struct{} 48 49 49 50 toRunAtShutdown []func() ··· 52 53 53 54 func newGracefulManager(ctx context.Context) *Manager { 54 55 manager := &Manager{ctx: ctx, shutdownRequested: make(chan struct{})} 55 - manager.createServerWaitGroup.Add(numberOfServersToCreate) 56 + manager.createServerCond.L = &sync.Mutex{} 56 57 manager.prepare(ctx) 57 58 manager.start() 58 59 return manager
+21 -23
modules/graceful/manager_unix.go
··· 57 57 // Handle clean up of unused provided listeners and delayed start-up 58 58 startupDone := make(chan struct{}) 59 59 go func() { 60 - defer close(startupDone) 61 - // Wait till we're done getting all the listeners and then close the unused ones 62 - func() { 63 - // FIXME: there is a fundamental design problem of the "manager" and the "wait group". 64 - // If nothing has started, the "Wait" just panics: sync: WaitGroup is reused before previous Wait has returned 65 - // There is no clear solution besides a complete rewriting of the "manager" 66 - defer func() { 67 - _ = recover() 68 - }() 69 - g.createServerWaitGroup.Wait() 60 + defer func() { 61 + close(startupDone) 62 + // 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 63 + _ = CloseProvidedListeners() 70 64 }() 71 - // Ignore the error here there's not much we can do with it, they're logged in the CloseProvidedListeners function 72 - _ = CloseProvidedListeners() 73 - g.notify(readyMsg) 65 + // Wait for all servers to be created 66 + g.createServerCond.L.Lock() 67 + for { 68 + if g.createdServer >= numberOfServersToCreate { 69 + g.createServerCond.L.Unlock() 70 + g.notify(readyMsg) 71 + return 72 + } 73 + select { 74 + case <-g.IsShutdown(): 75 + g.createServerCond.L.Unlock() 76 + return 77 + default: 78 + } 79 + g.createServerCond.Wait() 80 + } 74 81 }() 75 82 if setting.StartupTimeout > 0 { 76 83 go func() { ··· 78 85 case <-startupDone: 79 86 return 80 87 case <-g.IsShutdown(): 81 - func() { 82 - // When WaitGroup counter goes negative it will panic - we don't care about this so we can just ignore it. 83 - defer func() { 84 - _ = recover() 85 - }() 86 - // Ensure that the createServerWaitGroup stops waiting 87 - for { 88 - g.createServerWaitGroup.Done() 89 - } 90 - }() 88 + g.createServerCond.Signal() 91 89 return 92 90 case <-time.After(setting.StartupTimeout): 93 91 log.Error("Startup took too long! Shutting down")
+27 -25
modules/graceful/manager_windows.go
··· 149 149 func (g *Manager) awaitServer(limit time.Duration) bool { 150 150 c := make(chan struct{}) 151 151 go func() { 152 - defer close(c) 153 - func() { 154 - // FIXME: there is a fundamental design problem of the "manager" and the "wait group". 155 - // If nothing has started, the "Wait" just panics: sync: WaitGroup is reused before previous Wait has returned 156 - // There is no clear solution besides a complete rewriting of the "manager" 157 - defer func() { 158 - _ = recover() 159 - }() 160 - g.createServerWaitGroup.Wait() 161 - }() 152 + g.createServerCond.L.Lock() 153 + for { 154 + if g.createdServer >= numberOfServersToCreate { 155 + g.createServerCond.L.Unlock() 156 + close(c) 157 + return 158 + } 159 + select { 160 + case <-g.IsShutdown(): 161 + g.createServerCond.L.Unlock() 162 + return 163 + default: 164 + } 165 + g.createServerCond.Wait() 166 + } 162 167 }() 168 + 169 + var tc <-chan time.Time 163 170 if limit > 0 { 164 - select { 165 - case <-c: 166 - return true // completed normally 167 - case <-time.After(limit): 168 - return false // timed out 169 - case <-g.IsShutdown(): 170 - return false 171 - } 172 - } else { 173 - select { 174 - case <-c: 175 - return true // completed normally 176 - case <-g.IsShutdown(): 177 - return false 178 - } 171 + tc = time.After(limit) 172 + } 173 + select { 174 + case <-c: 175 + return true // completed normally 176 + case <-tc: 177 + return false // timed out 178 + case <-g.IsShutdown(): 179 + g.createServerCond.Signal() 180 + return false 179 181 } 180 182 } 181 183
+4 -8
modules/indexer/code/git.go
··· 91 91 return nil, runErr 92 92 } 93 93 94 + objectFormat := git.ObjectFormatFromName(repo.ObjectFormatName) 95 + 94 96 var err error 95 - objectFormat, err := git.GetObjectFormatOfRepo(ctx, repo.RepoPath()) 96 - if err != nil { 97 - return nil, err 98 - } 99 97 changes.Updates, err = parseGitLsTreeOutput(objectFormat, stdout) 100 98 return &changes, err 101 99 } ··· 174 172 return nil, err 175 173 } 176 174 177 - objectFormat, err := git.GetObjectFormatOfRepo(ctx, repo.RepoPath()) 178 - if err != nil { 179 - return nil, err 180 - } 175 + objectFormat := git.ObjectFormatFromName(repo.ObjectFormatName) 176 + 181 177 changes.Updates, err = parseGitLsTreeOutput(objectFormat, lsTreeStdout) 182 178 return &changes, err 183 179 }
+7 -5
modules/indexer/internal/bleve/query.go
··· 4 4 package bleve 5 5 6 6 import ( 7 + "code.gitea.io/gitea/modules/optional" 8 + 7 9 "github.com/blevesearch/bleve/v2" 8 10 "github.com/blevesearch/bleve/v2/search/query" 9 11 ) ··· 39 41 return q 40 42 } 41 43 42 - func NumericRangeInclusiveQuery(min, max *int64, field string) *query.NumericRangeQuery { 44 + func NumericRangeInclusiveQuery(min, max optional.Option[int64], field string) *query.NumericRangeQuery { 43 45 var minF, maxF *float64 44 46 var minI, maxI *bool 45 - if min != nil { 47 + if min.Has() { 46 48 minF = new(float64) 47 - *minF = float64(*min) 49 + *minF = float64(min.Value()) 48 50 minI = new(bool) 49 51 *minI = true 50 52 } 51 - if max != nil { 53 + if max.Has() { 52 54 maxF = new(float64) 53 - *maxF = float64(*max) 55 + *maxF = float64(max.Value()) 54 56 maxI = new(bool) 55 57 *maxI = true 56 58 }
+21 -18
modules/indexer/issues/bleve/bleve.go
··· 224 224 queries = append(queries, bleve.NewDisjunctionQuery(milestoneQueries...)) 225 225 } 226 226 227 - if options.ProjectID != nil { 228 - queries = append(queries, inner_bleve.NumericEqualityQuery(*options.ProjectID, "project_id")) 227 + if options.ProjectID.Has() { 228 + queries = append(queries, inner_bleve.NumericEqualityQuery(options.ProjectID.Value(), "project_id")) 229 229 } 230 - if options.ProjectBoardID != nil { 231 - queries = append(queries, inner_bleve.NumericEqualityQuery(*options.ProjectBoardID, "project_board_id")) 230 + if options.ProjectBoardID.Has() { 231 + queries = append(queries, inner_bleve.NumericEqualityQuery(options.ProjectBoardID.Value(), "project_board_id")) 232 232 } 233 233 234 - if options.PosterID != nil { 235 - queries = append(queries, inner_bleve.NumericEqualityQuery(*options.PosterID, "poster_id")) 234 + if options.PosterID.Has() { 235 + queries = append(queries, inner_bleve.NumericEqualityQuery(options.PosterID.Value(), "poster_id")) 236 236 } 237 237 238 - if options.AssigneeID != nil { 239 - queries = append(queries, inner_bleve.NumericEqualityQuery(*options.AssigneeID, "assignee_id")) 238 + if options.AssigneeID.Has() { 239 + queries = append(queries, inner_bleve.NumericEqualityQuery(options.AssigneeID.Value(), "assignee_id")) 240 240 } 241 241 242 - if options.MentionID != nil { 243 - queries = append(queries, inner_bleve.NumericEqualityQuery(*options.MentionID, "mention_ids")) 242 + if options.MentionID.Has() { 243 + queries = append(queries, inner_bleve.NumericEqualityQuery(options.MentionID.Value(), "mention_ids")) 244 244 } 245 245 246 - if options.ReviewedID != nil { 247 - queries = append(queries, inner_bleve.NumericEqualityQuery(*options.ReviewedID, "reviewed_ids")) 246 + if options.ReviewedID.Has() { 247 + queries = append(queries, inner_bleve.NumericEqualityQuery(options.ReviewedID.Value(), "reviewed_ids")) 248 248 } 249 - if options.ReviewRequestedID != nil { 250 - queries = append(queries, inner_bleve.NumericEqualityQuery(*options.ReviewRequestedID, "review_requested_ids")) 249 + if options.ReviewRequestedID.Has() { 250 + queries = append(queries, inner_bleve.NumericEqualityQuery(options.ReviewRequestedID.Value(), "review_requested_ids")) 251 251 } 252 252 253 - if options.SubscriberID != nil { 254 - queries = append(queries, inner_bleve.NumericEqualityQuery(*options.SubscriberID, "subscriber_ids")) 253 + if options.SubscriberID.Has() { 254 + queries = append(queries, inner_bleve.NumericEqualityQuery(options.SubscriberID.Value(), "subscriber_ids")) 255 255 } 256 256 257 - if options.UpdatedAfterUnix != nil || options.UpdatedBeforeUnix != nil { 258 - queries = append(queries, inner_bleve.NumericRangeInclusiveQuery(options.UpdatedAfterUnix, options.UpdatedBeforeUnix, "updated_unix")) 257 + if options.UpdatedAfterUnix.Has() || options.UpdatedBeforeUnix.Has() { 258 + queries = append(queries, inner_bleve.NumericRangeInclusiveQuery( 259 + options.UpdatedAfterUnix, 260 + options.UpdatedBeforeUnix, 261 + "updated_unix")) 259 262 } 260 263 261 264 var indexerQuery query.Query = bleve.NewConjunctionQuery(queries...)
+14 -18
modules/indexer/issues/db/options.go
··· 15 15 ) 16 16 17 17 func ToDBOptions(ctx context.Context, options *internal.SearchOptions) (*issue_model.IssuesOptions, error) { 18 - // See the comment of issues_model.SearchOptions for the reason why we need to convert 19 - convertID := func(id *int64) int64 { 20 - if id == nil { 21 - return 0 22 - } 23 - if *id == 0 { 24 - return db.NoConditionID 25 - } 26 - return *id 27 - } 28 - convertInt64 := func(i *int64) int64 { 29 - if i == nil { 30 - return 0 31 - } 32 - return *i 33 - } 34 18 var sortType string 35 19 switch options.SortBy { 36 20 case internal.SortByCreatedAsc: ··· 53 37 sortType = "newest" 54 38 } 55 39 40 + // See the comment of issues_model.SearchOptions for the reason why we need to convert 41 + convertID := func(id optional.Option[int64]) int64 { 42 + if !id.Has() { 43 + return 0 44 + } 45 + value := id.Value() 46 + if value == 0 { 47 + return db.NoConditionID 48 + } 49 + return value 50 + } 51 + 56 52 opts := &issue_model.IssuesOptions{ 57 53 Paginator: options.Paginator, 58 54 RepoIDs: options.RepoIDs, ··· 73 69 IncludeMilestones: nil, 74 70 SortType: sortType, 75 71 IssueIDs: nil, 76 - UpdatedAfterUnix: convertInt64(options.UpdatedAfterUnix), 77 - UpdatedBeforeUnix: convertInt64(options.UpdatedBeforeUnix), 72 + UpdatedAfterUnix: options.UpdatedAfterUnix.Value(), 73 + UpdatedBeforeUnix: options.UpdatedBeforeUnix.Value(), 78 74 PriorityRepoID: 0, 79 75 IsArchived: optional.None[bool](), 80 76 Org: nil,
+6 -6
modules/indexer/issues/dboptions.go
··· 6 6 import ( 7 7 "code.gitea.io/gitea/models/db" 8 8 issues_model "code.gitea.io/gitea/models/issues" 9 + "code.gitea.io/gitea/modules/optional" 9 10 ) 10 11 11 12 func ToSearchOptions(keyword string, opts *issues_model.IssuesOptions) *SearchOptions { ··· 38 39 } 39 40 40 41 // See the comment of issues_model.SearchOptions for the reason why we need to convert 41 - convertID := func(id int64) *int64 { 42 + convertID := func(id int64) optional.Option[int64] { 42 43 if id > 0 { 43 - return &id 44 + return optional.Some(id) 44 45 } 45 46 if id == db.NoConditionID { 46 - var zero int64 47 - return &zero 47 + return optional.None[int64]() 48 48 } 49 49 return nil 50 50 } ··· 59 59 searchOpt.SubscriberID = convertID(opts.SubscriberID) 60 60 61 61 if opts.UpdatedAfterUnix > 0 { 62 - searchOpt.UpdatedAfterUnix = &opts.UpdatedAfterUnix 62 + searchOpt.UpdatedAfterUnix = optional.Some(opts.UpdatedAfterUnix) 63 63 } 64 64 if opts.UpdatedBeforeUnix > 0 { 65 - searchOpt.UpdatedBeforeUnix = &opts.UpdatedBeforeUnix 65 + searchOpt.UpdatedBeforeUnix = optional.Some(opts.UpdatedBeforeUnix) 66 66 } 67 67 68 68 searchOpt.Paginator = opts.Paginator
+21 -21
modules/indexer/issues/elasticsearch/elasticsearch.go
··· 195 195 query.Must(elastic.NewTermsQuery("milestone_id", toAnySlice(options.MilestoneIDs)...)) 196 196 } 197 197 198 - if options.ProjectID != nil { 199 - query.Must(elastic.NewTermQuery("project_id", *options.ProjectID)) 198 + if options.ProjectID.Has() { 199 + query.Must(elastic.NewTermQuery("project_id", options.ProjectID.Value())) 200 200 } 201 - if options.ProjectBoardID != nil { 202 - query.Must(elastic.NewTermQuery("project_board_id", *options.ProjectBoardID)) 201 + if options.ProjectBoardID.Has() { 202 + query.Must(elastic.NewTermQuery("project_board_id", options.ProjectBoardID.Value())) 203 203 } 204 204 205 - if options.PosterID != nil { 206 - query.Must(elastic.NewTermQuery("poster_id", *options.PosterID)) 205 + if options.PosterID.Has() { 206 + query.Must(elastic.NewTermQuery("poster_id", options.PosterID.Value())) 207 207 } 208 208 209 - if options.AssigneeID != nil { 210 - query.Must(elastic.NewTermQuery("assignee_id", *options.AssigneeID)) 209 + if options.AssigneeID.Has() { 210 + query.Must(elastic.NewTermQuery("assignee_id", options.AssigneeID.Value())) 211 211 } 212 212 213 - if options.MentionID != nil { 214 - query.Must(elastic.NewTermQuery("mention_ids", *options.MentionID)) 213 + if options.MentionID.Has() { 214 + query.Must(elastic.NewTermQuery("mention_ids", options.MentionID.Value())) 215 215 } 216 216 217 - if options.ReviewedID != nil { 218 - query.Must(elastic.NewTermQuery("reviewed_ids", *options.ReviewedID)) 217 + if options.ReviewedID.Has() { 218 + query.Must(elastic.NewTermQuery("reviewed_ids", options.ReviewedID.Value())) 219 219 } 220 - if options.ReviewRequestedID != nil { 221 - query.Must(elastic.NewTermQuery("review_requested_ids", *options.ReviewRequestedID)) 220 + if options.ReviewRequestedID.Has() { 221 + query.Must(elastic.NewTermQuery("review_requested_ids", options.ReviewRequestedID.Value())) 222 222 } 223 223 224 - if options.SubscriberID != nil { 225 - query.Must(elastic.NewTermQuery("subscriber_ids", *options.SubscriberID)) 224 + if options.SubscriberID.Has() { 225 + query.Must(elastic.NewTermQuery("subscriber_ids", options.SubscriberID.Value())) 226 226 } 227 227 228 - if options.UpdatedAfterUnix != nil || options.UpdatedBeforeUnix != nil { 228 + if options.UpdatedAfterUnix.Has() || options.UpdatedBeforeUnix.Has() { 229 229 q := elastic.NewRangeQuery("updated_unix") 230 - if options.UpdatedAfterUnix != nil { 231 - q.Gte(*options.UpdatedAfterUnix) 230 + if options.UpdatedAfterUnix.Has() { 231 + q.Gte(options.UpdatedAfterUnix.Value()) 232 232 } 233 - if options.UpdatedBeforeUnix != nil { 234 - q.Lte(*options.UpdatedBeforeUnix) 233 + if options.UpdatedBeforeUnix.Has() { 234 + q.Lte(options.UpdatedBeforeUnix.Value()) 235 235 } 236 236 query.Must(q) 237 237 }
+28 -37
modules/indexer/issues/indexer_test.go
··· 134 134 } 135 135 136 136 func searchIssueByID(t *testing.T) { 137 - int64Pointer := func(x int64) *int64 { 138 - return &x 139 - } 140 137 tests := []struct { 141 138 opts SearchOptions 142 139 expectedIDs []int64 143 140 }{ 144 141 { 145 - SearchOptions{ 146 - PosterID: int64Pointer(1), 142 + opts: SearchOptions{ 143 + PosterID: optional.Some(int64(1)), 147 144 }, 148 - []int64{11, 6, 3, 2, 1}, 145 + expectedIDs: []int64{11, 6, 3, 2, 1}, 149 146 }, 150 147 { 151 - SearchOptions{ 152 - AssigneeID: int64Pointer(1), 148 + opts: SearchOptions{ 149 + AssigneeID: optional.Some(int64(1)), 153 150 }, 154 - []int64{6, 1}, 151 + expectedIDs: []int64{6, 1}, 155 152 }, 156 153 { 157 - SearchOptions{ 158 - MentionID: int64Pointer(4), 154 + opts: SearchOptions{ 155 + MentionID: optional.Some(int64(4)), 159 156 }, 160 - []int64{1}, 157 + expectedIDs: []int64{1}, 161 158 }, 162 159 { 163 - SearchOptions{ 164 - ReviewedID: int64Pointer(1), 160 + opts: SearchOptions{ 161 + ReviewedID: optional.Some(int64(1)), 165 162 }, 166 - []int64{}, 163 + expectedIDs: []int64{}, 167 164 }, 168 165 { 169 - SearchOptions{ 170 - ReviewRequestedID: int64Pointer(1), 166 + opts: SearchOptions{ 167 + ReviewRequestedID: optional.Some(int64(1)), 171 168 }, 172 - []int64{12}, 169 + expectedIDs: []int64{12}, 173 170 }, 174 171 { 175 - SearchOptions{ 176 - SubscriberID: int64Pointer(1), 172 + opts: SearchOptions{ 173 + SubscriberID: optional.Some(int64(1)), 177 174 }, 178 - []int64{11, 6, 5, 3, 2, 1}, 175 + expectedIDs: []int64{11, 6, 5, 3, 2, 1}, 179 176 }, 180 177 { 181 178 // issue 20 request user 15 and team 5 which user 15 belongs to 182 179 // the review request number of issue 20 should be 1 183 - SearchOptions{ 184 - ReviewRequestedID: int64Pointer(15), 180 + opts: SearchOptions{ 181 + ReviewRequestedID: optional.Some(int64(15)), 185 182 }, 186 - []int64{12, 20}, 183 + expectedIDs: []int64{12, 20}, 187 184 }, 188 185 { 189 186 // user 20 approved the issue 20, so return nothing 190 - SearchOptions{ 191 - ReviewRequestedID: int64Pointer(20), 187 + opts: SearchOptions{ 188 + ReviewRequestedID: optional.Some(int64(20)), 192 189 }, 193 - []int64{}, 190 + expectedIDs: []int64{}, 194 191 }, 195 192 } 196 193 ··· 318 315 } 319 316 320 317 func searchIssueByTime(t *testing.T) { 321 - int64Pointer := func(i int64) *int64 { 322 - return &i 323 - } 324 318 tests := []struct { 325 319 opts SearchOptions 326 320 expectedIDs []int64 327 321 }{ 328 322 { 329 323 SearchOptions{ 330 - UpdatedAfterUnix: int64Pointer(0), 324 + UpdatedAfterUnix: optional.Some(int64(0)), 331 325 }, 332 326 []int64{22, 21, 17, 16, 15, 14, 13, 12, 11, 20, 6, 5, 19, 18, 10, 7, 4, 9, 8, 3, 2, 1}, 333 327 }, ··· 363 357 } 364 358 365 359 func searchIssueInProject(t *testing.T) { 366 - int64Pointer := func(i int64) *int64 { 367 - return &i 368 - } 369 360 tests := []struct { 370 361 opts SearchOptions 371 362 expectedIDs []int64 372 363 }{ 373 364 { 374 365 SearchOptions{ 375 - ProjectID: int64Pointer(1), 366 + ProjectID: optional.Some(int64(1)), 376 367 }, 377 368 []int64{5, 3, 2, 1}, 378 369 }, 379 370 { 380 371 SearchOptions{ 381 - ProjectBoardID: int64Pointer(1), 372 + ProjectBoardID: optional.Some(int64(1)), 382 373 }, 383 374 []int64{1}, 384 375 }, 385 376 { 386 377 SearchOptions{ 387 - ProjectBoardID: int64Pointer(0), // issue with in default board 378 + ProjectBoardID: optional.Some(int64(0)), // issue with in default board 388 379 }, 389 380 []int64{2}, 390 381 },
+10 -10
modules/indexer/issues/internal/model.go
··· 89 89 90 90 MilestoneIDs []int64 // milestones the issues have 91 91 92 - ProjectID *int64 // project the issues belong to 93 - ProjectBoardID *int64 // project board the issues belong to 92 + ProjectID optional.Option[int64] // project the issues belong to 93 + ProjectBoardID optional.Option[int64] // project board the issues belong to 94 94 95 - PosterID *int64 // poster of the issues 95 + PosterID optional.Option[int64] // poster of the issues 96 96 97 - AssigneeID *int64 // assignee of the issues, zero means no assignee 97 + AssigneeID optional.Option[int64] // assignee of the issues, zero means no assignee 98 98 99 - MentionID *int64 // mentioned user of the issues 99 + MentionID optional.Option[int64] // mentioned user of the issues 100 100 101 - ReviewedID *int64 // reviewer of the issues 102 - ReviewRequestedID *int64 // requested reviewer of the issues 101 + ReviewedID optional.Option[int64] // reviewer of the issues 102 + ReviewRequestedID optional.Option[int64] // requested reviewer of the issues 103 103 104 - SubscriberID *int64 // subscriber of the issues 104 + SubscriberID optional.Option[int64] // subscriber of the issues 105 105 106 - UpdatedAfterUnix *int64 107 - UpdatedBeforeUnix *int64 106 + UpdatedAfterUnix optional.Option[int64] 107 + UpdatedBeforeUnix optional.Option[int64] 108 108 109 109 db.Paginator 110 110
+13 -52
modules/indexer/issues/internal/tests/tests.go
··· 300 300 Paginator: &db.ListOptions{ 301 301 PageSize: 5, 302 302 }, 303 - ProjectID: func() *int64 { 304 - id := int64(1) 305 - return &id 306 - }(), 303 + ProjectID: optional.Some(int64(1)), 307 304 }, 308 305 Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { 309 306 assert.Equal(t, 5, len(result.Hits)) ··· 321 318 Paginator: &db.ListOptions{ 322 319 PageSize: 5, 323 320 }, 324 - ProjectID: func() *int64 { 325 - id := int64(0) 326 - return &id 327 - }(), 321 + ProjectID: optional.Some(int64(0)), 328 322 }, 329 323 Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { 330 324 assert.Equal(t, 5, len(result.Hits)) ··· 342 336 Paginator: &db.ListOptions{ 343 337 PageSize: 5, 344 338 }, 345 - ProjectBoardID: func() *int64 { 346 - id := int64(1) 347 - return &id 348 - }(), 339 + ProjectBoardID: optional.Some(int64(1)), 349 340 }, 350 341 Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { 351 342 assert.Equal(t, 5, len(result.Hits)) ··· 363 354 Paginator: &db.ListOptions{ 364 355 PageSize: 5, 365 356 }, 366 - ProjectBoardID: func() *int64 { 367 - id := int64(0) 368 - return &id 369 - }(), 357 + ProjectBoardID: optional.Some(int64(0)), 370 358 }, 371 359 Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { 372 360 assert.Equal(t, 5, len(result.Hits)) ··· 384 372 Paginator: &db.ListOptions{ 385 373 PageSize: 5, 386 374 }, 387 - PosterID: func() *int64 { 388 - id := int64(1) 389 - return &id 390 - }(), 375 + PosterID: optional.Some(int64(1)), 391 376 }, 392 377 Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { 393 378 assert.Equal(t, 5, len(result.Hits)) ··· 405 390 Paginator: &db.ListOptions{ 406 391 PageSize: 5, 407 392 }, 408 - AssigneeID: func() *int64 { 409 - id := int64(1) 410 - return &id 411 - }(), 393 + AssigneeID: optional.Some(int64(1)), 412 394 }, 413 395 Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { 414 396 assert.Equal(t, 5, len(result.Hits)) ··· 426 408 Paginator: &db.ListOptions{ 427 409 PageSize: 5, 428 410 }, 429 - AssigneeID: func() *int64 { 430 - id := int64(0) 431 - return &id 432 - }(), 411 + AssigneeID: optional.Some(int64(0)), 433 412 }, 434 413 Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { 435 414 assert.Equal(t, 5, len(result.Hits)) ··· 447 426 Paginator: &db.ListOptions{ 448 427 PageSize: 5, 449 428 }, 450 - MentionID: func() *int64 { 451 - id := int64(1) 452 - return &id 453 - }(), 429 + MentionID: optional.Some(int64(1)), 454 430 }, 455 431 Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { 456 432 assert.Equal(t, 5, len(result.Hits)) ··· 468 444 Paginator: &db.ListOptions{ 469 445 PageSize: 5, 470 446 }, 471 - ReviewedID: func() *int64 { 472 - id := int64(1) 473 - return &id 474 - }(), 447 + ReviewedID: optional.Some(int64(1)), 475 448 }, 476 449 Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { 477 450 assert.Equal(t, 5, len(result.Hits)) ··· 489 462 Paginator: &db.ListOptions{ 490 463 PageSize: 5, 491 464 }, 492 - ReviewRequestedID: func() *int64 { 493 - id := int64(1) 494 - return &id 495 - }(), 465 + ReviewRequestedID: optional.Some(int64(1)), 496 466 }, 497 467 Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { 498 468 assert.Equal(t, 5, len(result.Hits)) ··· 510 480 Paginator: &db.ListOptions{ 511 481 PageSize: 5, 512 482 }, 513 - SubscriberID: func() *int64 { 514 - id := int64(1) 515 - return &id 516 - }(), 483 + SubscriberID: optional.Some(int64(1)), 517 484 }, 518 485 Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { 519 486 assert.Equal(t, 5, len(result.Hits)) ··· 531 498 Paginator: &db.ListOptions{ 532 499 PageSize: 5, 533 500 }, 534 - UpdatedAfterUnix: func() *int64 { 535 - var t int64 = 20 536 - return &t 537 - }(), 538 - UpdatedBeforeUnix: func() *int64 { 539 - var t int64 = 30 540 - return &t 541 - }(), 501 + UpdatedAfterUnix: optional.Some(int64(20)), 502 + UpdatedBeforeUnix: optional.Some(int64(30)), 542 503 }, 543 504 Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { 544 505 assert.Equal(t, 5, len(result.Hits))
+45 -78
modules/indexer/issues/meilisearch/meilisearch.go
··· 6 6 import ( 7 7 "context" 8 8 "errors" 9 + "fmt" 9 10 "strconv" 10 11 "strings" 11 12 ··· 170 171 query.And(inner_meilisearch.NewFilterIn("milestone_id", options.MilestoneIDs...)) 171 172 } 172 173 173 - if options.ProjectID != nil { 174 - query.And(inner_meilisearch.NewFilterEq("project_id", *options.ProjectID)) 174 + if options.ProjectID.Has() { 175 + query.And(inner_meilisearch.NewFilterEq("project_id", options.ProjectID.Value())) 175 176 } 176 - if options.ProjectBoardID != nil { 177 - query.And(inner_meilisearch.NewFilterEq("project_board_id", *options.ProjectBoardID)) 177 + if options.ProjectBoardID.Has() { 178 + query.And(inner_meilisearch.NewFilterEq("project_board_id", options.ProjectBoardID.Value())) 178 179 } 179 180 180 - if options.PosterID != nil { 181 - query.And(inner_meilisearch.NewFilterEq("poster_id", *options.PosterID)) 181 + if options.PosterID.Has() { 182 + query.And(inner_meilisearch.NewFilterEq("poster_id", options.PosterID.Value())) 182 183 } 183 184 184 - if options.AssigneeID != nil { 185 - query.And(inner_meilisearch.NewFilterEq("assignee_id", *options.AssigneeID)) 185 + if options.AssigneeID.Has() { 186 + query.And(inner_meilisearch.NewFilterEq("assignee_id", options.AssigneeID.Value())) 186 187 } 187 188 188 - if options.MentionID != nil { 189 - query.And(inner_meilisearch.NewFilterEq("mention_ids", *options.MentionID)) 189 + if options.MentionID.Has() { 190 + query.And(inner_meilisearch.NewFilterEq("mention_ids", options.MentionID.Value())) 190 191 } 191 192 192 - if options.ReviewedID != nil { 193 - query.And(inner_meilisearch.NewFilterEq("reviewed_ids", *options.ReviewedID)) 193 + if options.ReviewedID.Has() { 194 + query.And(inner_meilisearch.NewFilterEq("reviewed_ids", options.ReviewedID.Value())) 194 195 } 195 - if options.ReviewRequestedID != nil { 196 - query.And(inner_meilisearch.NewFilterEq("review_requested_ids", *options.ReviewRequestedID)) 196 + if options.ReviewRequestedID.Has() { 197 + query.And(inner_meilisearch.NewFilterEq("review_requested_ids", options.ReviewRequestedID.Value())) 197 198 } 198 199 199 - if options.SubscriberID != nil { 200 - query.And(inner_meilisearch.NewFilterEq("subscriber_ids", *options.SubscriberID)) 200 + if options.SubscriberID.Has() { 201 + query.And(inner_meilisearch.NewFilterEq("subscriber_ids", options.SubscriberID.Value())) 201 202 } 202 203 203 - if options.UpdatedAfterUnix != nil { 204 - query.And(inner_meilisearch.NewFilterGte("updated_unix", *options.UpdatedAfterUnix)) 204 + if options.UpdatedAfterUnix.Has() { 205 + query.And(inner_meilisearch.NewFilterGte("updated_unix", options.UpdatedAfterUnix.Value())) 205 206 } 206 - if options.UpdatedBeforeUnix != nil { 207 - query.And(inner_meilisearch.NewFilterLte("updated_unix", *options.UpdatedBeforeUnix)) 207 + if options.UpdatedBeforeUnix.Has() { 208 + query.And(inner_meilisearch.NewFilterLte("updated_unix", options.UpdatedBeforeUnix.Value())) 208 209 } 209 210 210 211 if options.SortBy == "" { ··· 217 218 218 219 skip, limit := indexer_internal.ParsePaginator(options.Paginator, maxTotalHits) 219 220 220 - searchRes, err := b.inner.Client.Index(b.inner.VersionedIndexName()).Search(options.Keyword, &meilisearch.SearchRequest{ 221 + keyword := options.Keyword 222 + if !options.IsFuzzyKeyword { 223 + // to make it non fuzzy ("typo tolerance" in meilisearch terms), we have to quote the keyword(s) 224 + // https://www.meilisearch.com/docs/reference/api/search#phrase-search 225 + keyword = doubleQuoteKeyword(keyword) 226 + } 227 + 228 + searchRes, err := b.inner.Client.Index(b.inner.VersionedIndexName()).Search(keyword, &meilisearch.SearchRequest{ 221 229 Filter: query.Statement(), 222 230 Limit: int64(limit), 223 231 Offset: int64(skip), ··· 228 236 return nil, err 229 237 } 230 238 231 - hits, err := nonFuzzyWorkaround(searchRes, options.Keyword, options.IsFuzzyKeyword) 239 + hits, err := convertHits(searchRes) 232 240 if err != nil { 233 241 return nil, err 234 242 } ··· 247 255 return field + ":asc" 248 256 } 249 257 250 - // nonFuzzyWorkaround is needed as meilisearch does not have an exact search 251 - // and you can only change "typo tolerance" per index. So we have to post-filter the results 252 - // https://www.meilisearch.com/docs/learn/configuration/typo_tolerance#configuring-typo-tolerance 253 - // TODO: remove once https://github.com/orgs/meilisearch/discussions/377 is addressed 254 - func nonFuzzyWorkaround(searchRes *meilisearch.SearchResponse, keyword string, isFuzzy bool) ([]internal.Match, error) { 258 + func doubleQuoteKeyword(k string) string { 259 + kp := strings.Split(k, " ") 260 + parts := 0 261 + for i := range kp { 262 + part := strings.Trim(kp[i], "\"") 263 + if part != "" { 264 + kp[parts] = fmt.Sprintf(`"%s"`, part) 265 + parts++ 266 + } 267 + } 268 + return strings.Join(kp[:parts], " ") 269 + } 270 + 271 + func convertHits(searchRes *meilisearch.SearchResponse) ([]internal.Match, error) { 255 272 hits := make([]internal.Match, 0, len(searchRes.Hits)) 256 273 for _, hit := range searchRes.Hits { 257 274 hit, ok := hit.(map[string]any) ··· 259 276 return nil, ErrMalformedResponse 260 277 } 261 278 262 - if !isFuzzy { 263 - keyword = strings.ToLower(keyword) 264 - 265 - // declare a anon func to check if the title, content or at least one comment contains the keyword 266 - found, err := func() (bool, error) { 267 - // check if title match first 268 - title, ok := hit["title"].(string) 269 - if !ok { 270 - return false, ErrMalformedResponse 271 - } else if strings.Contains(strings.ToLower(title), keyword) { 272 - return true, nil 273 - } 274 - 275 - // check if content has a match 276 - content, ok := hit["content"].(string) 277 - if !ok { 278 - return false, ErrMalformedResponse 279 - } else if strings.Contains(strings.ToLower(content), keyword) { 280 - return true, nil 281 - } 282 - 283 - // now check for each comment if one has a match 284 - // so we first try to cast and skip if there are no comments 285 - comments, ok := hit["comments"].([]any) 286 - if !ok { 287 - return false, ErrMalformedResponse 288 - } else if len(comments) == 0 { 289 - return false, nil 290 - } 291 - 292 - // now we iterate over all and report as soon as we detect one match 293 - for i := range comments { 294 - comment, ok := comments[i].(string) 295 - if !ok { 296 - return false, ErrMalformedResponse 297 - } 298 - if strings.Contains(strings.ToLower(comment), keyword) { 299 - return true, nil 300 - } 301 - } 302 - 303 - // we got no match 304 - return false, nil 305 - }() 306 - 307 - if err != nil { 308 - return nil, err 309 - } else if !found { 310 - continue 311 - } 312 - } 313 279 issueID, ok := hit["id"].(float64) 314 280 if !ok { 315 281 return nil, ErrMalformedResponse 316 282 } 283 + 317 284 hits = append(hits, internal.Match{ 318 285 ID: int64(issueID), 319 286 })
+12 -12
modules/indexer/issues/meilisearch/meilisearch_test.go
··· 54 54 tests.TestIndexer(t, indexer) 55 55 } 56 56 57 - func TestNonFuzzyWorkaround(t *testing.T) { 58 - // get unexpected return 59 - _, err := nonFuzzyWorkaround(&meilisearch.SearchResponse{ 57 + func TestConvertHits(t *testing.T) { 58 + _, err := convertHits(&meilisearch.SearchResponse{ 60 59 Hits: []any{"aa", "bb", "cc", "dd"}, 61 - }, "bowling", false) 60 + }) 62 61 assert.ErrorIs(t, err, ErrMalformedResponse) 63 62 64 63 validResponse := &meilisearch.SearchResponse{ ··· 83 82 }, 84 83 }, 85 84 } 86 - 87 - // nonFuzzy 88 - hits, err := nonFuzzyWorkaround(validResponse, "bowling", false) 85 + hits, err := convertHits(validResponse) 89 86 assert.NoError(t, err) 90 - assert.EqualValues(t, []internal.Match{{ID: 11}, {ID: 22}}, hits) 87 + assert.EqualValues(t, []internal.Match{{ID: 11}, {ID: 22}, {ID: 33}}, hits) 88 + } 91 89 92 - // fuzzy 93 - hits, err = nonFuzzyWorkaround(validResponse, "bowling", true) 94 - assert.NoError(t, err) 95 - assert.EqualValues(t, []internal.Match{{ID: 11}, {ID: 22}, {ID: 33}}, hits) 90 + func TestDoubleQuoteKeyword(t *testing.T) { 91 + assert.EqualValues(t, "", doubleQuoteKeyword("")) 92 + assert.EqualValues(t, `"a" "b" "c"`, doubleQuoteKeyword("a b c")) 93 + assert.EqualValues(t, `"a" "d" "g"`, doubleQuoteKeyword("a d g")) 94 + assert.EqualValues(t, `"a" "d" "g"`, doubleQuoteKeyword("a d g")) 95 + assert.EqualValues(t, `"a" "d" "g"`, doubleQuoteKeyword(`a "" "d" """g`)) 96 96 }
+45 -12
modules/markup/csv/csv.go
··· 77 77 } 78 78 79 79 // Render implements markup.Renderer 80 - func (Renderer) Render(ctx *markup.RenderContext, input io.Reader, output io.Writer) error { 80 + func (r Renderer) Render(ctx *markup.RenderContext, input io.Reader, output io.Writer) error { 81 81 tmpBlock := bufio.NewWriter(output) 82 + maxSize := setting.UI.CSV.MaxFileSize 82 83 83 - // FIXME: don't read all to memory 84 - rawBytes, err := io.ReadAll(input) 84 + if maxSize == 0 { 85 + return r.tableRender(ctx, input, tmpBlock) 86 + } 87 + 88 + rawBytes, err := io.ReadAll(io.LimitReader(input, maxSize+1)) 89 + if err != nil { 90 + return err 91 + } 92 + 93 + if int64(len(rawBytes)) <= maxSize { 94 + return r.tableRender(ctx, bytes.NewReader(rawBytes), tmpBlock) 95 + } 96 + return r.fallbackRender(io.MultiReader(bytes.NewReader(rawBytes), input), tmpBlock) 97 + } 98 + 99 + func (Renderer) fallbackRender(input io.Reader, tmpBlock *bufio.Writer) error { 100 + _, err := tmpBlock.WriteString("<pre>") 85 101 if err != nil { 86 102 return err 87 103 } 88 104 89 - if setting.UI.CSV.MaxFileSize != 0 && setting.UI.CSV.MaxFileSize < int64(len(rawBytes)) { 90 - if _, err := tmpBlock.WriteString("<pre>"); err != nil { 91 - return err 92 - } 93 - if _, err := tmpBlock.WriteString(html.EscapeString(string(rawBytes))); err != nil { 94 - return err 105 + scan := bufio.NewScanner(input) 106 + scan.Split(bufio.ScanRunes) 107 + for scan.Scan() { 108 + switch scan.Text() { 109 + case `&`: 110 + _, err = tmpBlock.WriteString("&amp;") 111 + case `'`: 112 + _, err = tmpBlock.WriteString("&#39;") // "&#39;" is shorter than "&apos;" and apos was not in HTML until HTML5. 113 + case `<`: 114 + _, err = tmpBlock.WriteString("&lt;") 115 + case `>`: 116 + _, err = tmpBlock.WriteString("&gt;") 117 + case `"`: 118 + _, err = tmpBlock.WriteString("&#34;") // "&#34;" is shorter than "&quot;". 119 + default: 120 + _, err = tmpBlock.Write(scan.Bytes()) 95 121 } 96 - if _, err := tmpBlock.WriteString("</pre>"); err != nil { 122 + if err != nil { 97 123 return err 98 124 } 99 - return tmpBlock.Flush() 100 125 } 101 126 102 - rd, err := csv.CreateReaderAndDetermineDelimiter(ctx, bytes.NewReader(rawBytes)) 127 + _, err = tmpBlock.WriteString("</pre>") 128 + if err != nil { 129 + return err 130 + } 131 + return tmpBlock.Flush() 132 + } 133 + 134 + func (Renderer) tableRender(ctx *markup.RenderContext, input io.Reader, tmpBlock *bufio.Writer) error { 135 + rd, err := csv.CreateReaderAndDetermineDelimiter(ctx, input) 103 136 if err != nil { 104 137 return err 105 138 }
+10
modules/markup/csv/csv_test.go
··· 4 4 package markup 5 5 6 6 import ( 7 + "bufio" 8 + "bytes" 7 9 "strings" 8 10 "testing" 9 11 ··· 29 31 assert.NoError(t, err) 30 32 assert.EqualValues(t, v, buf.String()) 31 33 } 34 + 35 + t.Run("fallbackRender", func(t *testing.T) { 36 + var buf bytes.Buffer 37 + err := render.fallbackRender(strings.NewReader("1,<a>\n2,<b>"), bufio.NewWriter(&buf)) 38 + assert.NoError(t, err) 39 + want := "<pre>1,&lt;a&gt;\n2,&lt;b&gt;</pre>" 40 + assert.Equal(t, want, buf.String()) 41 + }) 32 42 }
+6 -6
modules/markup/html.go
··· 609 609 if ok && strings.Contains(mention, "/") { 610 610 mentionOrgAndTeam := strings.Split(mention, "/") 611 611 if mentionOrgAndTeam[0][1:] == ctx.Metas["org"] && strings.Contains(teams, ","+strings.ToLower(mentionOrgAndTeam[1])+",") { 612 - replaceContent(node, loc.Start, loc.End, createLink(util.URLJoin(setting.AppURL, "org", ctx.Metas["org"], "teams", mentionOrgAndTeam[1]), mention, "mention")) 612 + replaceContent(node, loc.Start, loc.End, createLink(util.URLJoin(ctx.Links.Prefix(), "org", ctx.Metas["org"], "teams", mentionOrgAndTeam[1]), mention, "mention")) 613 613 node = node.NextSibling.NextSibling 614 614 start = 0 615 615 continue ··· 620 620 mentionedUsername := mention[1:] 621 621 622 622 if DefaultProcessorHelper.IsUsernameMentionable != nil && DefaultProcessorHelper.IsUsernameMentionable(ctx.Ctx, mentionedUsername) { 623 - replaceContent(node, loc.Start, loc.End, createLink(util.URLJoin(setting.AppURL, mentionedUsername), mention, "mention")) 623 + replaceContent(node, loc.Start, loc.End, createLink(util.URLJoin(ctx.Links.Prefix(), mentionedUsername), mention, "mention")) 624 624 node = node.NextSibling.NextSibling 625 625 } else { 626 626 node = node.NextSibling ··· 898 898 path = "pulls" 899 899 } 900 900 if ref.Owner == "" { 901 - link = createLink(util.URLJoin(setting.AppURL, ctx.Metas["user"], ctx.Metas["repo"], path, ref.Issue), reftext, "ref-issue") 901 + link = createLink(util.URLJoin(ctx.Links.Prefix(), ctx.Metas["user"], ctx.Metas["repo"], path, ref.Issue), reftext, "ref-issue") 902 902 } else { 903 - link = createLink(util.URLJoin(setting.AppURL, ref.Owner, ref.Name, path, ref.Issue), reftext, "ref-issue") 903 + link = createLink(util.URLJoin(ctx.Links.Prefix(), ref.Owner, ref.Name, path, ref.Issue), reftext, "ref-issue") 904 904 } 905 905 } 906 906 ··· 939 939 } 940 940 941 941 reftext := ref.Owner + "/" + ref.Name + "@" + base.ShortSha(ref.CommitSha) 942 - link := createLink(util.URLJoin(setting.AppSubURL, ref.Owner, ref.Name, "commit", ref.CommitSha), reftext, "commit") 942 + link := createLink(util.URLJoin(ctx.Links.Prefix(), ref.Owner, ref.Name, "commit", ref.CommitSha), reftext, "commit") 943 943 944 944 replaceContent(node, ref.RefLocation.Start, ref.RefLocation.End, link) 945 945 node = node.NextSibling.NextSibling ··· 1166 1166 continue 1167 1167 } 1168 1168 1169 - link := util.URLJoin(setting.AppURL, ctx.Metas["user"], ctx.Metas["repo"], "commit", hash) 1169 + link := util.URLJoin(ctx.Links.Prefix(), ctx.Metas["user"], ctx.Metas["repo"], "commit", hash) 1170 1170 replaceContent(node, m[2], m[3], createCodeLink(link, base.ShortSha(hash), "commit")) 1171 1171 start = 0 1172 1172 node = node.NextSibling.NextSibling
+1
modules/markup/html_internal_test.go
··· 287 287 } 288 288 289 289 func testRenderIssueIndexPattern(t *testing.T, input, expected string, ctx *RenderContext) { 290 + ctx.Links.AbsolutePrefix = true 290 291 if ctx.Links.Base == "" { 291 292 ctx.Links.Base = TestRepoURL 292 293 }
+6 -3
modules/markup/html_test.go
··· 43 43 Ctx: git.DefaultContext, 44 44 RelativePath: ".md", 45 45 Links: markup.Links{ 46 - Base: markup.TestRepoURL, 46 + AbsolutePrefix: true, 47 + Base: markup.TestRepoURL, 47 48 }, 48 49 Metas: localMetas, 49 50 }, input) ··· 96 97 Ctx: git.DefaultContext, 97 98 RelativePath: "a.md", 98 99 Links: markup.Links{ 99 - Base: setting.AppSubURL, 100 + AbsolutePrefix: true, 101 + Base: setting.AppSubURL, 100 102 }, 101 103 Metas: localMetas, 102 104 }, input) ··· 579 581 err := markup.PostProcess(&markup.RenderContext{ 580 582 Ctx: git.DefaultContext, 581 583 Links: markup.Links{ 582 - Base: "https://example.com", 584 + AbsolutePrefix: true, 585 + Base: "https://example.com", 583 586 }, 584 587 Metas: localMetas, 585 588 }, strings.NewReader(input), &res)
+2 -1
modules/markup/markdown/markdown.go
··· 105 105 } 106 106 107 107 // include language-x class as part of commonmark spec 108 - _, err = w.WriteString(`<code class="chroma language-` + string(language) + `">`) 108 + // the "display" class is used by "js/markup/math.js" to render the code element as a block 109 + _, err = w.WriteString(`<code class="chroma language-` + string(language) + ` display">`) 109 110 if err != nil { 110 111 return 111 112 }
+3 -3
modules/markup/markdown/markdown_test.go
··· 131 131 <li><a href="` + baseURLContent + `/Links" rel="nofollow">Links, Language bindings, Engine bindings</a></li> 132 132 <li><a href="` + baseURLContent + `/Tips" rel="nofollow">Tips</a></li> 133 133 </ul> 134 - <p>See commit <a href="http://localhost:3000/gogits/gogs/commit/65f1bf27bc" rel="nofollow"><code>65f1bf27bc</code></a></p> 134 + <p>See commit <a href="/gogits/gogs/commit/65f1bf27bc" rel="nofollow"><code>65f1bf27bc</code></a></p> 135 135 <p>Ideas and codes</p> 136 136 <ul> 137 - <li>Bezier widget (by <a href="` + AppURL + `r-lyeh" rel="nofollow">@r-lyeh</a>) <a href="http://localhost:3000/ocornut/imgui/issues/786" class="ref-issue" rel="nofollow">ocornut/imgui#786</a></li> 138 - <li>Bezier widget (by <a href="` + AppURL + `r-lyeh" rel="nofollow">@r-lyeh</a>) <a href="http://localhost:3000/gogits/gogs/issues/786" class="ref-issue" rel="nofollow">#786</a></li> 137 + <li>Bezier widget (by <a href="/r-lyeh" rel="nofollow">@r-lyeh</a>) <a href="http://localhost:3000/ocornut/imgui/issues/786" class="ref-issue" rel="nofollow">ocornut/imgui#786</a></li> 138 + <li>Bezier widget (by <a href="/r-lyeh" rel="nofollow">@r-lyeh</a>) <a href="http://localhost:3000/gogits/gogs/issues/786" class="ref-issue" rel="nofollow">#786</a></li> 139 139 <li>Node graph editors <a href="https://github.com/ocornut/imgui/issues/306" rel="nofollow">https://github.com/ocornut/imgui/issues/306</a></li> 140 140 <li><a href="` + baseURLContent + `/memory_editor_example" rel="nofollow">Memory Editor</a></li> 141 141 <li><a href="` + baseURLContent + `/plot_var_example" rel="nofollow">Plot var helper</a></li>
+11 -3
modules/markup/renderer.go
··· 82 82 } 83 83 84 84 type Links struct { 85 - Base string 86 - BranchPath string 87 - TreePath string 85 + AbsolutePrefix bool 86 + Base string 87 + BranchPath string 88 + TreePath string 89 + } 90 + 91 + func (l *Links) Prefix() string { 92 + if l.AbsolutePrefix { 93 + return setting.AppURL 94 + } 95 + return setting.AppSubURL 88 96 } 89 97 90 98 func (l *Links) HasBranchInfo() bool {
+3
modules/setting/setting.go
··· 13 13 14 14 "code.gitea.io/gitea/modules/log" 15 15 "code.gitea.io/gitea/modules/user" 16 + "code.gitea.io/gitea/modules/util" 16 17 ) 17 18 18 19 var ForgejoVersion = "1.0.0" ··· 161 162 func loadRunModeFrom(rootCfg ConfigProvider) { 162 163 rootSec := rootCfg.Section("") 163 164 RunUser = rootSec.Key("RUN_USER").MustString(user.CurrentUsername()) 165 + 164 166 // The following is a purposefully undocumented option. Please do not run Gitea as root. It will only cause future headaches. 165 167 // Please don't use root as a bandaid to "fix" something that is broken, instead the broken thing should instead be fixed properly. 166 168 unsafeAllowRunAsRoot := ConfigSectionKeyBool(rootSec, "I_AM_BEING_UNSAFE_RUNNING_AS_ROOT") 169 + unsafeAllowRunAsRoot = unsafeAllowRunAsRoot || util.OptionalBoolParse(os.Getenv("GITEA_I_AM_BEING_UNSAFE_RUNNING_AS_ROOT")).Value() 167 170 RunMode = os.Getenv("GITEA_RUN_MODE") 168 171 if RunMode == "" { 169 172 RunMode = rootSec.Key("RUN_MODE").MustString("prod")
+5 -1
modules/templates/helper.go
··· 38 38 "SafeHTML": SafeHTML, 39 39 "HTMLFormat": HTMLFormat, 40 40 "HTMLEscape": HTMLEscape, 41 - "QueryEscape": url.QueryEscape, 41 + "QueryEscape": QueryEscape, 42 42 "JSEscape": JSEscapeSafe, 43 43 "SanitizeHTML": SanitizeHTML, 44 44 "URLJoin": util.URLJoin, ··· 227 227 228 228 func JSEscapeSafe(s string) template.HTML { 229 229 return template.HTML(template.JSEscapeString(s)) 230 + } 231 + 232 + func QueryEscape(s string) template.URL { 233 + return template.URL(url.QueryEscape(s)) 230 234 } 231 235 232 236 // DotEscape wraps a dots in names with ZWJ [U+200D] in order to prevent autolinkers from detecting these as urls
+8 -8
modules/templates/util_render_test.go
··· 61 61 62 62 func TestApostrophesInMentions(t *testing.T) { 63 63 rendered := RenderMarkdownToHtml(context.Background(), "@mention-user's comment") 64 - assert.EqualValues(t, "<p><a href=\"http://localhost:3000/mention-user\" rel=\"nofollow\">@mention-user</a>&#39;s comment</p>\n", rendered) 64 + assert.EqualValues(t, template.HTML("<p><a href=\"/mention-user\" rel=\"nofollow\">@mention-user</a>&#39;s comment</p>\n"), rendered) 65 65 } 66 66 67 67 func TestRenderCommitBody(t *testing.T) { ··· 122 122 com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit 123 123 <span class="emoji" aria-label="thumbs up">👍</span> 124 124 <a href="mailto:mail@domain.com" class="mailto">mail@domain.com</a> 125 - <a href="http://localhost:3000/mention-user" class="mention">@mention-user</a> test 126 - <a href="http://localhost:3000/user13/repo11/issues/123" class="ref-issue">#123</a> 125 + <a href="/mention-user" class="mention">@mention-user</a> test 126 + <a href="/user13/repo11/issues/123" class="ref-issue">#123</a> 127 127 space` 128 128 129 129 assert.EqualValues(t, expected, RenderCommitBody(context.Background(), testInput, testMetas)) 130 130 } 131 131 132 132 func TestRenderCommitMessage(t *testing.T) { 133 - expected := `space <a href="http://localhost:3000/mention-user" class="mention">@mention-user</a> ` 133 + expected := `space <a href="/mention-user" class="mention">@mention-user</a> ` 134 134 135 135 assert.EqualValues(t, expected, RenderCommitMessage(context.Background(), testInput, testMetas)) 136 136 } 137 137 138 138 func TestRenderCommitMessageLinkSubject(t *testing.T) { 139 - expected := `<a href="https://example.com/link" class="default-link muted">space </a><a href="http://localhost:3000/mention-user" class="mention">@mention-user</a>` 139 + expected := `<a href="https://example.com/link" class="default-link muted">space </a><a href="/mention-user" class="mention">@mention-user</a>` 140 140 141 141 assert.EqualValues(t, expected, RenderCommitMessageLinkSubject(context.Background(), testInput, "https://example.com/link", testMetas)) 142 142 } ··· 160 160 <span class="emoji" aria-label="thumbs up">👍</span> 161 161 mail@domain.com 162 162 @mention-user test 163 - <a href="http://localhost:3000/user13/repo11/issues/123" class="ref-issue">#123</a> 163 + <a href="/user13/repo11/issues/123" class="ref-issue">#123</a> 164 164 space 165 165 ` 166 166 assert.EqualValues(t, expected, RenderIssueTitle(context.Background(), testInput, testMetas)) 167 167 } 168 168 169 169 func TestRenderMarkdownToHtml(t *testing.T) { 170 - expected := `<p>space <a href="http://localhost:3000/mention-user" rel="nofollow">@mention-user</a><br/> 170 + expected := `<p>space <a href="/mention-user" rel="nofollow">@mention-user</a><br/> 171 171 /just/a/path.bin 172 172 <a href="https://example.com/file.bin" rel="nofollow">https://example.com/file.bin</a> 173 173 <a href="/file.bin" rel="nofollow">local link</a> ··· 184 184 com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit 185 185 <span class="emoji" aria-label="thumbs up">👍</span> 186 186 <a href="mailto:mail@domain.com" rel="nofollow">mail@domain.com</a> 187 - <a href="http://localhost:3000/mention-user" rel="nofollow">@mention-user</a> test 187 + <a href="/mention-user" rel="nofollow">@mention-user</a> test 188 188 #123 189 189 space</p> 190 190 `
+9 -9
modules/timeutil/datetime.go
··· 13 13 14 14 // DateTime renders an absolute time HTML element by datetime. 15 15 func DateTime(format string, datetime any, extraAttrs ...string) template.HTML { 16 + // TODO: remove the extraAttrs argument, it's not used in any call to DateTime 17 + 16 18 if p, ok := datetime.(*time.Time); ok { 17 19 datetime = *p 18 20 } ··· 51 53 52 54 attrs := make([]string, 0, 10+len(extraAttrs)) 53 55 attrs = append(attrs, extraAttrs...) 54 - attrs = append(attrs, `data-tooltip-content`, `data-tooltip-interactive="true"`) 55 - attrs = append(attrs, `format="datetime"`, `weekday=""`, `year="numeric"`) 56 + attrs = append(attrs, `weekday=""`, `year="numeric"`) 56 57 57 58 switch format { 58 - case "short": 59 - attrs = append(attrs, `month="short"`, `day="numeric"`) 60 - case "long": 61 - attrs = append(attrs, `month="long"`, `day="numeric"`) 62 - case "full": 63 - attrs = append(attrs, `month="short"`, `day="numeric"`, `hour="numeric"`, `minute="numeric"`, `second="numeric"`) 59 + case "short", "long": // date only 60 + attrs = append(attrs, `month="`+format+`"`, `day="numeric"`) 61 + return template.HTML(fmt.Sprintf(`<absolute-date %s date="%s">%s</absolute-date>`, strings.Join(attrs, " "), datetimeEscaped, textEscaped)) 62 + case "full": // full date including time 63 + attrs = append(attrs, `format="datetime"`, `month="short"`, `day="numeric"`, `hour="numeric"`, `minute="numeric"`, `second="numeric"`, `data-tooltip-content`, `data-tooltip-interactive="true"`) 64 + return template.HTML(fmt.Sprintf(`<relative-time %s datetime="%s">%s</relative-time>`, strings.Join(attrs, " "), datetimeEscaped, textEscaped)) 64 65 default: 65 66 panic(fmt.Sprintf("Unsupported format %s", format)) 66 67 } 67 - return template.HTML(fmt.Sprintf(`<relative-time %s datetime="%s">%s</relative-time>`, strings.Join(attrs, " "), datetimeEscaped, textEscaped)) 68 68 }
+9 -5
modules/timeutil/datetime_test.go
··· 18 18 defer test.MockVariableValue(&setting.DefaultUILocation, testTz)() 19 19 20 20 refTimeStr := "2018-01-01T00:00:00Z" 21 + refDateStr := "2018-01-01" 21 22 refTime, _ := time.Parse(time.RFC3339, refTimeStr) 22 23 refTimeStamp := TimeStamp(refTime.Unix()) 23 24 ··· 27 28 assert.EqualValues(t, "-", DateTime("short", TimeStamp(0))) 28 29 29 30 actual := DateTime("short", "invalid") 30 - assert.EqualValues(t, `<relative-time data-tooltip-content data-tooltip-interactive="true" format="datetime" weekday="" year="numeric" month="short" day="numeric" datetime="invalid">invalid</relative-time>`, actual) 31 + assert.EqualValues(t, `<absolute-date weekday="" year="numeric" month="short" day="numeric" date="invalid">invalid</absolute-date>`, actual) 31 32 32 33 actual = DateTime("short", refTimeStr) 33 - assert.EqualValues(t, `<relative-time data-tooltip-content data-tooltip-interactive="true" format="datetime" weekday="" year="numeric" month="short" day="numeric" datetime="2018-01-01T00:00:00Z">2018-01-01T00:00:00Z</relative-time>`, actual) 34 + assert.EqualValues(t, `<absolute-date weekday="" year="numeric" month="short" day="numeric" date="2018-01-01T00:00:00Z">2018-01-01T00:00:00Z</absolute-date>`, actual) 34 35 35 36 actual = DateTime("short", refTime) 36 - assert.EqualValues(t, `<relative-time data-tooltip-content data-tooltip-interactive="true" format="datetime" weekday="" year="numeric" month="short" day="numeric" datetime="2018-01-01T00:00:00Z">2018-01-01</relative-time>`, actual) 37 + assert.EqualValues(t, `<absolute-date weekday="" year="numeric" month="short" day="numeric" date="2018-01-01T00:00:00Z">2018-01-01</absolute-date>`, actual) 38 + 39 + actual = DateTime("short", refDateStr) 40 + assert.EqualValues(t, `<absolute-date weekday="" year="numeric" month="short" day="numeric" date="2018-01-01">2018-01-01</absolute-date>`, actual) 37 41 38 42 actual = DateTime("short", refTimeStamp) 39 - assert.EqualValues(t, `<relative-time data-tooltip-content data-tooltip-interactive="true" format="datetime" weekday="" year="numeric" month="short" day="numeric" datetime="2017-12-31T19:00:00-05:00">2017-12-31</relative-time>`, actual) 43 + assert.EqualValues(t, `<absolute-date weekday="" year="numeric" month="short" day="numeric" date="2017-12-31T19:00:00-05:00">2017-12-31</absolute-date>`, actual) 40 44 41 45 actual = DateTime("full", refTimeStamp) 42 - assert.EqualValues(t, `<relative-time data-tooltip-content data-tooltip-interactive="true" format="datetime" weekday="" year="numeric" month="short" day="numeric" hour="numeric" minute="numeric" second="numeric" datetime="2017-12-31T19:00:00-05:00">2017-12-31 19:00:00 -05:00</relative-time>`, actual) 46 + assert.EqualValues(t, `<relative-time weekday="" year="numeric" format="datetime" month="short" day="numeric" hour="numeric" minute="numeric" second="numeric" data-tooltip-content data-tooltip-interactive="true" datetime="2017-12-31T19:00:00-05:00">2017-12-31 19:00:00 -05:00</relative-time>`, actual) 43 47 }
+2 -2
modules/timeutil/since.go
··· 126 126 } 127 127 128 128 // declare data-tooltip-content attribute to switch from "title" tooltip to "tippy" tooltip 129 - htm := fmt.Sprintf(`<relative-time class="time-since" prefix="" %s datetime="%s" data-tooltip-content data-tooltip-interactive="true">%s</relative-time>`, 129 + htm := fmt.Sprintf(`<relative-time prefix="" %s datetime="%s" data-tooltip-content data-tooltip-interactive="true">%s</relative-time>`, 130 130 attrs, then.Format(time.RFC3339), friendlyText) 131 131 return template.HTML(htm) 132 132 } ··· 134 134 // TimeSince renders relative time HTML given a time.Time 135 135 func TimeSince(then time.Time, lang translation.Locale) template.HTML { 136 136 if setting.UI.PreferredTimestampTense == "absolute" { 137 - return DateTime("full", then, `class="time-since"`) 137 + return DateTime("full", then) 138 138 } 139 139 return timeSinceUnix(then, time.Now(), lang) 140 140 }
+9
modules/util/slice.go
··· 53 53 slices.Sort(values) 54 54 return values 55 55 } 56 + 57 + // TODO: Replace with "maps.Values" once available 58 + func ValuesOfMap[K comparable, V any](m map[K]V) []V { 59 + values := make([]V, 0, len(m)) 60 + for _, v := range m { 61 + values = append(values, v) 62 + } 63 + return values 64 + }
+898 -518
package-lock.json
··· 5 5 "packages": { 6 6 "": { 7 7 "dependencies": { 8 - "@citation-js/core": "0.7.6", 9 - "@citation-js/plugin-bibtex": "0.7.8", 10 - "@citation-js/plugin-csl": "0.7.6", 8 + "@citation-js/core": "0.7.9", 9 + "@citation-js/plugin-bibtex": "0.7.9", 10 + "@citation-js/plugin-csl": "0.7.9", 11 11 "@citation-js/plugin-software-formats": "0.6.1", 12 12 "@claviska/jquery-minicolors": "2.3.6", 13 13 "@github/markdown-toolbar-element": "2.2.3", ··· 15 15 "@github/text-expander-element": "2.6.1", 16 16 "@mcaptcha/vanilla-glue": "0.1.0-alpha-3", 17 17 "@primer/octicons": "19.8.0", 18 - "@webcomponents/custom-elements": "1.6.0", 19 18 "add-asset-webpack-plugin": "2.0.1", 20 19 "ansi_up": "6.0.2", 21 20 "asciinema-player": "3.7.0", ··· 27 26 "dayjs": "1.11.10", 28 27 "dropzone": "6.0.0-beta.2", 29 28 "easymde": "2.18.0", 30 - "esbuild-loader": "4.0.3", 29 + "esbuild-loader": "4.1.0", 31 30 "escape-goat": "4.0.0", 32 31 "fast-glob": "3.3.2", 33 - "htmx.org": "1.9.10", 32 + "htmx.org": "1.9.11", 34 33 "idiomorph": "0.3.0", 35 34 "jquery": "3.7.1", 36 35 "katex": "0.16.9", 37 36 "license-checker-webpack-plugin": "0.2.1", 38 - "mermaid": "10.8.0", 37 + "mermaid": "10.9.0", 39 38 "mini-css-extract-plugin": "2.8.1", 40 39 "minimatch": "9.0.3", 41 - "monaco-editor": "0.46.0", 40 + "monaco-editor": "0.47.0", 42 41 "monaco-editor-webpack-plugin": "7.1.0", 43 42 "pdfobject": "2.3.0", 44 43 "postcss": "8.4.35", 45 44 "postcss-loader": "8.1.1", 45 + "postcss-nesting": "12.1.0", 46 46 "pretty-ms": "9.0.0", 47 47 "sortablejs": "1.15.2", 48 - "swagger-ui-dist": "5.11.8", 48 + "swagger-ui-dist": "5.12.0", 49 49 "tailwindcss": "3.4.1", 50 + "temporal-polyfill": "0.2.3", 50 51 "throttle-debounce": "5.0.0", 51 52 "tinycolor2": "1.6.0", 52 53 "tippy.js": "6.3.7", ··· 66 67 "@eslint-community/eslint-plugin-eslint-comments": "4.1.0", 67 68 "@playwright/test": "1.42.1", 68 69 "@stoplight/spectral-cli": "6.11.0", 69 - "@stylistic/eslint-plugin-js": "1.6.3", 70 + "@stylistic/eslint-plugin-js": "1.7.0", 70 71 "@stylistic/stylelint-plugin": "2.1.0", 71 72 "@vitejs/plugin-vue": "5.0.4", 72 73 "eslint": "8.57.0", ··· 76 77 "eslint-plugin-jquery": "1.5.1", 77 78 "eslint-plugin-no-jquery": "2.7.0", 78 79 "eslint-plugin-no-use-extend-native": "0.5.0", 79 - "eslint-plugin-regexp": "2.2.0", 80 + "eslint-plugin-regexp": "2.3.0", 80 81 "eslint-plugin-sonarjs": "0.24.0", 81 82 "eslint-plugin-unicorn": "51.0.1", 82 - "eslint-plugin-vitest": "0.3.22", 83 + "eslint-plugin-vitest": "0.3.26", 83 84 "eslint-plugin-vitest-globals": "1.4.0", 84 - "eslint-plugin-vue": "9.22.0", 85 + "eslint-plugin-vue": "9.23.0", 85 86 "eslint-plugin-vue-scoped-css": "2.7.2", 86 87 "eslint-plugin-wc": "2.0.4", 87 88 "jsdom": "24.0.0", ··· 91 92 "stylelint-declaration-block-no-ignored-properties": "2.8.0", 92 93 "stylelint-declaration-strict-value": "1.10.4", 93 94 "svgo": "3.2.0", 94 - "updates": "15.1.2", 95 + "updates": "15.3.1", 95 96 "vite-string-plugin": "1.1.5", 96 97 "vitest": "1.3.1" 97 98 }, ··· 322 323 "integrity": "sha512-s3jaWicZd0pkP0jf5ysyHUI/RE7MHos6qlToFcGWXVp+ykHOy77OUMrfbgJ9it2C5bow7OIQwYYaHjk9XlBQ2A==" 323 324 }, 324 325 "node_modules/@citation-js/core": { 325 - "version": "0.7.6", 326 - "resolved": "https://registry.npmjs.org/@citation-js/core/-/core-0.7.6.tgz", 327 - "integrity": "sha512-qbB6RjwSsx/AjlCSAqoWKN05VxpjADYe8GmnPJnRB7QeNiVmqaRc8NSQDdvQ+4qhCkQOtMH15Sa2Nde4cvlXhw==", 326 + "version": "0.7.9", 327 + "resolved": "https://registry.npmjs.org/@citation-js/core/-/core-0.7.9.tgz", 328 + "integrity": "sha512-fSbkB32JayDChZnAYC/kB+sWHRvxxL7ibVetyBOyzOc+5aCnjb6UVsbcfhnkOIEyAMoRRvWDyFmakEoTtA5ttQ==", 328 329 "dependencies": { 329 330 "@citation-js/date": "^0.5.0", 330 331 "@citation-js/name": "^0.4.2", ··· 352 353 } 353 354 }, 354 355 "node_modules/@citation-js/plugin-bibtex": { 355 - "version": "0.7.8", 356 - "resolved": "https://registry.npmjs.org/@citation-js/plugin-bibtex/-/plugin-bibtex-0.7.8.tgz", 357 - "integrity": "sha512-20fUXe1zm1oCONFflGj3mgIk6DHspPjWrBirGfsyHmVSR/4xqnSbrqtztLiV15zt3tbKLepTaHm3ZTrcLOK0MA==", 356 + "version": "0.7.9", 357 + "resolved": "https://registry.npmjs.org/@citation-js/plugin-bibtex/-/plugin-bibtex-0.7.9.tgz", 358 + "integrity": "sha512-gIJpCd6vmmTOcRfDrSOjtoNhw2Mi94UwFxmgJ7GwkXyTYcNheW5VlMMo1tlqjakJGARQ0eOsKcI57gSPqJSS2g==", 358 359 "dependencies": { 359 360 "@citation-js/date": "^0.5.0", 360 361 "@citation-js/name": "^0.4.2", ··· 380 381 } 381 382 }, 382 383 "node_modules/@citation-js/plugin-csl": { 383 - "version": "0.7.6", 384 - "resolved": "https://registry.npmjs.org/@citation-js/plugin-csl/-/plugin-csl-0.7.6.tgz", 385 - "integrity": "sha512-H/dhzU56+D71Hzjto1x9PDtvsWaiI+Dx6Jj1vjiFtCCnbU/Zvqo5xFZNPstee+hFE6AsJ2xYlI8QujrGH+V1pQ==", 384 + "version": "0.7.9", 385 + "resolved": "https://registry.npmjs.org/@citation-js/plugin-csl/-/plugin-csl-0.7.9.tgz", 386 + "integrity": "sha512-mbD7CnUiPOuVnjeJwo+d0RGUcY0PE8n01gHyjq0qpTeS42EGmQ9+LzqfsTUVWWBndTwc6zLRuIF1qFAUHKE4oA==", 386 387 "dependencies": { 387 388 "@citation-js/date": "^0.5.0", 388 389 "citeproc": "^2.4.6" ··· 465 466 } 466 467 }, 467 468 "node_modules/@csstools/css-parser-algorithms": { 468 - "version": "2.6.0", 469 - "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-2.6.0.tgz", 470 - "integrity": "sha512-YfEHq0eRH98ffb5/EsrrDspVWAuph6gDggAE74ZtjecsmyyWpW768hOyiONa8zwWGbIWYfa2Xp4tRTrpQQ00CQ==", 469 + "version": "2.6.1", 470 + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-2.6.1.tgz", 471 + "integrity": "sha512-ubEkAaTfVZa+WwGhs5jbo5Xfqpeaybr/RvWzvFxRs4jfq16wH8l8Ty/QEEpINxll4xhuGfdMbipRyz5QZh9+FA==", 471 472 "dev": true, 472 473 "funding": [ 473 474 { ··· 483 484 "node": "^14 || ^16 || >=18" 484 485 }, 485 486 "peerDependencies": { 486 - "@csstools/css-tokenizer": "^2.2.3" 487 + "@csstools/css-tokenizer": "^2.2.4" 487 488 } 488 489 }, 489 490 "node_modules/@csstools/css-tokenizer": { 490 - "version": "2.2.3", 491 - "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-2.2.3.tgz", 492 - "integrity": "sha512-pp//EvZ9dUmGuGtG1p+n17gTHEOqu9jO+FiCUjNN3BDmyhdA2Jq9QsVeR7K8/2QCK17HSsioPlTW9ZkzoWb3Lg==", 491 + "version": "2.2.4", 492 + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-2.2.4.tgz", 493 + "integrity": "sha512-PuWRAewQLbDhGeTvFuq2oClaSCKPIBmHyIobCV39JHRYN0byDcUWJl5baPeNUcqrjtdMNqFooE0FGl31I3JOqw==", 493 494 "dev": true, 494 495 "funding": [ 495 496 { ··· 506 507 } 507 508 }, 508 509 "node_modules/@csstools/media-query-list-parser": { 509 - "version": "2.1.8", 510 - "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-2.1.8.tgz", 511 - "integrity": "sha512-DiD3vG5ciNzeuTEoh74S+JMjQDs50R3zlxHnBnfd04YYfA/kh2KiBCGhzqLxlJcNq+7yNQ3stuZZYLX6wK/U2g==", 510 + "version": "2.1.9", 511 + "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-2.1.9.tgz", 512 + "integrity": "sha512-qqGuFfbn4rUmyOB0u8CVISIp5FfJ5GAR3mBrZ9/TKndHakdnm6pY0L/fbLcpPnrzwCyyTEZl1nUcXAYHEWneTA==", 512 513 "dev": true, 513 514 "funding": [ 514 515 { ··· 524 525 "node": "^14 || ^16 || >=18" 525 526 }, 526 527 "peerDependencies": { 527 - "@csstools/css-parser-algorithms": "^2.6.0", 528 - "@csstools/css-tokenizer": "^2.2.3" 528 + "@csstools/css-parser-algorithms": "^2.6.1", 529 + "@csstools/css-tokenizer": "^2.2.4" 530 + } 531 + }, 532 + "node_modules/@csstools/selector-resolve-nested": { 533 + "version": "1.1.0", 534 + "resolved": "https://registry.npmjs.org/@csstools/selector-resolve-nested/-/selector-resolve-nested-1.1.0.tgz", 535 + "integrity": "sha512-uWvSaeRcHyeNenKg8tp17EVDRkpflmdyvbE0DHo6D/GdBb6PDnCYYU6gRpXhtICMGMcahQmj2zGxwFM/WC8hCg==", 536 + "funding": [ 537 + { 538 + "type": "github", 539 + "url": "https://github.com/sponsors/csstools" 540 + }, 541 + { 542 + "type": "opencollective", 543 + "url": "https://opencollective.com/csstools" 544 + } 545 + ], 546 + "engines": { 547 + "node": "^14 || ^16 || >=18" 548 + }, 549 + "peerDependencies": { 550 + "postcss-selector-parser": "^6.0.13" 529 551 } 530 552 }, 531 553 "node_modules/@csstools/selector-specificity": { 532 554 "version": "3.0.2", 533 555 "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-3.0.2.tgz", 534 556 "integrity": "sha512-RpHaZ1h9LE7aALeQXmXrJkRG84ZxIsctEN2biEUmFyKpzFM3zZ35eUMcIzZFsw/2olQE6v69+esEqU2f1MKycg==", 535 - "dev": true, 536 557 "funding": [ 537 558 { 538 559 "type": "github", ··· 559 580 } 560 581 }, 561 582 "node_modules/@esbuild/aix-ppc64": { 562 - "version": "0.19.12", 563 - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz", 564 - "integrity": "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==", 583 + "version": "0.20.2", 584 + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz", 585 + "integrity": "sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==", 565 586 "cpu": [ 566 587 "ppc64" 567 588 ], ··· 574 595 } 575 596 }, 576 597 "node_modules/@esbuild/android-arm": { 577 - "version": "0.19.12", 578 - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.12.tgz", 579 - "integrity": "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==", 598 + "version": "0.20.2", 599 + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.2.tgz", 600 + "integrity": "sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==", 580 601 "cpu": [ 581 602 "arm" 582 603 ], ··· 589 610 } 590 611 }, 591 612 "node_modules/@esbuild/android-arm64": { 592 - "version": "0.19.12", 593 - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz", 594 - "integrity": "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==", 613 + "version": "0.20.2", 614 + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz", 615 + "integrity": "sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==", 595 616 "cpu": [ 596 617 "arm64" 597 618 ], ··· 604 625 } 605 626 }, 606 627 "node_modules/@esbuild/android-x64": { 607 - "version": "0.19.12", 608 - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.12.tgz", 609 - "integrity": "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==", 628 + "version": "0.20.2", 629 + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.2.tgz", 630 + "integrity": "sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==", 610 631 "cpu": [ 611 632 "x64" 612 633 ], ··· 619 640 } 620 641 }, 621 642 "node_modules/@esbuild/darwin-arm64": { 622 - "version": "0.19.12", 623 - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz", 624 - "integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==", 643 + "version": "0.20.2", 644 + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz", 645 + "integrity": "sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==", 625 646 "cpu": [ 626 647 "arm64" 627 648 ], ··· 634 655 } 635 656 }, 636 657 "node_modules/@esbuild/darwin-x64": { 637 - "version": "0.19.12", 638 - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz", 639 - "integrity": "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==", 658 + "version": "0.20.2", 659 + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz", 660 + "integrity": "sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==", 640 661 "cpu": [ 641 662 "x64" 642 663 ], ··· 649 670 } 650 671 }, 651 672 "node_modules/@esbuild/freebsd-arm64": { 652 - "version": "0.19.12", 653 - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz", 654 - "integrity": "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==", 673 + "version": "0.20.2", 674 + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz", 675 + "integrity": "sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==", 655 676 "cpu": [ 656 677 "arm64" 657 678 ], ··· 664 685 } 665 686 }, 666 687 "node_modules/@esbuild/freebsd-x64": { 667 - "version": "0.19.12", 668 - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz", 669 - "integrity": "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==", 688 + "version": "0.20.2", 689 + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz", 690 + "integrity": "sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==", 670 691 "cpu": [ 671 692 "x64" 672 693 ], ··· 679 700 } 680 701 }, 681 702 "node_modules/@esbuild/linux-arm": { 682 - "version": "0.19.12", 683 - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz", 684 - "integrity": "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==", 703 + "version": "0.20.2", 704 + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz", 705 + "integrity": "sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==", 685 706 "cpu": [ 686 707 "arm" 687 708 ], ··· 694 715 } 695 716 }, 696 717 "node_modules/@esbuild/linux-arm64": { 697 - "version": "0.19.12", 698 - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz", 699 - "integrity": "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==", 718 + "version": "0.20.2", 719 + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz", 720 + "integrity": "sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==", 700 721 "cpu": [ 701 722 "arm64" 702 723 ], ··· 709 730 } 710 731 }, 711 732 "node_modules/@esbuild/linux-ia32": { 712 - "version": "0.19.12", 713 - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz", 714 - "integrity": "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==", 733 + "version": "0.20.2", 734 + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz", 735 + "integrity": "sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==", 715 736 "cpu": [ 716 737 "ia32" 717 738 ], ··· 724 745 } 725 746 }, 726 747 "node_modules/@esbuild/linux-loong64": { 727 - "version": "0.19.12", 728 - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz", 729 - "integrity": "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==", 748 + "version": "0.20.2", 749 + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz", 750 + "integrity": "sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==", 730 751 "cpu": [ 731 752 "loong64" 732 753 ], ··· 739 760 } 740 761 }, 741 762 "node_modules/@esbuild/linux-mips64el": { 742 - "version": "0.19.12", 743 - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz", 744 - "integrity": "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==", 763 + "version": "0.20.2", 764 + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz", 765 + "integrity": "sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==", 745 766 "cpu": [ 746 767 "mips64el" 747 768 ], ··· 754 775 } 755 776 }, 756 777 "node_modules/@esbuild/linux-ppc64": { 757 - "version": "0.19.12", 758 - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz", 759 - "integrity": "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==", 778 + "version": "0.20.2", 779 + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz", 780 + "integrity": "sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==", 760 781 "cpu": [ 761 782 "ppc64" 762 783 ], ··· 769 790 } 770 791 }, 771 792 "node_modules/@esbuild/linux-riscv64": { 772 - "version": "0.19.12", 773 - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz", 774 - "integrity": "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==", 793 + "version": "0.20.2", 794 + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz", 795 + "integrity": "sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==", 775 796 "cpu": [ 776 797 "riscv64" 777 798 ], ··· 784 805 } 785 806 }, 786 807 "node_modules/@esbuild/linux-s390x": { 787 - "version": "0.19.12", 788 - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz", 789 - "integrity": "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==", 808 + "version": "0.20.2", 809 + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz", 810 + "integrity": "sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==", 790 811 "cpu": [ 791 812 "s390x" 792 813 ], ··· 799 820 } 800 821 }, 801 822 "node_modules/@esbuild/linux-x64": { 802 - "version": "0.19.12", 803 - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz", 804 - "integrity": "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==", 823 + "version": "0.20.2", 824 + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz", 825 + "integrity": "sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==", 805 826 "cpu": [ 806 827 "x64" 807 828 ], ··· 814 835 } 815 836 }, 816 837 "node_modules/@esbuild/netbsd-x64": { 817 - "version": "0.19.12", 818 - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz", 819 - "integrity": "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==", 838 + "version": "0.20.2", 839 + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz", 840 + "integrity": "sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==", 820 841 "cpu": [ 821 842 "x64" 822 843 ], ··· 829 850 } 830 851 }, 831 852 "node_modules/@esbuild/openbsd-x64": { 832 - "version": "0.19.12", 833 - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz", 834 - "integrity": "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==", 853 + "version": "0.20.2", 854 + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz", 855 + "integrity": "sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==", 835 856 "cpu": [ 836 857 "x64" 837 858 ], ··· 844 865 } 845 866 }, 846 867 "node_modules/@esbuild/sunos-x64": { 847 - "version": "0.19.12", 848 - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz", 849 - "integrity": "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==", 868 + "version": "0.20.2", 869 + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz", 870 + "integrity": "sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==", 850 871 "cpu": [ 851 872 "x64" 852 873 ], ··· 859 880 } 860 881 }, 861 882 "node_modules/@esbuild/win32-arm64": { 862 - "version": "0.19.12", 863 - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz", 864 - "integrity": "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==", 883 + "version": "0.20.2", 884 + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz", 885 + "integrity": "sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==", 865 886 "cpu": [ 866 887 "arm64" 867 888 ], ··· 874 895 } 875 896 }, 876 897 "node_modules/@esbuild/win32-ia32": { 877 - "version": "0.19.12", 878 - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz", 879 - "integrity": "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==", 898 + "version": "0.20.2", 899 + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz", 900 + "integrity": "sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==", 880 901 "cpu": [ 881 902 "ia32" 882 903 ], ··· 889 910 } 890 911 }, 891 912 "node_modules/@esbuild/win32-x64": { 892 - "version": "0.19.12", 893 - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz", 894 - "integrity": "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==", 913 + "version": "0.20.2", 914 + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz", 915 + "integrity": "sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==", 895 916 "cpu": [ 896 917 "x64" 897 918 ], ··· 1229 1250 } 1230 1251 }, 1231 1252 "node_modules/@jridgewell/source-map": { 1232 - "version": "0.3.5", 1233 - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz", 1234 - "integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==", 1253 + "version": "0.3.6", 1254 + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", 1255 + "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", 1235 1256 "dependencies": { 1236 - "@jridgewell/gen-mapping": "^0.3.0", 1237 - "@jridgewell/trace-mapping": "^0.3.9" 1257 + "@jridgewell/gen-mapping": "^0.3.5", 1258 + "@jridgewell/trace-mapping": "^0.3.25" 1238 1259 } 1239 1260 }, 1240 1261 "node_modules/@jridgewell/sourcemap-codec": { ··· 1459 1480 "dev": true 1460 1481 }, 1461 1482 "node_modules/@rollup/rollup-android-arm-eabi": { 1462 - "version": "4.12.0", 1463 - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.12.0.tgz", 1464 - "integrity": "sha512-+ac02NL/2TCKRrJu2wffk1kZ+RyqxVUlbjSagNgPm94frxtr+XDL12E5Ll1enWskLrtrZ2r8L3wED1orIibV/w==", 1483 + "version": "4.13.0", 1484 + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.13.0.tgz", 1485 + "integrity": "sha512-5ZYPOuaAqEH/W3gYsRkxQATBW3Ii1MfaT4EQstTnLKViLi2gLSQmlmtTpGucNP3sXEpOiI5tdGhjdE111ekyEg==", 1465 1486 "cpu": [ 1466 1487 "arm" 1467 1488 ], ··· 1472 1493 ] 1473 1494 }, 1474 1495 "node_modules/@rollup/rollup-android-arm64": { 1475 - "version": "4.12.0", 1476 - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.12.0.tgz", 1477 - "integrity": "sha512-OBqcX2BMe6nvjQ0Nyp7cC90cnumt8PXmO7Dp3gfAju/6YwG0Tj74z1vKrfRz7qAv23nBcYM8BCbhrsWqO7PzQQ==", 1496 + "version": "4.13.0", 1497 + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.13.0.tgz", 1498 + "integrity": "sha512-BSbaCmn8ZadK3UAQdlauSvtaJjhlDEjS5hEVVIN3A4bbl3X+otyf/kOJV08bYiRxfejP3DXFzO2jz3G20107+Q==", 1478 1499 "cpu": [ 1479 1500 "arm64" 1480 1501 ], ··· 1485 1506 ] 1486 1507 }, 1487 1508 "node_modules/@rollup/rollup-darwin-arm64": { 1488 - "version": "4.12.0", 1489 - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.12.0.tgz", 1490 - "integrity": "sha512-X64tZd8dRE/QTrBIEs63kaOBG0b5GVEd3ccoLtyf6IdXtHdh8h+I56C2yC3PtC9Ucnv0CpNFJLqKFVgCYe0lOQ==", 1509 + "version": "4.13.0", 1510 + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.13.0.tgz", 1511 + "integrity": "sha512-Ovf2evVaP6sW5Ut0GHyUSOqA6tVKfrTHddtmxGQc1CTQa1Cw3/KMCDEEICZBbyppcwnhMwcDce9ZRxdWRpVd6g==", 1491 1512 "cpu": [ 1492 1513 "arm64" 1493 1514 ], ··· 1498 1519 ] 1499 1520 }, 1500 1521 "node_modules/@rollup/rollup-darwin-x64": { 1501 - "version": "4.12.0", 1502 - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.12.0.tgz", 1503 - "integrity": "sha512-cc71KUZoVbUJmGP2cOuiZ9HSOP14AzBAThn3OU+9LcA1+IUqswJyR1cAJj3Mg55HbjZP6OLAIscbQsQLrpgTOg==", 1522 + "version": "4.13.0", 1523 + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.13.0.tgz", 1524 + "integrity": "sha512-U+Jcxm89UTK592vZ2J9st9ajRv/hrwHdnvyuJpa5A2ngGSVHypigidkQJP+YiGL6JODiUeMzkqQzbCG3At81Gg==", 1504 1525 "cpu": [ 1505 1526 "x64" 1506 1527 ], ··· 1511 1532 ] 1512 1533 }, 1513 1534 "node_modules/@rollup/rollup-linux-arm-gnueabihf": { 1514 - "version": "4.12.0", 1515 - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.12.0.tgz", 1516 - "integrity": "sha512-a6w/Y3hyyO6GlpKL2xJ4IOh/7d+APaqLYdMf86xnczU3nurFTaVN9s9jOXQg97BE4nYm/7Ga51rjec5nfRdrvA==", 1535 + "version": "4.13.0", 1536 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.13.0.tgz", 1537 + "integrity": "sha512-8wZidaUJUTIR5T4vRS22VkSMOVooG0F4N+JSwQXWSRiC6yfEsFMLTYRFHvby5mFFuExHa/yAp9juSphQQJAijQ==", 1517 1538 "cpu": [ 1518 1539 "arm" 1519 1540 ], ··· 1524 1545 ] 1525 1546 }, 1526 1547 "node_modules/@rollup/rollup-linux-arm64-gnu": { 1527 - "version": "4.12.0", 1528 - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.12.0.tgz", 1529 - "integrity": "sha512-0fZBq27b+D7Ar5CQMofVN8sggOVhEtzFUwOwPppQt0k+VR+7UHMZZY4y+64WJ06XOhBTKXtQB/Sv0NwQMXyNAA==", 1548 + "version": "4.13.0", 1549 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.13.0.tgz", 1550 + "integrity": "sha512-Iu0Kno1vrD7zHQDxOmvweqLkAzjxEVqNhUIXBsZ8hu8Oak7/5VTPrxOEZXYC1nmrBVJp0ZcL2E7lSuuOVaE3+w==", 1530 1551 "cpu": [ 1531 1552 "arm64" 1532 1553 ], ··· 1537 1558 ] 1538 1559 }, 1539 1560 "node_modules/@rollup/rollup-linux-arm64-musl": { 1540 - "version": "4.12.0", 1541 - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.12.0.tgz", 1542 - "integrity": "sha512-eTvzUS3hhhlgeAv6bfigekzWZjaEX9xP9HhxB0Dvrdbkk5w/b+1Sxct2ZuDxNJKzsRStSq1EaEkVSEe7A7ipgQ==", 1561 + "version": "4.13.0", 1562 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.13.0.tgz", 1563 + "integrity": "sha512-C31QrW47llgVyrRjIwiOwsHFcaIwmkKi3PCroQY5aVq4H0A5v/vVVAtFsI1nfBngtoRpeREvZOkIhmRwUKkAdw==", 1543 1564 "cpu": [ 1544 1565 "arm64" 1545 1566 ], ··· 1550 1571 ] 1551 1572 }, 1552 1573 "node_modules/@rollup/rollup-linux-riscv64-gnu": { 1553 - "version": "4.12.0", 1554 - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.12.0.tgz", 1555 - "integrity": "sha512-ix+qAB9qmrCRiaO71VFfY8rkiAZJL8zQRXveS27HS+pKdjwUfEhqo2+YF2oI+H/22Xsiski+qqwIBxVewLK7sw==", 1574 + "version": "4.13.0", 1575 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.13.0.tgz", 1576 + "integrity": "sha512-Oq90dtMHvthFOPMl7pt7KmxzX7E71AfyIhh+cPhLY9oko97Zf2C9tt/XJD4RgxhaGeAraAXDtqxvKE1y/j35lA==", 1556 1577 "cpu": [ 1557 1578 "riscv64" 1558 1579 ], ··· 1563 1584 ] 1564 1585 }, 1565 1586 "node_modules/@rollup/rollup-linux-x64-gnu": { 1566 - "version": "4.12.0", 1567 - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.12.0.tgz", 1568 - "integrity": "sha512-TenQhZVOtw/3qKOPa7d+QgkeM6xY0LtwzR8OplmyL5LrgTWIXpTQg2Q2ycBf8jm+SFW2Wt/DTn1gf7nFp3ssVA==", 1587 + "version": "4.13.0", 1588 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.13.0.tgz", 1589 + "integrity": "sha512-yUD/8wMffnTKuiIsl6xU+4IA8UNhQ/f1sAnQebmE/lyQ8abjsVyDkyRkWop0kdMhKMprpNIhPmYlCxgHrPoXoA==", 1569 1590 "cpu": [ 1570 1591 "x64" 1571 1592 ], ··· 1576 1597 ] 1577 1598 }, 1578 1599 "node_modules/@rollup/rollup-linux-x64-musl": { 1579 - "version": "4.12.0", 1580 - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.12.0.tgz", 1581 - "integrity": "sha512-LfFdRhNnW0zdMvdCb5FNuWlls2WbbSridJvxOvYWgSBOYZtgBfW9UGNJG//rwMqTX1xQE9BAodvMH9tAusKDUw==", 1600 + "version": "4.13.0", 1601 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.13.0.tgz", 1602 + "integrity": "sha512-9RyNqoFNdF0vu/qqX63fKotBh43fJQeYC98hCaf89DYQpv+xu0D8QFSOS0biA7cGuqJFOc1bJ+m2rhhsKcw1hw==", 1582 1603 "cpu": [ 1583 1604 "x64" 1584 1605 ], ··· 1589 1610 ] 1590 1611 }, 1591 1612 "node_modules/@rollup/rollup-win32-arm64-msvc": { 1592 - "version": "4.12.0", 1593 - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.12.0.tgz", 1594 - "integrity": "sha512-JPDxovheWNp6d7AHCgsUlkuCKvtu3RB55iNEkaQcf0ttsDU/JZF+iQnYcQJSk/7PtT4mjjVG8N1kpwnI9SLYaw==", 1613 + "version": "4.13.0", 1614 + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.13.0.tgz", 1615 + "integrity": "sha512-46ue8ymtm/5PUU6pCvjlic0z82qWkxv54GTJZgHrQUuZnVH+tvvSP0LsozIDsCBFO4VjJ13N68wqrKSeScUKdA==", 1595 1616 "cpu": [ 1596 1617 "arm64" 1597 1618 ], ··· 1602 1623 ] 1603 1624 }, 1604 1625 "node_modules/@rollup/rollup-win32-ia32-msvc": { 1605 - "version": "4.12.0", 1606 - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.12.0.tgz", 1607 - "integrity": "sha512-fjtuvMWRGJn1oZacG8IPnzIV6GF2/XG+h71FKn76OYFqySXInJtseAqdprVTDTyqPxQOG9Exak5/E9Z3+EJ8ZA==", 1626 + "version": "4.13.0", 1627 + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.13.0.tgz", 1628 + "integrity": "sha512-P5/MqLdLSlqxbeuJ3YDeX37srC8mCflSyTrUsgbU1c/U9j6l2g2GiIdYaGD9QjdMQPMSgYm7hgg0551wHyIluw==", 1608 1629 "cpu": [ 1609 1630 "ia32" 1610 1631 ], ··· 1615 1636 ] 1616 1637 }, 1617 1638 "node_modules/@rollup/rollup-win32-x64-msvc": { 1618 - "version": "4.12.0", 1619 - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.12.0.tgz", 1620 - "integrity": "sha512-ZYmr5mS2wd4Dew/JjT0Fqi2NPB/ZhZ2VvPp7SmvPZb4Y1CG/LRcS6tcRo2cYU7zLK5A7cdbhWnnWmUjoI4qapg==", 1639 + "version": "4.13.0", 1640 + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.13.0.tgz", 1641 + "integrity": "sha512-UKXUQNbO3DOhzLRwHSpa0HnhhCgNODvfoPWv2FCXme8N/ANFfhIPMGuOT+QuKd16+B5yxZ0HdpNlqPvTMS1qfw==", 1621 1642 "cpu": [ 1622 1643 "x64" 1623 1644 ], ··· 2085 2106 "dev": true 2086 2107 }, 2087 2108 "node_modules/@stylistic/eslint-plugin-js": { 2088 - "version": "1.6.3", 2089 - "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-js/-/eslint-plugin-js-1.6.3.tgz", 2090 - "integrity": "sha512-ckdz51oHxD2FaxgY2piJWJVJiwgp8Uu96s+as2yB3RMwavn3nHBrpliVukXY9S/DmMicPRB2+H8nBk23GDG+qA==", 2109 + "version": "1.7.0", 2110 + "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-js/-/eslint-plugin-js-1.7.0.tgz", 2111 + "integrity": "sha512-PN6On/+or63FGnhhMKSQfYcWutRlzOiYlVdLM6yN7lquoBTqUJHYnl4TA4MHwiAt46X5gRxDr1+xPZ1lOLcL+Q==", 2091 2112 "dev": true, 2092 2113 "dependencies": { 2093 2114 "@types/eslint": "^8.56.2", ··· 2235 2256 "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==" 2236 2257 }, 2237 2258 "node_modules/@types/node": { 2238 - "version": "20.11.24", 2239 - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.24.tgz", 2240 - "integrity": "sha512-Kza43ewS3xoLgCEpQrsT+xRo/EJej1y0kVYGiLFE1NEODXGzTfwiC6tXTLMQskn1X4/Rjlh0MQUvx9W+L9long==", 2259 + "version": "20.11.27", 2260 + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.27.tgz", 2261 + "integrity": "sha512-qyUZfMnCg1KEz57r7pzFtSGt49f6RPkPBis3Vo4PbS7roQEDn22hiHzl/Lo1q4i4hDEgBJmBF/NTNg2XR0HbFg==", 2241 2262 "dependencies": { 2242 2263 "undici-types": "~5.26.4" 2243 2264 } ··· 2280 2301 "dev": true 2281 2302 }, 2282 2303 "node_modules/@typescript-eslint/eslint-plugin": { 2283 - "version": "7.1.0", 2284 - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.1.0.tgz", 2285 - "integrity": "sha512-j6vT/kCulhG5wBmGtstKeiVr1rdXE4nk+DT1k6trYkwlrvW9eOF5ZbgKnd/YR6PcM4uTEXa0h6Fcvf6X7Dxl0w==", 2304 + "version": "7.2.0", 2305 + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.2.0.tgz", 2306 + "integrity": "sha512-mdekAHOqS9UjlmyF/LSs6AIEvfceV749GFxoBAjwAv0nkevfKHWQFDMcBZWUiIC5ft6ePWivXoS36aKQ0Cy3sw==", 2286 2307 "dev": true, 2287 2308 "dependencies": { 2288 2309 "@eslint-community/regexpp": "^4.5.1", 2289 - "@typescript-eslint/scope-manager": "7.1.0", 2290 - "@typescript-eslint/type-utils": "7.1.0", 2291 - "@typescript-eslint/utils": "7.1.0", 2292 - "@typescript-eslint/visitor-keys": "7.1.0", 2310 + "@typescript-eslint/scope-manager": "7.2.0", 2311 + "@typescript-eslint/type-utils": "7.2.0", 2312 + "@typescript-eslint/utils": "7.2.0", 2313 + "@typescript-eslint/visitor-keys": "7.2.0", 2293 2314 "debug": "^4.3.4", 2294 2315 "graphemer": "^1.4.0", 2295 2316 "ignore": "^5.2.4", ··· 2315 2336 } 2316 2337 }, 2317 2338 "node_modules/@typescript-eslint/parser": { 2318 - "version": "7.1.0", 2319 - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.1.0.tgz", 2320 - "integrity": "sha512-V1EknKUubZ1gWFjiOZhDSNToOjs63/9O0puCgGS8aDOgpZY326fzFu15QAUjwaXzRZjf/qdsdBrckYdv9YxB8w==", 2339 + "version": "7.2.0", 2340 + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.2.0.tgz", 2341 + "integrity": "sha512-5FKsVcHTk6TafQKQbuIVkXq58Fnbkd2wDL4LB7AURN7RUOu1utVP+G8+6u3ZhEroW3DF6hyo3ZEXxgKgp4KeCg==", 2321 2342 "dev": true, 2322 2343 "dependencies": { 2323 - "@typescript-eslint/scope-manager": "7.1.0", 2324 - "@typescript-eslint/types": "7.1.0", 2325 - "@typescript-eslint/typescript-estree": "7.1.0", 2326 - "@typescript-eslint/visitor-keys": "7.1.0", 2344 + "@typescript-eslint/scope-manager": "7.2.0", 2345 + "@typescript-eslint/types": "7.2.0", 2346 + "@typescript-eslint/typescript-estree": "7.2.0", 2347 + "@typescript-eslint/visitor-keys": "7.2.0", 2327 2348 "debug": "^4.3.4" 2328 2349 }, 2329 2350 "engines": { ··· 2343 2364 } 2344 2365 }, 2345 2366 "node_modules/@typescript-eslint/scope-manager": { 2346 - "version": "7.1.0", 2347 - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.1.0.tgz", 2348 - "integrity": "sha512-6TmN4OJiohHfoOdGZ3huuLhpiUgOGTpgXNUPJgeZOZR3DnIpdSgtt83RS35OYNNXxM4TScVlpVKC9jyQSETR1A==", 2367 + "version": "7.2.0", 2368 + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.2.0.tgz", 2369 + "integrity": "sha512-Qh976RbQM/fYtjx9hs4XkayYujB/aPwglw2choHmf3zBjB4qOywWSdt9+KLRdHubGcoSwBnXUH2sR3hkyaERRg==", 2349 2370 "dev": true, 2350 2371 "dependencies": { 2351 - "@typescript-eslint/types": "7.1.0", 2352 - "@typescript-eslint/visitor-keys": "7.1.0" 2372 + "@typescript-eslint/types": "7.2.0", 2373 + "@typescript-eslint/visitor-keys": "7.2.0" 2353 2374 }, 2354 2375 "engines": { 2355 2376 "node": "^16.0.0 || >=18.0.0" ··· 2360 2381 } 2361 2382 }, 2362 2383 "node_modules/@typescript-eslint/type-utils": { 2363 - "version": "7.1.0", 2364 - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.1.0.tgz", 2365 - "integrity": "sha512-UZIhv8G+5b5skkcuhgvxYWHjk7FW7/JP5lPASMEUoliAPwIH/rxoUSQPia2cuOj9AmDZmwUl1usKm85t5VUMew==", 2384 + "version": "7.2.0", 2385 + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.2.0.tgz", 2386 + "integrity": "sha512-xHi51adBHo9O9330J8GQYQwrKBqbIPJGZZVQTHHmy200hvkLZFWJIFtAG/7IYTWUyun6DE6w5InDReePJYJlJA==", 2366 2387 "dev": true, 2367 2388 "dependencies": { 2368 - "@typescript-eslint/typescript-estree": "7.1.0", 2369 - "@typescript-eslint/utils": "7.1.0", 2389 + "@typescript-eslint/typescript-estree": "7.2.0", 2390 + "@typescript-eslint/utils": "7.2.0", 2370 2391 "debug": "^4.3.4", 2371 2392 "ts-api-utils": "^1.0.1" 2372 2393 }, ··· 2387 2408 } 2388 2409 }, 2389 2410 "node_modules/@typescript-eslint/types": { 2390 - "version": "7.1.0", 2391 - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.1.0.tgz", 2392 - "integrity": "sha512-qTWjWieJ1tRJkxgZYXx6WUYtWlBc48YRxgY2JN1aGeVpkhmnopq+SUC8UEVGNXIvWH7XyuTjwALfG6bFEgCkQA==", 2411 + "version": "7.2.0", 2412 + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.2.0.tgz", 2413 + "integrity": "sha512-XFtUHPI/abFhm4cbCDc5Ykc8npOKBSJePY3a3s+lwumt7XWJuzP5cZcfZ610MIPHjQjNsOLlYK8ASPaNG8UiyA==", 2393 2414 "dev": true, 2394 2415 "engines": { 2395 2416 "node": "^16.0.0 || >=18.0.0" ··· 2400 2421 } 2401 2422 }, 2402 2423 "node_modules/@typescript-eslint/typescript-estree": { 2403 - "version": "7.1.0", 2404 - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.1.0.tgz", 2405 - "integrity": "sha512-k7MyrbD6E463CBbSpcOnwa8oXRdHzH1WiVzOipK3L5KSML92ZKgUBrTlehdi7PEIMT8k0bQixHUGXggPAlKnOQ==", 2424 + "version": "7.2.0", 2425 + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.2.0.tgz", 2426 + "integrity": "sha512-cyxS5WQQCoBwSakpMrvMXuMDEbhOo9bNHHrNcEWis6XHx6KF518tkF1wBvKIn/tpq5ZpUYK7Bdklu8qY0MsFIA==", 2406 2427 "dev": true, 2407 2428 "dependencies": { 2408 - "@typescript-eslint/types": "7.1.0", 2409 - "@typescript-eslint/visitor-keys": "7.1.0", 2429 + "@typescript-eslint/types": "7.2.0", 2430 + "@typescript-eslint/visitor-keys": "7.2.0", 2410 2431 "debug": "^4.3.4", 2411 2432 "globby": "^11.1.0", 2412 2433 "is-glob": "^4.0.3", ··· 2428 2449 } 2429 2450 }, 2430 2451 "node_modules/@typescript-eslint/utils": { 2431 - "version": "7.1.0", 2432 - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.1.0.tgz", 2433 - "integrity": "sha512-WUFba6PZC5OCGEmbweGpnNJytJiLG7ZvDBJJoUcX4qZYf1mGZ97mO2Mps6O2efxJcJdRNpqweCistDbZMwIVHw==", 2452 + "version": "7.2.0", 2453 + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.2.0.tgz", 2454 + "integrity": "sha512-YfHpnMAGb1Eekpm3XRK8hcMwGLGsnT6L+7b2XyRv6ouDuJU1tZir1GS2i0+VXRatMwSI1/UfcyPe53ADkU+IuA==", 2434 2455 "dev": true, 2435 2456 "dependencies": { 2436 2457 "@eslint-community/eslint-utils": "^4.4.0", 2437 2458 "@types/json-schema": "^7.0.12", 2438 2459 "@types/semver": "^7.5.0", 2439 - "@typescript-eslint/scope-manager": "7.1.0", 2440 - "@typescript-eslint/types": "7.1.0", 2441 - "@typescript-eslint/typescript-estree": "7.1.0", 2460 + "@typescript-eslint/scope-manager": "7.2.0", 2461 + "@typescript-eslint/types": "7.2.0", 2462 + "@typescript-eslint/typescript-estree": "7.2.0", 2442 2463 "semver": "^7.5.4" 2443 2464 }, 2444 2465 "engines": { ··· 2453 2474 } 2454 2475 }, 2455 2476 "node_modules/@typescript-eslint/visitor-keys": { 2456 - "version": "7.1.0", 2457 - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.1.0.tgz", 2458 - "integrity": "sha512-FhUqNWluiGNzlvnDZiXad4mZRhtghdoKW6e98GoEOYSu5cND+E39rG5KwJMUzeENwm1ztYBRqof8wMLP+wNPIA==", 2477 + "version": "7.2.0", 2478 + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.2.0.tgz", 2479 + "integrity": "sha512-c6EIQRHhcpl6+tO8EMR+kjkkV+ugUNXOmeASA1rlzkd8EPIriavpWoiEz1HR/VLhbVIdhqnV6E7JZm00cBDx2A==", 2459 2480 "dev": true, 2460 2481 "dependencies": { 2461 - "@typescript-eslint/types": "7.1.0", 2482 + "@typescript-eslint/types": "7.2.0", 2462 2483 "eslint-visitor-keys": "^3.4.1" 2463 2484 }, 2464 2485 "engines": { ··· 2558 2579 } 2559 2580 }, 2560 2581 "node_modules/@vitest/snapshot/node_modules/magic-string": { 2561 - "version": "0.30.7", 2562 - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.7.tgz", 2563 - "integrity": "sha512-8vBuFF/I/+OSLRmdf2wwFCJCz+nSn0m6DPvGH1fS/KiQoSaR+sETbov0eIk9KhEKy8CYqIkIAnbohxT/4H0kuA==", 2582 + "version": "0.30.8", 2583 + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.8.tgz", 2584 + "integrity": "sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==", 2564 2585 "dev": true, 2565 2586 "dependencies": { 2566 2587 "@jridgewell/sourcemap-codec": "^1.4.15" ··· 2649 2670 } 2650 2671 }, 2651 2672 "node_modules/@vue/compiler-sfc/node_modules/magic-string": { 2652 - "version": "0.30.7", 2653 - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.7.tgz", 2654 - "integrity": "sha512-8vBuFF/I/+OSLRmdf2wwFCJCz+nSn0m6DPvGH1fS/KiQoSaR+sETbov0eIk9KhEKy8CYqIkIAnbohxT/4H0kuA==", 2673 + "version": "0.30.8", 2674 + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.8.tgz", 2675 + "integrity": "sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==", 2655 2676 "dependencies": { 2656 2677 "@jridgewell/sourcemap-codec": "^1.4.15" 2657 2678 }, ··· 2713 2734 "integrity": "sha512-PuJe7vDIi6VYSinuEbUIQgMIRZGgM8e4R+G+/dQTk0X1NEdvgvvgv7m+rfmDH1gZzyA1OjjoWskvHlfRNfQf3g==" 2714 2735 }, 2715 2736 "node_modules/@webassemblyjs/ast": { 2716 - "version": "1.11.6", 2717 - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.6.tgz", 2718 - "integrity": "sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==", 2737 + "version": "1.12.1", 2738 + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.12.1.tgz", 2739 + "integrity": "sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==", 2719 2740 "dependencies": { 2720 2741 "@webassemblyjs/helper-numbers": "1.11.6", 2721 2742 "@webassemblyjs/helper-wasm-bytecode": "1.11.6" ··· 2732 2753 "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==" 2733 2754 }, 2734 2755 "node_modules/@webassemblyjs/helper-buffer": { 2735 - "version": "1.11.6", 2736 - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz", 2737 - "integrity": "sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA==" 2756 + "version": "1.12.1", 2757 + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz", 2758 + "integrity": "sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw==" 2738 2759 }, 2739 2760 "node_modules/@webassemblyjs/helper-numbers": { 2740 2761 "version": "1.11.6", ··· 2752 2773 "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==" 2753 2774 }, 2754 2775 "node_modules/@webassemblyjs/helper-wasm-section": { 2755 - "version": "1.11.6", 2756 - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz", 2757 - "integrity": "sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g==", 2776 + "version": "1.12.1", 2777 + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz", 2778 + "integrity": "sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g==", 2758 2779 "dependencies": { 2759 - "@webassemblyjs/ast": "1.11.6", 2760 - "@webassemblyjs/helper-buffer": "1.11.6", 2780 + "@webassemblyjs/ast": "1.12.1", 2781 + "@webassemblyjs/helper-buffer": "1.12.1", 2761 2782 "@webassemblyjs/helper-wasm-bytecode": "1.11.6", 2762 - "@webassemblyjs/wasm-gen": "1.11.6" 2783 + "@webassemblyjs/wasm-gen": "1.12.1" 2763 2784 } 2764 2785 }, 2765 2786 "node_modules/@webassemblyjs/ieee754": { ··· 2784 2805 "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==" 2785 2806 }, 2786 2807 "node_modules/@webassemblyjs/wasm-edit": { 2787 - "version": "1.11.6", 2788 - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz", 2789 - "integrity": "sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw==", 2808 + "version": "1.12.1", 2809 + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz", 2810 + "integrity": "sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g==", 2790 2811 "dependencies": { 2791 - "@webassemblyjs/ast": "1.11.6", 2792 - "@webassemblyjs/helper-buffer": "1.11.6", 2812 + "@webassemblyjs/ast": "1.12.1", 2813 + "@webassemblyjs/helper-buffer": "1.12.1", 2793 2814 "@webassemblyjs/helper-wasm-bytecode": "1.11.6", 2794 - "@webassemblyjs/helper-wasm-section": "1.11.6", 2795 - "@webassemblyjs/wasm-gen": "1.11.6", 2796 - "@webassemblyjs/wasm-opt": "1.11.6", 2797 - "@webassemblyjs/wasm-parser": "1.11.6", 2798 - "@webassemblyjs/wast-printer": "1.11.6" 2815 + "@webassemblyjs/helper-wasm-section": "1.12.1", 2816 + "@webassemblyjs/wasm-gen": "1.12.1", 2817 + "@webassemblyjs/wasm-opt": "1.12.1", 2818 + "@webassemblyjs/wasm-parser": "1.12.1", 2819 + "@webassemblyjs/wast-printer": "1.12.1" 2799 2820 } 2800 2821 }, 2801 2822 "node_modules/@webassemblyjs/wasm-gen": { 2802 - "version": "1.11.6", 2803 - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz", 2804 - "integrity": "sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA==", 2823 + "version": "1.12.1", 2824 + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz", 2825 + "integrity": "sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w==", 2805 2826 "dependencies": { 2806 - "@webassemblyjs/ast": "1.11.6", 2827 + "@webassemblyjs/ast": "1.12.1", 2807 2828 "@webassemblyjs/helper-wasm-bytecode": "1.11.6", 2808 2829 "@webassemblyjs/ieee754": "1.11.6", 2809 2830 "@webassemblyjs/leb128": "1.11.6", ··· 2811 2832 } 2812 2833 }, 2813 2834 "node_modules/@webassemblyjs/wasm-opt": { 2814 - "version": "1.11.6", 2815 - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz", 2816 - "integrity": "sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g==", 2835 + "version": "1.12.1", 2836 + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz", 2837 + "integrity": "sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg==", 2817 2838 "dependencies": { 2818 - "@webassemblyjs/ast": "1.11.6", 2819 - "@webassemblyjs/helper-buffer": "1.11.6", 2820 - "@webassemblyjs/wasm-gen": "1.11.6", 2821 - "@webassemblyjs/wasm-parser": "1.11.6" 2839 + "@webassemblyjs/ast": "1.12.1", 2840 + "@webassemblyjs/helper-buffer": "1.12.1", 2841 + "@webassemblyjs/wasm-gen": "1.12.1", 2842 + "@webassemblyjs/wasm-parser": "1.12.1" 2822 2843 } 2823 2844 }, 2824 2845 "node_modules/@webassemblyjs/wasm-parser": { 2825 - "version": "1.11.6", 2826 - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz", 2827 - "integrity": "sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ==", 2846 + "version": "1.12.1", 2847 + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz", 2848 + "integrity": "sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ==", 2828 2849 "dependencies": { 2829 - "@webassemblyjs/ast": "1.11.6", 2850 + "@webassemblyjs/ast": "1.12.1", 2830 2851 "@webassemblyjs/helper-api-error": "1.11.6", 2831 2852 "@webassemblyjs/helper-wasm-bytecode": "1.11.6", 2832 2853 "@webassemblyjs/ieee754": "1.11.6", ··· 2835 2856 } 2836 2857 }, 2837 2858 "node_modules/@webassemblyjs/wast-printer": { 2838 - "version": "1.11.6", 2839 - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz", 2840 - "integrity": "sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A==", 2859 + "version": "1.12.1", 2860 + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz", 2861 + "integrity": "sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==", 2841 2862 "dependencies": { 2842 - "@webassemblyjs/ast": "1.11.6", 2863 + "@webassemblyjs/ast": "1.12.1", 2843 2864 "@xtuc/long": "4.2.2" 2844 2865 } 2845 2866 }, 2846 - "node_modules/@webcomponents/custom-elements": { 2847 - "version": "1.6.0", 2848 - "resolved": "https://registry.npmjs.org/@webcomponents/custom-elements/-/custom-elements-1.6.0.tgz", 2849 - "integrity": "sha512-CqTpxOlUCPWRNUPZDxT5v2NnHXA4oox612iUGnmTUGQFhZ1Gkj8kirtl/2wcF6MqX7+PqqicZzOCBKKfIn0dww==" 2850 - }, 2851 2867 "node_modules/@webpack-cli/configtest": { 2852 2868 "version": "2.1.1", 2853 2869 "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-2.1.1.tgz", ··· 3409 3425 } 3410 3426 }, 3411 3427 "node_modules/binary-extensions": { 3412 - "version": "2.2.0", 3413 - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", 3414 - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", 3428 + "version": "2.3.0", 3429 + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", 3430 + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", 3415 3431 "engines": { 3416 3432 "node": ">=8" 3433 + }, 3434 + "funding": { 3435 + "url": "https://github.com/sponsors/sindresorhus" 3417 3436 } 3418 3437 }, 3419 3438 "node_modules/boolbase": { ··· 3563 3582 } 3564 3583 }, 3565 3584 "node_modules/caniuse-lite": { 3566 - "version": "1.0.30001591", 3567 - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001591.tgz", 3568 - "integrity": "sha512-PCzRMei/vXjJyL5mJtzNiUCKP59dm8Apqc3PH8gJkMnMXZGox93RbE76jHsmLwmIo6/3nsYIpJtx0O7u5PqFuQ==", 3585 + "version": "1.0.30001597", 3586 + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001597.tgz", 3587 + "integrity": "sha512-7LjJvmQU6Sj7bL0j5b5WY/3n7utXUJvAe1lxhsHDbLmwX9mdL86Yjtr+5SRCyf8qME4M7pU2hswj0FpyBVCv9w==", 3569 3588 "funding": [ 3570 3589 { 3571 3590 "type": "opencollective", ··· 4128 4147 } 4129 4148 }, 4130 4149 "node_modules/d3": { 4131 - "version": "7.8.5", 4132 - "resolved": "https://registry.npmjs.org/d3/-/d3-7.8.5.tgz", 4133 - "integrity": "sha512-JgoahDG51ncUfJu6wX/1vWQEqOflgXyl4MaHqlcSruTez7yhaRKR9i8VjjcQGeS2en/jnFivXuaIMnseMMt0XA==", 4150 + "version": "7.9.0", 4151 + "resolved": "https://registry.npmjs.org/d3/-/d3-7.9.0.tgz", 4152 + "integrity": "sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==", 4134 4153 "dependencies": { 4135 4154 "d3-array": "3", 4136 4155 "d3-axis": "3", ··· 4335 4354 } 4336 4355 }, 4337 4356 "node_modules/d3-geo": { 4338 - "version": "3.1.0", 4339 - "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.0.tgz", 4340 - "integrity": "sha512-JEo5HxXDdDYXCaWdwLRt79y7giK8SbhZJbFWXqbRTolCHFI5jRqteLzCsq51NKbUoX0PjBVSohxrx+NoOUujYA==", 4357 + "version": "3.1.1", 4358 + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.1.tgz", 4359 + "integrity": "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==", 4341 4360 "dependencies": { 4342 4361 "d3-array": "2.5.0 - 3" 4343 4362 }, ··· 4447 4466 } 4448 4467 }, 4449 4468 "node_modules/d3-scale-chromatic": { 4450 - "version": "3.0.0", 4451 - "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.0.0.tgz", 4452 - "integrity": "sha512-Lx9thtxAKrO2Pq6OO2Ua474opeziKr279P/TKZsMAhYyNDD3EnCffdbgeSYN5O7m2ByQsxtuP2CSDczNUIZ22g==", 4469 + "version": "3.1.0", 4470 + "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", 4471 + "integrity": "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==", 4453 4472 "dependencies": { 4454 4473 "d3-color": "1 - 3", 4455 4474 "d3-interpolate": "1 - 3" ··· 4855 4874 } 4856 4875 }, 4857 4876 "node_modules/electron-to-chromium": { 4858 - "version": "1.4.690", 4859 - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.690.tgz", 4860 - "integrity": "sha512-+2OAGjUx68xElQhydpcbqH50hE8Vs2K6TkAeLhICYfndb67CVH0UsZaijmRUE3rHlIxU1u0jxwhgVe6fK3YANA==" 4877 + "version": "1.4.706", 4878 + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.706.tgz", 4879 + "integrity": "sha512-fO01fufoGd6jKK3HR8ofBapF3ZPfgxNJ/ua9xQAhFu93TwWIs4d+weDn3kje3GB4S7aGUTfk5nvdU5F7z5mF9Q==" 4861 4880 }, 4862 4881 "node_modules/elkjs": { 4863 4882 "version": "0.9.2", ··· 4878 4897 } 4879 4898 }, 4880 4899 "node_modules/enhanced-resolve": { 4881 - "version": "5.15.1", 4882 - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.1.tgz", 4883 - "integrity": "sha512-3d3JRbwsCLJsYgvb6NuWEG44jjPSOMuS73L/6+7BZuoKm3W+qXnSoIYVHi8dG7Qcg4inAY4jbzkZ7MnskePeDg==", 4900 + "version": "5.16.0", 4901 + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.16.0.tgz", 4902 + "integrity": "sha512-O+QWCviPNSSLAD9Ucn8Awv+poAkqn3T1XY5/N7kR7rQO9yfSGWkYZDwpJ+iKF7B8rxaQKWngSqACpgzeapSyoA==", 4884 4903 "dependencies": { 4885 4904 "graceful-fs": "^4.2.4", 4886 4905 "tapable": "^2.2.0" ··· 5103 5122 } 5104 5123 }, 5105 5124 "node_modules/esbuild": { 5106 - "version": "0.19.12", 5107 - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz", 5108 - "integrity": "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==", 5125 + "version": "0.20.2", 5126 + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz", 5127 + "integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==", 5109 5128 "hasInstallScript": true, 5110 5129 "bin": { 5111 5130 "esbuild": "bin/esbuild" ··· 5114 5133 "node": ">=12" 5115 5134 }, 5116 5135 "optionalDependencies": { 5117 - "@esbuild/aix-ppc64": "0.19.12", 5118 - "@esbuild/android-arm": "0.19.12", 5119 - "@esbuild/android-arm64": "0.19.12", 5120 - "@esbuild/android-x64": "0.19.12", 5121 - "@esbuild/darwin-arm64": "0.19.12", 5122 - "@esbuild/darwin-x64": "0.19.12", 5123 - "@esbuild/freebsd-arm64": "0.19.12", 5124 - "@esbuild/freebsd-x64": "0.19.12", 5125 - "@esbuild/linux-arm": "0.19.12", 5126 - "@esbuild/linux-arm64": "0.19.12", 5127 - "@esbuild/linux-ia32": "0.19.12", 5128 - "@esbuild/linux-loong64": "0.19.12", 5129 - "@esbuild/linux-mips64el": "0.19.12", 5130 - "@esbuild/linux-ppc64": "0.19.12", 5131 - "@esbuild/linux-riscv64": "0.19.12", 5132 - "@esbuild/linux-s390x": "0.19.12", 5133 - "@esbuild/linux-x64": "0.19.12", 5134 - "@esbuild/netbsd-x64": "0.19.12", 5135 - "@esbuild/openbsd-x64": "0.19.12", 5136 - "@esbuild/sunos-x64": "0.19.12", 5137 - "@esbuild/win32-arm64": "0.19.12", 5138 - "@esbuild/win32-ia32": "0.19.12", 5139 - "@esbuild/win32-x64": "0.19.12" 5136 + "@esbuild/aix-ppc64": "0.20.2", 5137 + "@esbuild/android-arm": "0.20.2", 5138 + "@esbuild/android-arm64": "0.20.2", 5139 + "@esbuild/android-x64": "0.20.2", 5140 + "@esbuild/darwin-arm64": "0.20.2", 5141 + "@esbuild/darwin-x64": "0.20.2", 5142 + "@esbuild/freebsd-arm64": "0.20.2", 5143 + "@esbuild/freebsd-x64": "0.20.2", 5144 + "@esbuild/linux-arm": "0.20.2", 5145 + "@esbuild/linux-arm64": "0.20.2", 5146 + "@esbuild/linux-ia32": "0.20.2", 5147 + "@esbuild/linux-loong64": "0.20.2", 5148 + "@esbuild/linux-mips64el": "0.20.2", 5149 + "@esbuild/linux-ppc64": "0.20.2", 5150 + "@esbuild/linux-riscv64": "0.20.2", 5151 + "@esbuild/linux-s390x": "0.20.2", 5152 + "@esbuild/linux-x64": "0.20.2", 5153 + "@esbuild/netbsd-x64": "0.20.2", 5154 + "@esbuild/openbsd-x64": "0.20.2", 5155 + "@esbuild/sunos-x64": "0.20.2", 5156 + "@esbuild/win32-arm64": "0.20.2", 5157 + "@esbuild/win32-ia32": "0.20.2", 5158 + "@esbuild/win32-x64": "0.20.2" 5140 5159 } 5141 5160 }, 5142 5161 "node_modules/esbuild-loader": { 5143 - "version": "4.0.3", 5144 - "resolved": "https://registry.npmjs.org/esbuild-loader/-/esbuild-loader-4.0.3.tgz", 5145 - "integrity": "sha512-YpaSRisj7TSg6maKKKG9OJGGm0BZ7EXeov8J8cXEYdugjlAJ0wL7aj2JactoQvPJ113v2Ar204pdJWrZsAQc8Q==", 5162 + "version": "4.1.0", 5163 + "resolved": "https://registry.npmjs.org/esbuild-loader/-/esbuild-loader-4.1.0.tgz", 5164 + "integrity": "sha512-543TtIvqbqouEMlOHg4xKoDQkmdImlwIpyAIgpUtDPvMuklU/c2k+Qt2O3VeDBgAwozxmlEbjOzV+F8CZ0g+Bw==", 5146 5165 "dependencies": { 5147 - "esbuild": "^0.19.0", 5166 + "esbuild": "^0.20.0", 5148 5167 "get-tsconfig": "^4.7.0", 5149 5168 "loader-utils": "^2.0.4", 5150 5169 "webpack-sources": "^1.4.3" ··· 5677 5696 } 5678 5697 }, 5679 5698 "node_modules/eslint-plugin-regexp": { 5680 - "version": "2.2.0", 5681 - "resolved": "https://registry.npmjs.org/eslint-plugin-regexp/-/eslint-plugin-regexp-2.2.0.tgz", 5682 - "integrity": "sha512-0kwpiWiLRVBkVr3oIRQLl196sXP/NF6DQFefv9jtR4ZOgQR+6WID2pIZ0I+wIt54qgBPwBB7Gm2a+ueh8/WsFQ==", 5699 + "version": "2.3.0", 5700 + "resolved": "https://registry.npmjs.org/eslint-plugin-regexp/-/eslint-plugin-regexp-2.3.0.tgz", 5701 + "integrity": "sha512-T8JUs7ssRGbuXb+CGfdUJbcxTBMCNOpNqNBLuC8JUKAEIez72J37RaOi5/4dAUsGz92GbWVtqTLPSJZGyP/sQA==", 5683 5702 "dev": true, 5684 5703 "dependencies": { 5685 5704 "@eslint-community/eslint-utils": "^4.2.0", ··· 5743 5762 } 5744 5763 }, 5745 5764 "node_modules/eslint-plugin-vitest": { 5746 - "version": "0.3.22", 5747 - "resolved": "https://registry.npmjs.org/eslint-plugin-vitest/-/eslint-plugin-vitest-0.3.22.tgz", 5748 - "integrity": "sha512-atkFGQ7aVgcuSeSMDqnyevIyUpfBPMnosksgEPrKE7Y8xQlqG/5z2IQ6UDau05zXaaFv7Iz8uzqvIuKshjZ0Zw==", 5765 + "version": "0.3.26", 5766 + "resolved": "https://registry.npmjs.org/eslint-plugin-vitest/-/eslint-plugin-vitest-0.3.26.tgz", 5767 + "integrity": "sha512-oxe5JSPgRjco8caVLTh7Ti8PxpwJdhSV0hTQAmkFcNcmy/9DnqLB/oNVRA11RmVRP//2+jIIT6JuBEcpW3obYg==", 5749 5768 "dev": true, 5750 5769 "dependencies": { 5751 - "@typescript-eslint/utils": "^6.21.0" 5770 + "@typescript-eslint/utils": "^7.1.1" 5752 5771 }, 5753 5772 "engines": { 5754 5773 "node": "^18.0.0 || >= 20.0.0" ··· 5772 5791 "integrity": "sha512-WE+YlK9X9s4vf5EaYRU0Scw7WItDZStm+PapFSYlg2ABNtaQ4zIG7wEqpoUB3SlfM+SgkhgmzR0TeJOO5k3/Nw==", 5773 5792 "dev": true 5774 5793 }, 5775 - "node_modules/eslint-plugin-vitest/node_modules/@typescript-eslint/scope-manager": { 5776 - "version": "6.21.0", 5777 - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz", 5778 - "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==", 5779 - "dev": true, 5780 - "dependencies": { 5781 - "@typescript-eslint/types": "6.21.0", 5782 - "@typescript-eslint/visitor-keys": "6.21.0" 5783 - }, 5784 - "engines": { 5785 - "node": "^16.0.0 || >=18.0.0" 5786 - }, 5787 - "funding": { 5788 - "type": "opencollective", 5789 - "url": "https://opencollective.com/typescript-eslint" 5790 - } 5791 - }, 5792 - "node_modules/eslint-plugin-vitest/node_modules/@typescript-eslint/types": { 5793 - "version": "6.21.0", 5794 - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz", 5795 - "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==", 5796 - "dev": true, 5797 - "engines": { 5798 - "node": "^16.0.0 || >=18.0.0" 5799 - }, 5800 - "funding": { 5801 - "type": "opencollective", 5802 - "url": "https://opencollective.com/typescript-eslint" 5803 - } 5804 - }, 5805 - "node_modules/eslint-plugin-vitest/node_modules/@typescript-eslint/typescript-estree": { 5806 - "version": "6.21.0", 5807 - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz", 5808 - "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==", 5809 - "dev": true, 5810 - "dependencies": { 5811 - "@typescript-eslint/types": "6.21.0", 5812 - "@typescript-eslint/visitor-keys": "6.21.0", 5813 - "debug": "^4.3.4", 5814 - "globby": "^11.1.0", 5815 - "is-glob": "^4.0.3", 5816 - "minimatch": "9.0.3", 5817 - "semver": "^7.5.4", 5818 - "ts-api-utils": "^1.0.1" 5819 - }, 5820 - "engines": { 5821 - "node": "^16.0.0 || >=18.0.0" 5822 - }, 5823 - "funding": { 5824 - "type": "opencollective", 5825 - "url": "https://opencollective.com/typescript-eslint" 5826 - }, 5827 - "peerDependenciesMeta": { 5828 - "typescript": { 5829 - "optional": true 5830 - } 5831 - } 5832 - }, 5833 - "node_modules/eslint-plugin-vitest/node_modules/@typescript-eslint/utils": { 5834 - "version": "6.21.0", 5835 - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.21.0.tgz", 5836 - "integrity": "sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==", 5837 - "dev": true, 5838 - "dependencies": { 5839 - "@eslint-community/eslint-utils": "^4.4.0", 5840 - "@types/json-schema": "^7.0.12", 5841 - "@types/semver": "^7.5.0", 5842 - "@typescript-eslint/scope-manager": "6.21.0", 5843 - "@typescript-eslint/types": "6.21.0", 5844 - "@typescript-eslint/typescript-estree": "6.21.0", 5845 - "semver": "^7.5.4" 5846 - }, 5847 - "engines": { 5848 - "node": "^16.0.0 || >=18.0.0" 5849 - }, 5850 - "funding": { 5851 - "type": "opencollective", 5852 - "url": "https://opencollective.com/typescript-eslint" 5853 - }, 5854 - "peerDependencies": { 5855 - "eslint": "^7.0.0 || ^8.0.0" 5856 - } 5857 - }, 5858 - "node_modules/eslint-plugin-vitest/node_modules/@typescript-eslint/visitor-keys": { 5859 - "version": "6.21.0", 5860 - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", 5861 - "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", 5862 - "dev": true, 5863 - "dependencies": { 5864 - "@typescript-eslint/types": "6.21.0", 5865 - "eslint-visitor-keys": "^3.4.1" 5866 - }, 5867 - "engines": { 5868 - "node": "^16.0.0 || >=18.0.0" 5869 - }, 5870 - "funding": { 5871 - "type": "opencollective", 5872 - "url": "https://opencollective.com/typescript-eslint" 5873 - } 5874 - }, 5875 5794 "node_modules/eslint-plugin-vue": { 5876 - "version": "9.22.0", 5877 - "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.22.0.tgz", 5878 - "integrity": "sha512-7wCXv5zuVnBtZE/74z4yZ0CM8AjH6bk4MQGm7hZjUC2DBppKU5ioeOk5LGSg/s9a1ZJnIsdPLJpXnu1Rc+cVHg==", 5795 + "version": "9.23.0", 5796 + "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.23.0.tgz", 5797 + "integrity": "sha512-Bqd/b7hGYGrlV+wP/g77tjyFmp81lh5TMw0be9093X02SyelxRRfCI6/IsGq/J7Um0YwB9s0Ry0wlFyjPdmtUw==", 5879 5798 "dev": true, 5880 5799 "dependencies": { 5881 5800 "@eslint-community/eslint-utils": "^4.4.0", ··· 6512 6431 } 6513 6432 }, 6514 6433 "node_modules/get-tsconfig": { 6515 - "version": "4.7.2", 6516 - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.2.tgz", 6517 - "integrity": "sha512-wuMsz4leaj5hbGgg4IvDU0bqJagpftG5l5cXIAvo8uZrqn0NJqwtfupTN00VnkQJPcIRrxYrm1Ue24btpCha2A==", 6434 + "version": "4.7.3", 6435 + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.3.tgz", 6436 + "integrity": "sha512-ZvkrzoUA0PQZM6fy6+/Hce561s+faD1rsNwhnO5FelNjyy7EMGJ3Rz1AQ8GYDWjhRs/7dBLOEJvhK8MiEJOAFg==", 6518 6437 "dependencies": { 6519 6438 "resolve-pkg-maps": "^1.0.0" 6520 6439 }, ··· 6787 6706 "integrity": "sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg==" 6788 6707 }, 6789 6708 "node_modules/hasown": { 6790 - "version": "2.0.1", 6791 - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.1.tgz", 6792 - "integrity": "sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==", 6709 + "version": "2.0.2", 6710 + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", 6711 + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", 6793 6712 "dependencies": { 6794 6713 "function-bind": "^1.1.2" 6795 6714 }, ··· 6861 6780 } 6862 6781 }, 6863 6782 "node_modules/htmx.org": { 6864 - "version": "1.9.10", 6865 - "resolved": "https://registry.npmjs.org/htmx.org/-/htmx.org-1.9.10.tgz", 6866 - "integrity": "sha512-UgchasltTCrTuU2DQLom3ohHrBvwr7OqpwyAVJ9VxtNBng4XKkVsqrv0Qr3srqvM9ZNI3f1MmvVQQqK7KW/bTA==" 6783 + "version": "1.9.11", 6784 + "resolved": "https://registry.npmjs.org/htmx.org/-/htmx.org-1.9.11.tgz", 6785 + "integrity": "sha512-WlVuICn8dfNOOgYmdYzYG8zSnP3++AdHkMHooQAzGZObWpVXYathpz/I37ycF4zikR6YduzfCvEcxk20JkIUsw==" 6867 6786 }, 6868 6787 "node_modules/http-proxy-agent": { 6869 6788 "version": "7.0.2", ··· 7031 6950 "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 7032 6951 }, 7033 6952 "node_modules/ini": { 7034 - "version": "4.1.1", 7035 - "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.1.tgz", 7036 - "integrity": "sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==", 6953 + "version": "4.1.2", 6954 + "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.2.tgz", 6955 + "integrity": "sha512-AMB1mvwR1pyBFY/nSevUX6y8nJWS63/SzUKD3JyQn97s4xgIdgQPT75IRouIiBAN4yLQBUShNYVW0+UG25daCw==", 7037 6956 "dev": true, 7038 6957 "engines": { 7039 6958 "node": "^14.17.0 || ^16.13.0 || >=18.0.0" ··· 7271 7190 } 7272 7191 }, 7273 7192 "node_modules/is-map": { 7274 - "version": "2.0.2", 7275 - "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz", 7276 - "integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==", 7193 + "version": "2.0.3", 7194 + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", 7195 + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", 7277 7196 "dev": true, 7197 + "engines": { 7198 + "node": ">= 0.4" 7199 + }, 7278 7200 "funding": { 7279 7201 "url": "https://github.com/sponsors/ljharb" 7280 7202 } ··· 7384 7306 } 7385 7307 }, 7386 7308 "node_modules/is-set": { 7387 - "version": "2.0.2", 7388 - "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz", 7389 - "integrity": "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==", 7309 + "version": "2.0.3", 7310 + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", 7311 + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", 7390 7312 "dev": true, 7313 + "engines": { 7314 + "node": ">= 0.4" 7315 + }, 7391 7316 "funding": { 7392 7317 "url": "https://github.com/sponsors/ljharb" 7393 7318 } ··· 7474 7399 } 7475 7400 }, 7476 7401 "node_modules/is-weakmap": { 7477 - "version": "2.0.1", 7478 - "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz", 7479 - "integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==", 7402 + "version": "2.0.2", 7403 + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", 7404 + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", 7480 7405 "dev": true, 7406 + "engines": { 7407 + "node": ">= 0.4" 7408 + }, 7481 7409 "funding": { 7482 7410 "url": "https://github.com/sponsors/ljharb" 7483 7411 } ··· 7495 7423 } 7496 7424 }, 7497 7425 "node_modules/is-weakset": { 7498 - "version": "2.0.2", 7499 - "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.2.tgz", 7500 - "integrity": "sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==", 7426 + "version": "2.0.3", 7427 + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.3.tgz", 7428 + "integrity": "sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==", 7501 7429 "dev": true, 7502 7430 "dependencies": { 7503 - "call-bind": "^1.0.2", 7504 - "get-intrinsic": "^1.1.1" 7431 + "call-bind": "^1.0.7", 7432 + "get-intrinsic": "^1.2.4" 7433 + }, 7434 + "engines": { 7435 + "node": ">= 0.4" 7505 7436 }, 7506 7437 "funding": { 7507 7438 "url": "https://github.com/sponsors/ljharb" ··· 8343 8274 } 8344 8275 }, 8345 8276 "node_modules/mermaid": { 8346 - "version": "10.8.0", 8347 - "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-10.8.0.tgz", 8348 - "integrity": "sha512-9CzfSreRjdDJxX796+jW4zjEq0DVw5xVF0nWsqff8OTbrt+ml0TZ5PyYUjjUZJa2NYxYJZZXewEquxGiM8qZEA==", 8277 + "version": "10.9.0", 8278 + "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-10.9.0.tgz", 8279 + "integrity": "sha512-swZju0hFox/B/qoLKK0rOxxgh8Cf7rJSfAUc1u8fezVihYMvrJAS45GzAxTVf4Q+xn9uMgitBcmWk7nWGXOs/g==", 8349 8280 "dependencies": { 8350 8281 "@braintree/sanitize-url": "^6.0.1", 8351 8282 "@types/d3-scale": "^4.0.3", ··· 8358 8289 "dayjs": "^1.11.7", 8359 8290 "dompurify": "^3.0.5", 8360 8291 "elkjs": "^0.9.0", 8292 + "katex": "^0.16.9", 8361 8293 "khroma": "^2.0.0", 8362 8294 "lodash-es": "^4.17.21", 8363 8295 "mdast-util-from-markdown": "^1.3.0", ··· 8904 8836 } 8905 8837 }, 8906 8838 "node_modules/monaco-editor": { 8907 - "version": "0.46.0", 8908 - "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.46.0.tgz", 8909 - "integrity": "sha512-ADwtLIIww+9FKybWscd7OCfm9odsFYHImBRI1v9AviGce55QY8raT+9ihH8jX/E/e6QVSGM+pKj4jSUSRmALNQ==" 8839 + "version": "0.47.0", 8840 + "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.47.0.tgz", 8841 + "integrity": "sha512-VabVvHvQ9QmMwXu4du008ZDuyLnHs9j7ThVFsiJoXSOQk18+LF89N4ADzPbFenm0W4V2bGHnFBztIRQTgBfxzw==" 8910 8842 }, 8911 8843 "node_modules/monaco-editor-webpack-plugin": { 8912 8844 "version": "7.1.0", ··· 9816 9748 "postcss": "^8.2.14" 9817 9749 } 9818 9750 }, 9751 + "node_modules/postcss-nesting": { 9752 + "version": "12.1.0", 9753 + "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-12.1.0.tgz", 9754 + "integrity": "sha512-QOYnosaZ+mlP6plQrAxFw09UUp2Sgtxj1BVHN+rSVbtV0Yx48zRt9/9F/ZOoxOKBBEsaJk2MYhhVRjeRRw5yuw==", 9755 + "funding": [ 9756 + { 9757 + "type": "github", 9758 + "url": "https://github.com/sponsors/csstools" 9759 + }, 9760 + { 9761 + "type": "opencollective", 9762 + "url": "https://opencollective.com/csstools" 9763 + } 9764 + ], 9765 + "dependencies": { 9766 + "@csstools/selector-resolve-nested": "^1.1.0", 9767 + "@csstools/selector-specificity": "^3.0.2", 9768 + "postcss-selector-parser": "^6.0.13" 9769 + }, 9770 + "engines": { 9771 + "node": "^14 || ^16 || >=18" 9772 + }, 9773 + "peerDependencies": { 9774 + "postcss": "^8.4" 9775 + } 9776 + }, 9819 9777 "node_modules/postcss-resolve-nested-selector": { 9820 9778 "version": "0.1.1", 9821 9779 "resolved": "https://registry.npmjs.org/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.1.tgz", ··· 9865 9823 } 9866 9824 }, 9867 9825 "node_modules/postcss-selector-parser": { 9868 - "version": "6.0.15", 9869 - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.15.tgz", 9870 - "integrity": "sha512-rEYkQOMUCEMhsKbK66tbEU9QVIxbhN18YiniAwA7XQYTVBqrBy+P2p5JcdqsHgKM2zWylp8d7J6eszocfds5Sw==", 9826 + "version": "6.0.16", 9827 + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.16.tgz", 9828 + "integrity": "sha512-A0RVJrX+IUkVZbW3ClroRWurercFhieevHB38sr2+l9eUClMqome3LmEmnhlNy+5Mr2EYN6B2Kaw9wYdd+VHiw==", 9871 9829 "dependencies": { 9872 9830 "cssesc": "^3.0.0", 9873 9831 "util-deprecate": "^1.0.2" ··· 10471 10429 } 10472 10430 }, 10473 10431 "node_modules/safe-array-concat": { 10474 - "version": "1.1.0", 10475 - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.0.tgz", 10476 - "integrity": "sha512-ZdQ0Jeb9Ofti4hbt5lX3T2JcAamT9hfzYU1MNB+z/jaEbB6wfFfPIR/zEORmZqobkCCJhSjodobH6WHNmJ97dg==", 10432 + "version": "1.1.2", 10433 + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz", 10434 + "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==", 10477 10435 "dev": true, 10478 10436 "dependencies": { 10479 - "call-bind": "^1.0.5", 10480 - "get-intrinsic": "^1.2.2", 10437 + "call-bind": "^1.0.7", 10438 + "get-intrinsic": "^1.2.4", 10481 10439 "has-symbols": "^1.0.3", 10482 10440 "isarray": "^2.0.5" 10483 10441 }, ··· 10608 10566 } 10609 10567 }, 10610 10568 "node_modules/seroval": { 10611 - "version": "1.0.4", 10612 - "resolved": "https://registry.npmjs.org/seroval/-/seroval-1.0.4.tgz", 10613 - "integrity": "sha512-qQs/N+KfJu83rmszFQaTxcoJoPn6KNUruX4KmnmyD0oZkUoiNvJ1rpdYKDf4YHM05k+HOgCxa3yvf15QbVijGg==", 10569 + "version": "1.0.5", 10570 + "resolved": "https://registry.npmjs.org/seroval/-/seroval-1.0.5.tgz", 10571 + "integrity": "sha512-TM+Z11tHHvQVQKeNlOUonOWnsNM+2IBwZ4vwoi4j3zKzIpc5IDw8WPwCfcc8F17wy6cBcJGbZbFOR0UCuTZHQA==", 10614 10572 "engines": { 10615 10573 "node": ">=10" 10616 10574 } 10617 10575 }, 10618 10576 "node_modules/seroval-plugins": { 10619 - "version": "1.0.4", 10620 - "resolved": "https://registry.npmjs.org/seroval-plugins/-/seroval-plugins-1.0.4.tgz", 10621 - "integrity": "sha512-DQ2IK6oQVvy8k+c2V5x5YCtUa/GGGsUwUBNN9UqohrZ0rWdUapBFpNMYP1bCyRHoxOJjdKGl+dieacFIpU/i1A==", 10577 + "version": "1.0.5", 10578 + "resolved": "https://registry.npmjs.org/seroval-plugins/-/seroval-plugins-1.0.5.tgz", 10579 + "integrity": "sha512-8+pDC1vOedPXjKG7oz8o+iiHrtF2WswaMQJ7CKFpccvSYfrzmvKY9zOJWCg+881722wIHfwkdnRmiiDm9ym+zQ==", 10622 10580 "engines": { 10623 10581 "node": ">=10" 10624 10582 }, ··· 10627 10585 } 10628 10586 }, 10629 10587 "node_modules/set-function-length": { 10630 - "version": "1.2.1", 10631 - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.1.tgz", 10632 - "integrity": "sha512-j4t6ccc+VsKwYHso+kElc5neZpjtq9EnRICFZtWyBsLojhmeF/ZBd/elqm22WJh/BziDe/SBiOeAt0m2mfLD0g==", 10588 + "version": "1.2.2", 10589 + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", 10590 + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", 10633 10591 "dev": true, 10634 10592 "dependencies": { 10635 - "define-data-property": "^1.1.2", 10593 + "define-data-property": "^1.1.4", 10636 10594 "es-errors": "^1.3.0", 10637 10595 "function-bind": "^1.1.2", 10638 - "get-intrinsic": "^1.2.3", 10596 + "get-intrinsic": "^1.2.4", 10639 10597 "gopd": "^1.0.1", 10640 - "has-property-descriptors": "^1.0.1" 10598 + "has-property-descriptors": "^1.0.2" 10641 10599 }, 10642 10600 "engines": { 10643 10601 "node": ">= 0.4" ··· 11424 11382 } 11425 11383 }, 11426 11384 "node_modules/swagger-ui-dist": { 11427 - "version": "5.11.8", 11428 - "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.11.8.tgz", 11429 - "integrity": "sha512-IfPtCPdf6opT5HXrzHO4kjL1eco0/8xJCtcs7ilhKuzatrpF2j9s+3QbOag6G3mVFKf+g+Ca5UG9DquVUs2obA==" 11385 + "version": "5.12.0", 11386 + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.12.0.tgz", 11387 + "integrity": "sha512-Rt1xUpbHulJVGbiQjq9yy9/r/0Pg6TmpcG+fXTaMePDc8z5WUw4LfaWts5qcNv/8ewPvBIbY7DKq7qReIKNCCQ==" 11430 11388 }, 11431 11389 "node_modules/symbol-tree": { 11432 11390 "version": "3.2.4", ··· 11567 11525 "node": ">=6" 11568 11526 } 11569 11527 }, 11528 + "node_modules/temporal-polyfill": { 11529 + "version": "0.2.3", 11530 + "resolved": "https://registry.npmjs.org/temporal-polyfill/-/temporal-polyfill-0.2.3.tgz", 11531 + "integrity": "sha512-7ZJRc7wq/1XjrOQYkkNpgo2qfE9XLrUU8D/DS+LAC/T0bYqZ46rW6dow0sOTXTPZS4bwer8bD/0OyuKQBfA3yw==", 11532 + "dependencies": { 11533 + "temporal-spec": "^0.2.0" 11534 + } 11535 + }, 11536 + "node_modules/temporal-spec": { 11537 + "version": "0.2.0", 11538 + "resolved": "https://registry.npmjs.org/temporal-spec/-/temporal-spec-0.2.0.tgz", 11539 + "integrity": "sha512-r1AT0XdEp8TMQ13FLvOt8mOtAxDQsRt2QU5rSWCA7YfshddU651Y1NHVrceLANvixKdf9fYO8B/S9fXHodB7HQ==" 11540 + }, 11570 11541 "node_modules/terser": { 11571 - "version": "5.28.1", 11572 - "resolved": "https://registry.npmjs.org/terser/-/terser-5.28.1.tgz", 11573 - "integrity": "sha512-wM+bZp54v/E9eRRGXb5ZFDvinrJIOaTapx3WUokyVGZu5ucVCK55zEgGd5Dl2fSr3jUo5sDiERErUWLY6QPFyA==", 11542 + "version": "5.29.2", 11543 + "resolved": "https://registry.npmjs.org/terser/-/terser-5.29.2.tgz", 11544 + "integrity": "sha512-ZiGkhUBIM+7LwkNjXYJq8svgkd+QK3UUr0wJqY4MieaezBSAIPgbSPZyIx0idM6XWK5CMzSWa8MJIzmRcB8Caw==", 11574 11545 "dependencies": { 11575 11546 "@jridgewell/source-map": "^0.3.3", 11576 11547 "acorn": "^8.8.2", ··· 11795 11766 "integrity": "sha512-B5CXihaVzXw+1UHhNFyAwUTMDk1EfoLP5Tj1VhD9yybZ1I8DZJEv8tZ1l0RJo0t0tk9ZhR8eG5tEsaCvRigmdQ==" 11796 11767 }, 11797 11768 "node_modules/ts-api-utils": { 11798 - "version": "1.2.1", 11799 - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.2.1.tgz", 11800 - "integrity": "sha512-RIYA36cJn2WiH9Hy77hdF9r7oEwxAtB/TS9/S4Qd90Ap4z5FSiin5zEiTL44OII1Y3IIlEvxwxFUVgrHSZ/UpA==", 11769 + "version": "1.3.0", 11770 + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", 11771 + "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", 11801 11772 "dev": true, 11802 11773 "engines": { 11803 11774 "node": ">=16" ··· 11956 11927 } 11957 11928 }, 11958 11929 "node_modules/typescript": { 11959 - "version": "5.3.3", 11960 - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", 11961 - "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", 11930 + "version": "5.4.2", 11931 + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.2.tgz", 11932 + "integrity": "sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==", 11962 11933 "devOptional": true, 11963 11934 "peer": true, 11964 11935 "bin": { ··· 12062 12033 } 12063 12034 }, 12064 12035 "node_modules/updates": { 12065 - "version": "15.1.2", 12066 - "resolved": "https://registry.npmjs.org/updates/-/updates-15.1.2.tgz", 12067 - "integrity": "sha512-+/JT4NChl82iexV9G80TY5HF3ubQ5O9UTOk3LlCo4Y4aRCYvo1h4bJE8YkP0PE7KiFRWIQq/rPmUYrY2QF8wVA==", 12036 + "version": "15.3.1", 12037 + "resolved": "https://registry.npmjs.org/updates/-/updates-15.3.1.tgz", 12038 + "integrity": "sha512-DqHT1aJ6p6jVLWRiAeuVx/TQotvEwUjgrY1Mlc0a2qYk+eKEQVXugQ4M+6QoVMA3X1NFAVsb02d93pmWam4bBA==", 12068 12039 "dev": true, 12069 12040 "bin": { 12070 12041 "updates": "bin/updates.js" ··· 12160 12131 } 12161 12132 }, 12162 12133 "node_modules/vite": { 12163 - "version": "5.1.4", 12164 - "resolved": "https://registry.npmjs.org/vite/-/vite-5.1.4.tgz", 12165 - "integrity": "sha512-n+MPqzq+d9nMVTKyewqw6kSt+R3CkvF9QAKY8obiQn8g1fwTscKxyfaYnC632HtBXAQGc1Yjomphwn1dtwGAHg==", 12134 + "version": "5.1.6", 12135 + "resolved": "https://registry.npmjs.org/vite/-/vite-5.1.6.tgz", 12136 + "integrity": "sha512-yYIAZs9nVfRJ/AiOLCA91zzhjsHUgMjB+EigzFb6W2XTLO8JixBCKCjvhKZaye+NKYHCrkv3Oh50dH9EdLU2RA==", 12166 12137 "dev": true, 12167 12138 "dependencies": { 12168 12139 "esbuild": "^0.19.3", ··· 12242 12213 "integrity": "sha512-KRCIFX3PWVUuEjpi9O7EKLT9E27OqOA3RimIvVx6cziLAUxvnk2VvHQfMrP+mKkqyqqSmnnYyTig3OyDnK/zlA==", 12243 12214 "dev": true 12244 12215 }, 12216 + "node_modules/vite/node_modules/@esbuild/aix-ppc64": { 12217 + "version": "0.19.12", 12218 + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz", 12219 + "integrity": "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==", 12220 + "cpu": [ 12221 + "ppc64" 12222 + ], 12223 + "dev": true, 12224 + "optional": true, 12225 + "os": [ 12226 + "aix" 12227 + ], 12228 + "engines": { 12229 + "node": ">=12" 12230 + } 12231 + }, 12232 + "node_modules/vite/node_modules/@esbuild/android-arm": { 12233 + "version": "0.19.12", 12234 + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.12.tgz", 12235 + "integrity": "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==", 12236 + "cpu": [ 12237 + "arm" 12238 + ], 12239 + "dev": true, 12240 + "optional": true, 12241 + "os": [ 12242 + "android" 12243 + ], 12244 + "engines": { 12245 + "node": ">=12" 12246 + } 12247 + }, 12248 + "node_modules/vite/node_modules/@esbuild/android-arm64": { 12249 + "version": "0.19.12", 12250 + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz", 12251 + "integrity": "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==", 12252 + "cpu": [ 12253 + "arm64" 12254 + ], 12255 + "dev": true, 12256 + "optional": true, 12257 + "os": [ 12258 + "android" 12259 + ], 12260 + "engines": { 12261 + "node": ">=12" 12262 + } 12263 + }, 12264 + "node_modules/vite/node_modules/@esbuild/android-x64": { 12265 + "version": "0.19.12", 12266 + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.12.tgz", 12267 + "integrity": "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==", 12268 + "cpu": [ 12269 + "x64" 12270 + ], 12271 + "dev": true, 12272 + "optional": true, 12273 + "os": [ 12274 + "android" 12275 + ], 12276 + "engines": { 12277 + "node": ">=12" 12278 + } 12279 + }, 12280 + "node_modules/vite/node_modules/@esbuild/darwin-arm64": { 12281 + "version": "0.19.12", 12282 + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz", 12283 + "integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==", 12284 + "cpu": [ 12285 + "arm64" 12286 + ], 12287 + "dev": true, 12288 + "optional": true, 12289 + "os": [ 12290 + "darwin" 12291 + ], 12292 + "engines": { 12293 + "node": ">=12" 12294 + } 12295 + }, 12296 + "node_modules/vite/node_modules/@esbuild/darwin-x64": { 12297 + "version": "0.19.12", 12298 + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz", 12299 + "integrity": "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==", 12300 + "cpu": [ 12301 + "x64" 12302 + ], 12303 + "dev": true, 12304 + "optional": true, 12305 + "os": [ 12306 + "darwin" 12307 + ], 12308 + "engines": { 12309 + "node": ">=12" 12310 + } 12311 + }, 12312 + "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { 12313 + "version": "0.19.12", 12314 + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz", 12315 + "integrity": "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==", 12316 + "cpu": [ 12317 + "arm64" 12318 + ], 12319 + "dev": true, 12320 + "optional": true, 12321 + "os": [ 12322 + "freebsd" 12323 + ], 12324 + "engines": { 12325 + "node": ">=12" 12326 + } 12327 + }, 12328 + "node_modules/vite/node_modules/@esbuild/freebsd-x64": { 12329 + "version": "0.19.12", 12330 + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz", 12331 + "integrity": "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==", 12332 + "cpu": [ 12333 + "x64" 12334 + ], 12335 + "dev": true, 12336 + "optional": true, 12337 + "os": [ 12338 + "freebsd" 12339 + ], 12340 + "engines": { 12341 + "node": ">=12" 12342 + } 12343 + }, 12344 + "node_modules/vite/node_modules/@esbuild/linux-arm": { 12345 + "version": "0.19.12", 12346 + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz", 12347 + "integrity": "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==", 12348 + "cpu": [ 12349 + "arm" 12350 + ], 12351 + "dev": true, 12352 + "optional": true, 12353 + "os": [ 12354 + "linux" 12355 + ], 12356 + "engines": { 12357 + "node": ">=12" 12358 + } 12359 + }, 12360 + "node_modules/vite/node_modules/@esbuild/linux-arm64": { 12361 + "version": "0.19.12", 12362 + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz", 12363 + "integrity": "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==", 12364 + "cpu": [ 12365 + "arm64" 12366 + ], 12367 + "dev": true, 12368 + "optional": true, 12369 + "os": [ 12370 + "linux" 12371 + ], 12372 + "engines": { 12373 + "node": ">=12" 12374 + } 12375 + }, 12376 + "node_modules/vite/node_modules/@esbuild/linux-ia32": { 12377 + "version": "0.19.12", 12378 + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz", 12379 + "integrity": "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==", 12380 + "cpu": [ 12381 + "ia32" 12382 + ], 12383 + "dev": true, 12384 + "optional": true, 12385 + "os": [ 12386 + "linux" 12387 + ], 12388 + "engines": { 12389 + "node": ">=12" 12390 + } 12391 + }, 12392 + "node_modules/vite/node_modules/@esbuild/linux-loong64": { 12393 + "version": "0.19.12", 12394 + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz", 12395 + "integrity": "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==", 12396 + "cpu": [ 12397 + "loong64" 12398 + ], 12399 + "dev": true, 12400 + "optional": true, 12401 + "os": [ 12402 + "linux" 12403 + ], 12404 + "engines": { 12405 + "node": ">=12" 12406 + } 12407 + }, 12408 + "node_modules/vite/node_modules/@esbuild/linux-mips64el": { 12409 + "version": "0.19.12", 12410 + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz", 12411 + "integrity": "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==", 12412 + "cpu": [ 12413 + "mips64el" 12414 + ], 12415 + "dev": true, 12416 + "optional": true, 12417 + "os": [ 12418 + "linux" 12419 + ], 12420 + "engines": { 12421 + "node": ">=12" 12422 + } 12423 + }, 12424 + "node_modules/vite/node_modules/@esbuild/linux-ppc64": { 12425 + "version": "0.19.12", 12426 + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz", 12427 + "integrity": "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==", 12428 + "cpu": [ 12429 + "ppc64" 12430 + ], 12431 + "dev": true, 12432 + "optional": true, 12433 + "os": [ 12434 + "linux" 12435 + ], 12436 + "engines": { 12437 + "node": ">=12" 12438 + } 12439 + }, 12440 + "node_modules/vite/node_modules/@esbuild/linux-riscv64": { 12441 + "version": "0.19.12", 12442 + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz", 12443 + "integrity": "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==", 12444 + "cpu": [ 12445 + "riscv64" 12446 + ], 12447 + "dev": true, 12448 + "optional": true, 12449 + "os": [ 12450 + "linux" 12451 + ], 12452 + "engines": { 12453 + "node": ">=12" 12454 + } 12455 + }, 12456 + "node_modules/vite/node_modules/@esbuild/linux-s390x": { 12457 + "version": "0.19.12", 12458 + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz", 12459 + "integrity": "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==", 12460 + "cpu": [ 12461 + "s390x" 12462 + ], 12463 + "dev": true, 12464 + "optional": true, 12465 + "os": [ 12466 + "linux" 12467 + ], 12468 + "engines": { 12469 + "node": ">=12" 12470 + } 12471 + }, 12472 + "node_modules/vite/node_modules/@esbuild/linux-x64": { 12473 + "version": "0.19.12", 12474 + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz", 12475 + "integrity": "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==", 12476 + "cpu": [ 12477 + "x64" 12478 + ], 12479 + "dev": true, 12480 + "optional": true, 12481 + "os": [ 12482 + "linux" 12483 + ], 12484 + "engines": { 12485 + "node": ">=12" 12486 + } 12487 + }, 12488 + "node_modules/vite/node_modules/@esbuild/netbsd-x64": { 12489 + "version": "0.19.12", 12490 + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz", 12491 + "integrity": "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==", 12492 + "cpu": [ 12493 + "x64" 12494 + ], 12495 + "dev": true, 12496 + "optional": true, 12497 + "os": [ 12498 + "netbsd" 12499 + ], 12500 + "engines": { 12501 + "node": ">=12" 12502 + } 12503 + }, 12504 + "node_modules/vite/node_modules/@esbuild/openbsd-x64": { 12505 + "version": "0.19.12", 12506 + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz", 12507 + "integrity": "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==", 12508 + "cpu": [ 12509 + "x64" 12510 + ], 12511 + "dev": true, 12512 + "optional": true, 12513 + "os": [ 12514 + "openbsd" 12515 + ], 12516 + "engines": { 12517 + "node": ">=12" 12518 + } 12519 + }, 12520 + "node_modules/vite/node_modules/@esbuild/sunos-x64": { 12521 + "version": "0.19.12", 12522 + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz", 12523 + "integrity": "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==", 12524 + "cpu": [ 12525 + "x64" 12526 + ], 12527 + "dev": true, 12528 + "optional": true, 12529 + "os": [ 12530 + "sunos" 12531 + ], 12532 + "engines": { 12533 + "node": ">=12" 12534 + } 12535 + }, 12536 + "node_modules/vite/node_modules/@esbuild/win32-arm64": { 12537 + "version": "0.19.12", 12538 + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz", 12539 + "integrity": "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==", 12540 + "cpu": [ 12541 + "arm64" 12542 + ], 12543 + "dev": true, 12544 + "optional": true, 12545 + "os": [ 12546 + "win32" 12547 + ], 12548 + "engines": { 12549 + "node": ">=12" 12550 + } 12551 + }, 12552 + "node_modules/vite/node_modules/@esbuild/win32-ia32": { 12553 + "version": "0.19.12", 12554 + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz", 12555 + "integrity": "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==", 12556 + "cpu": [ 12557 + "ia32" 12558 + ], 12559 + "dev": true, 12560 + "optional": true, 12561 + "os": [ 12562 + "win32" 12563 + ], 12564 + "engines": { 12565 + "node": ">=12" 12566 + } 12567 + }, 12568 + "node_modules/vite/node_modules/@esbuild/win32-x64": { 12569 + "version": "0.19.12", 12570 + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz", 12571 + "integrity": "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==", 12572 + "cpu": [ 12573 + "x64" 12574 + ], 12575 + "dev": true, 12576 + "optional": true, 12577 + "os": [ 12578 + "win32" 12579 + ], 12580 + "engines": { 12581 + "node": ">=12" 12582 + } 12583 + }, 12245 12584 "node_modules/vite/node_modules/@types/estree": { 12246 12585 "version": "1.0.5", 12247 12586 "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", 12248 12587 "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", 12249 12588 "dev": true 12250 12589 }, 12590 + "node_modules/vite/node_modules/esbuild": { 12591 + "version": "0.19.12", 12592 + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz", 12593 + "integrity": "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==", 12594 + "dev": true, 12595 + "hasInstallScript": true, 12596 + "bin": { 12597 + "esbuild": "bin/esbuild" 12598 + }, 12599 + "engines": { 12600 + "node": ">=12" 12601 + }, 12602 + "optionalDependencies": { 12603 + "@esbuild/aix-ppc64": "0.19.12", 12604 + "@esbuild/android-arm": "0.19.12", 12605 + "@esbuild/android-arm64": "0.19.12", 12606 + "@esbuild/android-x64": "0.19.12", 12607 + "@esbuild/darwin-arm64": "0.19.12", 12608 + "@esbuild/darwin-x64": "0.19.12", 12609 + "@esbuild/freebsd-arm64": "0.19.12", 12610 + "@esbuild/freebsd-x64": "0.19.12", 12611 + "@esbuild/linux-arm": "0.19.12", 12612 + "@esbuild/linux-arm64": "0.19.12", 12613 + "@esbuild/linux-ia32": "0.19.12", 12614 + "@esbuild/linux-loong64": "0.19.12", 12615 + "@esbuild/linux-mips64el": "0.19.12", 12616 + "@esbuild/linux-ppc64": "0.19.12", 12617 + "@esbuild/linux-riscv64": "0.19.12", 12618 + "@esbuild/linux-s390x": "0.19.12", 12619 + "@esbuild/linux-x64": "0.19.12", 12620 + "@esbuild/netbsd-x64": "0.19.12", 12621 + "@esbuild/openbsd-x64": "0.19.12", 12622 + "@esbuild/sunos-x64": "0.19.12", 12623 + "@esbuild/win32-arm64": "0.19.12", 12624 + "@esbuild/win32-ia32": "0.19.12", 12625 + "@esbuild/win32-x64": "0.19.12" 12626 + } 12627 + }, 12251 12628 "node_modules/vite/node_modules/fsevents": { 12252 12629 "version": "2.3.3", 12253 12630 "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", ··· 12263 12640 } 12264 12641 }, 12265 12642 "node_modules/vite/node_modules/rollup": { 12266 - "version": "4.12.0", 12267 - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.12.0.tgz", 12268 - "integrity": "sha512-wz66wn4t1OHIJw3+XU7mJJQV/2NAfw5OAk6G6Hoo3zcvz/XOfQ52Vgi+AN4Uxoxi0KBBwk2g8zPrTDA4btSB/Q==", 12643 + "version": "4.13.0", 12644 + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.13.0.tgz", 12645 + "integrity": "sha512-3YegKemjoQnYKmsBlOHfMLVPPA5xLkQ8MHLLSw/fBrFaVkEayL51DilPpNNLq1exr98F2B1TzrV0FUlN3gWRPg==", 12269 12646 "dev": true, 12270 12647 "dependencies": { 12271 12648 "@types/estree": "1.0.5" ··· 12278 12655 "npm": ">=8.0.0" 12279 12656 }, 12280 12657 "optionalDependencies": { 12281 - "@rollup/rollup-android-arm-eabi": "4.12.0", 12282 - "@rollup/rollup-android-arm64": "4.12.0", 12283 - "@rollup/rollup-darwin-arm64": "4.12.0", 12284 - "@rollup/rollup-darwin-x64": "4.12.0", 12285 - "@rollup/rollup-linux-arm-gnueabihf": "4.12.0", 12286 - "@rollup/rollup-linux-arm64-gnu": "4.12.0", 12287 - "@rollup/rollup-linux-arm64-musl": "4.12.0", 12288 - "@rollup/rollup-linux-riscv64-gnu": "4.12.0", 12289 - "@rollup/rollup-linux-x64-gnu": "4.12.0", 12290 - "@rollup/rollup-linux-x64-musl": "4.12.0", 12291 - "@rollup/rollup-win32-arm64-msvc": "4.12.0", 12292 - "@rollup/rollup-win32-ia32-msvc": "4.12.0", 12293 - "@rollup/rollup-win32-x64-msvc": "4.12.0", 12658 + "@rollup/rollup-android-arm-eabi": "4.13.0", 12659 + "@rollup/rollup-android-arm64": "4.13.0", 12660 + "@rollup/rollup-darwin-arm64": "4.13.0", 12661 + "@rollup/rollup-darwin-x64": "4.13.0", 12662 + "@rollup/rollup-linux-arm-gnueabihf": "4.13.0", 12663 + "@rollup/rollup-linux-arm64-gnu": "4.13.0", 12664 + "@rollup/rollup-linux-arm64-musl": "4.13.0", 12665 + "@rollup/rollup-linux-riscv64-gnu": "4.13.0", 12666 + "@rollup/rollup-linux-x64-gnu": "4.13.0", 12667 + "@rollup/rollup-linux-x64-musl": "4.13.0", 12668 + "@rollup/rollup-win32-arm64-msvc": "4.13.0", 12669 + "@rollup/rollup-win32-ia32-msvc": "4.13.0", 12670 + "@rollup/rollup-win32-x64-msvc": "4.13.0", 12294 12671 "fsevents": "~2.3.2" 12295 12672 } 12296 12673 }, ··· 12360 12737 } 12361 12738 }, 12362 12739 "node_modules/vitest/node_modules/magic-string": { 12363 - "version": "0.30.7", 12364 - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.7.tgz", 12365 - "integrity": "sha512-8vBuFF/I/+OSLRmdf2wwFCJCz+nSn0m6DPvGH1fS/KiQoSaR+sETbov0eIk9KhEKy8CYqIkIAnbohxT/4H0kuA==", 12740 + "version": "0.30.8", 12741 + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.8.tgz", 12742 + "integrity": "sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==", 12366 12743 "dev": true, 12367 12744 "dependencies": { 12368 12745 "@jridgewell/sourcemap-codec": "^1.4.15" ··· 12488 12865 } 12489 12866 }, 12490 12867 "node_modules/watchpack": { 12491 - "version": "2.4.0", 12492 - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", 12493 - "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", 12868 + "version": "2.4.1", 12869 + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.1.tgz", 12870 + "integrity": "sha512-8wrBCMtVhqcXP2Sup1ctSkga6uc2Bx0IIvKyT7yTFier5AXHooSI+QyQQAtTb7+E0IUCCKyTFmXqdqgum2XWGg==", 12494 12871 "dependencies": { 12495 12872 "glob-to-regexp": "^0.4.1", 12496 12873 "graceful-fs": "^4.1.2" ··· 12802 13179 } 12803 13180 }, 12804 13181 "node_modules/which-collection": { 12805 - "version": "1.0.1", 12806 - "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz", 12807 - "integrity": "sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==", 13182 + "version": "1.0.2", 13183 + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", 13184 + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", 12808 13185 "dev": true, 12809 13186 "dependencies": { 12810 - "is-map": "^2.0.1", 12811 - "is-set": "^2.0.1", 12812 - "is-weakmap": "^2.0.1", 12813 - "is-weakset": "^2.0.1" 13187 + "is-map": "^2.0.3", 13188 + "is-set": "^2.0.3", 13189 + "is-weakmap": "^2.0.2", 13190 + "is-weakset": "^2.0.3" 13191 + }, 13192 + "engines": { 13193 + "node": ">= 0.4" 12814 13194 }, 12815 13195 "funding": { 12816 13196 "url": "https://github.com/sponsors/ljharb" 12817 13197 } 12818 13198 }, 12819 13199 "node_modules/which-typed-array": { 12820 - "version": "1.1.14", 12821 - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.14.tgz", 12822 - "integrity": "sha512-VnXFiIW8yNn9kIHN88xvZ4yOWchftKDsRJ8fEPacX/wl1lOvBrhsJ/OeJCXq7B0AaijRuqgzSKalJoPk+D8MPg==", 13200 + "version": "1.1.15", 13201 + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", 13202 + "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", 12823 13203 "dev": true, 12824 13204 "dependencies": { 12825 - "available-typed-arrays": "^1.0.6", 12826 - "call-bind": "^1.0.5", 13205 + "available-typed-arrays": "^1.0.7", 13206 + "call-bind": "^1.0.7", 12827 13207 "for-each": "^0.3.3", 12828 13208 "gopd": "^1.0.1", 12829 - "has-tostringtag": "^1.0.1" 13209 + "has-tostringtag": "^1.0.2" 12830 13210 }, 12831 13211 "engines": { 12832 13212 "node": ">= 0.4" ··· 13015 13395 "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" 13016 13396 }, 13017 13397 "node_modules/yaml": { 13018 - "version": "2.4.0", 13019 - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.0.tgz", 13020 - "integrity": "sha512-j9iR8g+/t0lArF4V6NE/QCfT+CO7iLqrXAHZbJdo+LfjqP1vR8Fg5bSiaq6Q2lOD1AUEVrEVIgABvBFYojJVYQ==", 13398 + "version": "2.4.1", 13399 + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.1.tgz", 13400 + "integrity": "sha512-pIXzoImaqmfOrL7teGUBt/T7ZDnyeGBWyXQBvOVhLkWLN37GXv8NMLK406UY6dS51JfcQHsmcW5cJ441bHg6Lg==", 13021 13401 "bin": { 13022 13402 "yaml": "bin.mjs" 13023 13403 },
+15 -14
package.json
··· 4 4 "node": ">= 18.0.0" 5 5 }, 6 6 "dependencies": { 7 - "@citation-js/core": "0.7.6", 8 - "@citation-js/plugin-bibtex": "0.7.8", 9 - "@citation-js/plugin-csl": "0.7.6", 7 + "@citation-js/core": "0.7.9", 8 + "@citation-js/plugin-bibtex": "0.7.9", 9 + "@citation-js/plugin-csl": "0.7.9", 10 10 "@citation-js/plugin-software-formats": "0.6.1", 11 11 "@claviska/jquery-minicolors": "2.3.6", 12 12 "@github/markdown-toolbar-element": "2.2.3", ··· 14 14 "@github/text-expander-element": "2.6.1", 15 15 "@mcaptcha/vanilla-glue": "0.1.0-alpha-3", 16 16 "@primer/octicons": "19.8.0", 17 - "@webcomponents/custom-elements": "1.6.0", 18 17 "add-asset-webpack-plugin": "2.0.1", 19 18 "ansi_up": "6.0.2", 20 19 "asciinema-player": "3.7.0", ··· 26 25 "dayjs": "1.11.10", 27 26 "dropzone": "6.0.0-beta.2", 28 27 "easymde": "2.18.0", 29 - "esbuild-loader": "4.0.3", 28 + "esbuild-loader": "4.1.0", 30 29 "escape-goat": "4.0.0", 31 30 "fast-glob": "3.3.2", 32 - "htmx.org": "1.9.10", 31 + "htmx.org": "1.9.11", 33 32 "idiomorph": "0.3.0", 34 33 "jquery": "3.7.1", 35 34 "katex": "0.16.9", 36 35 "license-checker-webpack-plugin": "0.2.1", 37 - "mermaid": "10.8.0", 36 + "mermaid": "10.9.0", 38 37 "mini-css-extract-plugin": "2.8.1", 39 38 "minimatch": "9.0.3", 40 - "monaco-editor": "0.46.0", 39 + "monaco-editor": "0.47.0", 41 40 "monaco-editor-webpack-plugin": "7.1.0", 42 41 "pdfobject": "2.3.0", 43 42 "postcss": "8.4.35", 44 43 "postcss-loader": "8.1.1", 44 + "postcss-nesting": "12.1.0", 45 45 "pretty-ms": "9.0.0", 46 46 "sortablejs": "1.15.2", 47 - "swagger-ui-dist": "5.11.8", 47 + "swagger-ui-dist": "5.12.0", 48 48 "tailwindcss": "3.4.1", 49 + "temporal-polyfill": "0.2.3", 49 50 "throttle-debounce": "5.0.0", 50 51 "tinycolor2": "1.6.0", 51 52 "tippy.js": "6.3.7", ··· 65 66 "@eslint-community/eslint-plugin-eslint-comments": "4.1.0", 66 67 "@playwright/test": "1.42.1", 67 68 "@stoplight/spectral-cli": "6.11.0", 68 - "@stylistic/eslint-plugin-js": "1.6.3", 69 + "@stylistic/eslint-plugin-js": "1.7.0", 69 70 "@stylistic/stylelint-plugin": "2.1.0", 70 71 "@vitejs/plugin-vue": "5.0.4", 71 72 "eslint": "8.57.0", ··· 75 76 "eslint-plugin-jquery": "1.5.1", 76 77 "eslint-plugin-no-jquery": "2.7.0", 77 78 "eslint-plugin-no-use-extend-native": "0.5.0", 78 - "eslint-plugin-regexp": "2.2.0", 79 + "eslint-plugin-regexp": "2.3.0", 79 80 "eslint-plugin-sonarjs": "0.24.0", 80 81 "eslint-plugin-unicorn": "51.0.1", 81 - "eslint-plugin-vitest": "0.3.22", 82 + "eslint-plugin-vitest": "0.3.26", 82 83 "eslint-plugin-vitest-globals": "1.4.0", 83 - "eslint-plugin-vue": "9.22.0", 84 + "eslint-plugin-vue": "9.23.0", 84 85 "eslint-plugin-vue-scoped-css": "2.7.2", 85 86 "eslint-plugin-wc": "2.0.4", 86 87 "jsdom": "24.0.0", ··· 90 91 "stylelint-declaration-block-no-ignored-properties": "2.8.0", 91 92 "stylelint-declaration-strict-value": "1.10.4", 92 93 "svgo": "3.2.0", 93 - "updates": "15.1.2", 94 + "updates": "15.3.1", 94 95 "vite-string-plugin": "1.1.5", 95 96 "vitest": "1.3.1" 96 97 },
+9
routers/api/v1/admin/user.go
··· 147 147 } 148 148 return 149 149 } 150 + 151 + if !user_model.IsEmailDomainAllowed(u.Email) { 152 + ctx.Resp.Header().Add("X-Gitea-Warning", fmt.Sprintf("the domain of user email %s conflicts with EMAIL_DOMAIN_ALLOWLIST or EMAIL_DOMAIN_BLOCKLIST", u.Email)) 153 + } 154 + 150 155 log.Trace("Account created by admin (%s): %s", ctx.Doer.Name, u.Name) 151 156 152 157 // Send email notification. ··· 219 224 ctx.Error(http.StatusInternalServerError, "AddOrSetPrimaryEmailAddress", err) 220 225 } 221 226 return 227 + } 228 + 229 + if !user_model.IsEmailDomainAllowed(*form.Email) { 230 + ctx.Resp.Header().Add("X-Gitea-Warning", fmt.Sprintf("the domain of user email %s conflicts with EMAIL_DOMAIN_ALLOWLIST or EMAIL_DOMAIN_BLOCKLIST", *form.Email)) 222 231 } 223 232 } 224 233
+13 -13
routers/api/v1/repo/issue.go
··· 269 269 } 270 270 271 271 if since != 0 { 272 - searchOpt.UpdatedAfterUnix = &since 272 + searchOpt.UpdatedAfterUnix = optional.Some(since) 273 273 } 274 274 if before != 0 { 275 - searchOpt.UpdatedBeforeUnix = &before 275 + searchOpt.UpdatedBeforeUnix = optional.Some(before) 276 276 } 277 277 278 278 if ctx.IsSigned { 279 279 ctxUserID := ctx.Doer.ID 280 280 if ctx.FormBool("created") { 281 - searchOpt.PosterID = &ctxUserID 281 + searchOpt.PosterID = optional.Some(ctxUserID) 282 282 } 283 283 if ctx.FormBool("assigned") { 284 - searchOpt.AssigneeID = &ctxUserID 284 + searchOpt.AssigneeID = optional.Some(ctxUserID) 285 285 } 286 286 if ctx.FormBool("mentioned") { 287 - searchOpt.MentionID = &ctxUserID 287 + searchOpt.MentionID = optional.Some(ctxUserID) 288 288 } 289 289 if ctx.FormBool("review_requested") { 290 - searchOpt.ReviewRequestedID = &ctxUserID 290 + searchOpt.ReviewRequestedID = optional.Some(ctxUserID) 291 291 } 292 292 if ctx.FormBool("reviewed") { 293 - searchOpt.ReviewedID = &ctxUserID 293 + searchOpt.ReviewedID = optional.Some(ctxUserID) 294 294 } 295 295 } 296 296 ··· 368 368 // required: false 369 369 // - name: created_by 370 370 // in: query 371 - // description: Only show items which were created by the the given user 371 + // description: Only show items which were created by the given user 372 372 // type: string 373 373 // - name: assigned_by 374 374 // in: query ··· 502 502 SortBy: issue_indexer.SortByCreatedDesc, 503 503 } 504 504 if since != 0 { 505 - searchOpt.UpdatedAfterUnix = &since 505 + searchOpt.UpdatedAfterUnix = optional.Some(since) 506 506 } 507 507 if before != 0 { 508 - searchOpt.UpdatedBeforeUnix = &before 508 + searchOpt.UpdatedBeforeUnix = optional.Some(before) 509 509 } 510 510 if len(labelIDs) == 1 && labelIDs[0] == 0 { 511 511 searchOpt.NoLabelOnly = true ··· 526 526 } 527 527 528 528 if createdByID > 0 { 529 - searchOpt.PosterID = &createdByID 529 + searchOpt.PosterID = optional.Some(createdByID) 530 530 } 531 531 if assignedByID > 0 { 532 - searchOpt.AssigneeID = &assignedByID 532 + searchOpt.AssigneeID = optional.Some(assignedByID) 533 533 } 534 534 if mentionedByID > 0 { 535 - searchOpt.MentionID = &mentionedByID 535 + searchOpt.MentionID = optional.Some(mentionedByID) 536 536 } 537 537 538 538 ids, total, err := issue_indexer.SearchIssues(ctx, searchOpt)
+2
routers/api/v1/repo/pull.go
··· 1065 1065 return nil, nil, nil, nil, "", "" 1066 1066 } 1067 1067 headBranch = headInfos[1] 1068 + // The head repository can also point to the same repo 1069 + isSameRepo = ctx.Repo.Owner.ID == headUser.ID 1068 1070 1069 1071 } else { 1070 1072 ctx.NotFound()
+1 -1
routers/api/v1/repo/pull_review.go
··· 692 692 return nil, nil, true 693 693 } 694 694 695 - // validate the the review is for the given PR 695 + // validate the review is for the given PR 696 696 if review.IssueID != pr.IssueID { 697 697 ctx.NotFound("ReviewNotInPR") 698 698 return nil, nil, true
+4 -2
routers/common/markup.go
··· 34 34 if err := markdown.RenderRaw(&markup.RenderContext{ 35 35 Ctx: ctx, 36 36 Links: markup.Links{ 37 - Base: urlPrefix, 37 + AbsolutePrefix: true, 38 + Base: urlPrefix, 38 39 }, 39 40 }, strings.NewReader(text), ctx.Resp); err != nil { 40 41 ctx.Error(http.StatusInternalServerError, err.Error()) ··· 79 80 if err := markup.Render(&markup.RenderContext{ 80 81 Ctx: ctx, 81 82 Links: markup.Links{ 82 - Base: urlPrefix, 83 + AbsolutePrefix: true, 84 + Base: urlPrefix, 83 85 }, 84 86 Metas: meta, 85 87 IsWiki: wiki,
+8
routers/web/admin/users.go
··· 202 202 } 203 203 return 204 204 } 205 + 206 + if !user_model.IsEmailDomainAllowed(u.Email) { 207 + ctx.Flash.Warning(ctx.Tr("form.email_domain_is_not_allowed", u.Email)) 208 + } 209 + 205 210 log.Trace("Account created by admin (%s): %s", ctx.Doer.Name, u.Name) 206 211 207 212 // Send email notification. ··· 424 429 ctx.ServerError("AddOrSetPrimaryEmailAddress", err) 425 430 } 426 431 return 432 + } 433 + if !user_model.IsEmailDomainAllowed(form.Email) { 434 + ctx.Flash.Warning(ctx.Tr("form.email_domain_is_not_allowed", form.Email)) 427 435 } 428 436 } 429 437
+1 -6
routers/web/repo/branch.go
··· 148 148 return 149 149 } 150 150 151 - objectFormat, err := git.GetObjectFormatOfRepo(ctx, ctx.Repo.Repository.RepoPath()) 152 - if err != nil { 153 - log.Error("RestoreBranch: CreateBranch: %w", err) 154 - ctx.Flash.Error(ctx.Tr("repo.branch.restore_failed", deletedBranch.Name)) 155 - return 156 - } 151 + objectFormat := git.ObjectFormatFromName(ctx.Repo.Repository.ObjectFormatName) 157 152 158 153 // Don't return error below this 159 154 if err := repo_service.PushUpdate(
+16 -16
routers/web/repo/issue.go
··· 2636 2636 } 2637 2637 } 2638 2638 2639 - var projectID *int64 2639 + projectID := optional.None[int64]() 2640 2640 if v := ctx.FormInt64("project"); v > 0 { 2641 - projectID = &v 2641 + projectID = optional.Some(v) 2642 2642 } 2643 2643 2644 2644 // this api is also used in UI, ··· 2667 2667 } 2668 2668 2669 2669 if since != 0 { 2670 - searchOpt.UpdatedAfterUnix = &since 2670 + searchOpt.UpdatedAfterUnix = optional.Some(since) 2671 2671 } 2672 2672 if before != 0 { 2673 - searchOpt.UpdatedBeforeUnix = &before 2673 + searchOpt.UpdatedBeforeUnix = optional.Some(before) 2674 2674 } 2675 2675 2676 2676 if ctx.IsSigned { 2677 2677 ctxUserID := ctx.Doer.ID 2678 2678 if ctx.FormBool("created") { 2679 - searchOpt.PosterID = &ctxUserID 2679 + searchOpt.PosterID = optional.Some(ctxUserID) 2680 2680 } 2681 2681 if ctx.FormBool("assigned") { 2682 - searchOpt.AssigneeID = &ctxUserID 2682 + searchOpt.AssigneeID = optional.Some(ctxUserID) 2683 2683 } 2684 2684 if ctx.FormBool("mentioned") { 2685 - searchOpt.MentionID = &ctxUserID 2685 + searchOpt.MentionID = optional.Some(ctxUserID) 2686 2686 } 2687 2687 if ctx.FormBool("review_requested") { 2688 - searchOpt.ReviewRequestedID = &ctxUserID 2688 + searchOpt.ReviewRequestedID = optional.Some(ctxUserID) 2689 2689 } 2690 2690 if ctx.FormBool("reviewed") { 2691 - searchOpt.ReviewedID = &ctxUserID 2691 + searchOpt.ReviewedID = optional.Some(ctxUserID) 2692 2692 } 2693 2693 } 2694 2694 ··· 2795 2795 } 2796 2796 } 2797 2797 2798 - var projectID *int64 2798 + projectID := optional.None[int64]() 2799 2799 if v := ctx.FormInt64("project"); v > 0 { 2800 - projectID = &v 2800 + projectID = optional.Some(v) 2801 2801 } 2802 2802 2803 2803 isPull := optional.None[bool]() ··· 2835 2835 SortBy: issue_indexer.SortByCreatedDesc, 2836 2836 } 2837 2837 if since != 0 { 2838 - searchOpt.UpdatedAfterUnix = &since 2838 + searchOpt.UpdatedAfterUnix = optional.Some(since) 2839 2839 } 2840 2840 if before != 0 { 2841 - searchOpt.UpdatedBeforeUnix = &before 2841 + searchOpt.UpdatedBeforeUnix = optional.Some(before) 2842 2842 } 2843 2843 if len(labelIDs) == 1 && labelIDs[0] == 0 { 2844 2844 searchOpt.NoLabelOnly = true ··· 2859 2859 } 2860 2860 2861 2861 if createdByID > 0 { 2862 - searchOpt.PosterID = &createdByID 2862 + searchOpt.PosterID = optional.Some(createdByID) 2863 2863 } 2864 2864 if assignedByID > 0 { 2865 - searchOpt.AssigneeID = &assignedByID 2865 + searchOpt.AssigneeID = optional.Some(assignedByID) 2866 2866 } 2867 2867 if mentionedByID > 0 { 2868 - searchOpt.MentionID = &mentionedByID 2868 + searchOpt.MentionID = optional.Some(mentionedByID) 2869 2869 } 2870 2870 2871 2871 ids, total, err := issue_indexer.SearchIssues(ctx, searchOpt)
+5 -1
routers/web/repo/repo.go
··· 543 543 544 544 // SearchRepo repositories via options 545 545 func SearchRepo(ctx *context.Context) { 546 + page := ctx.FormInt("page") 547 + if page <= 0 { 548 + page = 1 549 + } 546 550 opts := &repo_model.SearchRepoOptions{ 547 551 ListOptions: db.ListOptions{ 548 - Page: ctx.FormInt("page"), 552 + Page: page, 549 553 PageSize: convert.ToCorrectPageSize(ctx.FormInt("limit")), 550 554 }, 551 555 Actor: ctx.Doer,
+2 -6
routers/web/repo/setting/webhook.go
··· 616 616 return nil, nil 617 617 } 618 618 ctx.Data["BaseLink"] = orCtx.Link 619 + ctx.Data["BaseLinkNew"] = orCtx.LinkNew 619 620 620 621 var w *webhook.Webhook 621 622 if orCtx.RepoID > 0 { ··· 684 685 commit := ctx.Repo.Commit 685 686 if commit == nil { 686 687 ghost := user_model.NewGhostUser() 687 - objectFormat, err := git.GetObjectFormatOfRepo(ctx, ctx.Repo.Repository.RepoPath()) 688 - if err != nil { 689 - ctx.Flash.Error("GetObjectFormatOfRepo: " + err.Error()) 690 - ctx.Status(http.StatusInternalServerError) 691 - return 692 - } 688 + objectFormat := git.ObjectFormatFromName(ctx.Repo.Repository.ObjectFormatName) 693 689 commit = &git.Commit{ 694 690 ID: objectFormat.EmptyObjectID(), 695 691 Author: ghost.NewGitSig(),
+10 -10
routers/web/user/home.go
··· 791 791 case issues_model.FilterModeYourRepositories: 792 792 openClosedOpts.AllPublic = false 793 793 case issues_model.FilterModeAssign: 794 - openClosedOpts.AssigneeID = &doerID 794 + openClosedOpts.AssigneeID = optional.Some(doerID) 795 795 case issues_model.FilterModeCreate: 796 - openClosedOpts.PosterID = &doerID 796 + openClosedOpts.PosterID = optional.Some(doerID) 797 797 case issues_model.FilterModeMention: 798 - openClosedOpts.MentionID = &doerID 798 + openClosedOpts.MentionID = optional.Some(doerID) 799 799 case issues_model.FilterModeReviewRequested: 800 - openClosedOpts.ReviewRequestedID = &doerID 800 + openClosedOpts.ReviewRequestedID = optional.Some(doerID) 801 801 case issues_model.FilterModeReviewed: 802 - openClosedOpts.ReviewedID = &doerID 802 + openClosedOpts.ReviewedID = optional.Some(doerID) 803 803 } 804 804 openClosedOpts.IsClosed = optional.Some(false) 805 805 ret.OpenCount, err = issue_indexer.CountIssues(ctx, openClosedOpts) ··· 817 817 if err != nil { 818 818 return nil, err 819 819 } 820 - ret.AssignCount, err = issue_indexer.CountIssues(ctx, opts.Copy(func(o *issue_indexer.SearchOptions) { o.AssigneeID = &doerID })) 820 + ret.AssignCount, err = issue_indexer.CountIssues(ctx, opts.Copy(func(o *issue_indexer.SearchOptions) { o.AssigneeID = optional.Some(doerID) })) 821 821 if err != nil { 822 822 return nil, err 823 823 } 824 - ret.CreateCount, err = issue_indexer.CountIssues(ctx, opts.Copy(func(o *issue_indexer.SearchOptions) { o.PosterID = &doerID })) 824 + ret.CreateCount, err = issue_indexer.CountIssues(ctx, opts.Copy(func(o *issue_indexer.SearchOptions) { o.PosterID = optional.Some(doerID) })) 825 825 if err != nil { 826 826 return nil, err 827 827 } 828 - ret.MentionCount, err = issue_indexer.CountIssues(ctx, opts.Copy(func(o *issue_indexer.SearchOptions) { o.MentionID = &doerID })) 828 + ret.MentionCount, err = issue_indexer.CountIssues(ctx, opts.Copy(func(o *issue_indexer.SearchOptions) { o.MentionID = optional.Some(doerID) })) 829 829 if err != nil { 830 830 return nil, err 831 831 } 832 - ret.ReviewRequestedCount, err = issue_indexer.CountIssues(ctx, opts.Copy(func(o *issue_indexer.SearchOptions) { o.ReviewRequestedID = &doerID })) 832 + ret.ReviewRequestedCount, err = issue_indexer.CountIssues(ctx, opts.Copy(func(o *issue_indexer.SearchOptions) { o.ReviewRequestedID = optional.Some(doerID) })) 833 833 if err != nil { 834 834 return nil, err 835 835 } 836 - ret.ReviewedCount, err = issue_indexer.CountIssues(ctx, opts.Copy(func(o *issue_indexer.SearchOptions) { o.ReviewedID = &doerID })) 836 + ret.ReviewedCount, err = issue_indexer.CountIssues(ctx, opts.Copy(func(o *issue_indexer.SearchOptions) { o.ReviewedID = optional.Some(doerID) })) 837 837 if err != nil { 838 838 return nil, err 839 839 }
+1 -1
routers/web/web.go
··· 1413 1413 }) 1414 1414 m.Post("/cancel", reqRepoActionsWriter, actions.Cancel) 1415 1415 m.Post("/approve", reqRepoActionsWriter, actions.Approve) 1416 - m.Post("/artifacts", actions.ArtifactsView) 1416 + m.Get("/artifacts", actions.ArtifactsView) 1417 1417 m.Get("/artifacts/{artifact_name}", actions.ArtifactsDownloadView) 1418 1418 m.Delete("/artifacts/{artifact_name}", reqRepoActionsWriter, actions.ArtifactsDeleteView) 1419 1419 m.Post("/rerun", reqRepoActionsWriter, actions.Rerun)
+2 -6
services/forms/user_form.go
··· 10 10 "strings" 11 11 12 12 auth_model "code.gitea.io/gitea/models/auth" 13 + user_model "code.gitea.io/gitea/models/user" 13 14 "code.gitea.io/gitea/modules/setting" 14 15 "code.gitea.io/gitea/modules/structs" 15 - "code.gitea.io/gitea/modules/validation" 16 16 "code.gitea.io/gitea/modules/web/middleware" 17 17 "code.gitea.io/gitea/services/context" 18 18 ··· 109 109 // domains in the whitelist or if it doesn't match any of 110 110 // domains in the blocklist, if any such list is not empty. 111 111 func (f *RegisterForm) IsEmailDomainAllowed() bool { 112 - if len(setting.Service.EmailDomainAllowList) == 0 { 113 - return !validation.IsEmailDomainListed(setting.Service.EmailDomainBlockList, f.Email) 114 - } 115 - 116 - return validation.IsEmailDomainListed(setting.Service.EmailDomainAllowList, f.Email) 112 + return user_model.IsEmailDomainAllowed(f.Email) 117 113 } 118 114 119 115 // MustChangePasswordForm form for updating your password after account creation
+2 -1
services/mailer/mail.go
··· 222 222 body, err := markdown.RenderString(&markup.RenderContext{ 223 223 Ctx: ctx, 224 224 Links: markup.Links{ 225 - Base: ctx.Issue.Repo.HTMLURL(), 225 + AbsolutePrefix: true, 226 + Base: ctx.Issue.Repo.HTMLURL(), 226 227 }, 227 228 Metas: ctx.Issue.Repo.ComposeMetas(ctx), 228 229 }, ctx.Content)
+25 -1
services/mailer/mail_test.go
··· 8 8 "context" 9 9 "fmt" 10 10 "html/template" 11 + "io" 12 + "mime/quotedprintable" 11 13 "regexp" 12 14 "strings" 13 15 "testing" ··· 19 21 repo_model "code.gitea.io/gitea/models/repo" 20 22 "code.gitea.io/gitea/models/unittest" 21 23 user_model "code.gitea.io/gitea/models/user" 24 + "code.gitea.io/gitea/modules/markup" 22 25 "code.gitea.io/gitea/modules/setting" 23 26 24 27 "github.com/stretchr/testify/assert" ··· 67 70 func TestComposeIssueCommentMessage(t *testing.T) { 68 71 doer, _, issue, comment := prepareMailerTest(t) 69 72 73 + markup.Init(&markup.ProcessorHelper{ 74 + IsUsernameMentionable: func(ctx context.Context, username string) bool { 75 + return username == doer.Name 76 + }, 77 + }) 78 + 70 79 setting.IncomingEmail.Enabled = true 71 80 defer func() { setting.IncomingEmail.Enabled = false }() 72 81 ··· 77 86 msgs, err := composeIssueCommentMessages(&mailCommentContext{ 78 87 Context: context.TODO(), // TODO: use a correct context 79 88 Issue: issue, Doer: doer, ActionType: activities_model.ActionCommentIssue, 80 - Content: "test body", Comment: comment, 89 + Content: fmt.Sprintf("test @%s %s#%d body", doer.Name, issue.Repo.FullName(), issue.Index), 90 + Comment: comment, 81 91 }, "en-US", recipients, false, "issue comment") 82 92 assert.NoError(t, err) 83 93 assert.Len(t, msgs, 2) ··· 96 106 assert.Equal(t, "<user2/repo1/issues/1/comment/2@localhost>", gomailMsg.GetHeader("Message-ID")[0], "Message-ID header doesn't match") 97 107 assert.Equal(t, "<mailto:"+replyTo+">", gomailMsg.GetHeader("List-Post")[0]) 98 108 assert.Len(t, gomailMsg.GetHeader("List-Unsubscribe"), 2) // url + mailto 109 + 110 + var buf bytes.Buffer 111 + gomailMsg.WriteTo(&buf) 112 + 113 + b, err := io.ReadAll(quotedprintable.NewReader(&buf)) 114 + assert.NoError(t, err) 115 + 116 + // text/plain 117 + assert.Contains(t, string(b), fmt.Sprintf(`( %s )`, doer.HTMLURL())) 118 + assert.Contains(t, string(b), fmt.Sprintf(`( %s )`, issue.HTMLURL())) 119 + 120 + // text/html 121 + assert.Contains(t, string(b), fmt.Sprintf(`href="%s"`, doer.HTMLURL())) 122 + assert.Contains(t, string(b), fmt.Sprintf(`href="%s"`, issue.HTMLURL())) 99 123 } 100 124 101 125 func TestComposeIssueMessage(t *testing.T) {
+1 -4
services/mirror/mirror_pull.go
··· 479 479 log.Error("SyncMirrors [repo: %-v]: unable to GetRefCommitID [ref_name: %s]: %v", m.Repo, result.refName, err) 480 480 continue 481 481 } 482 - objectFormat, err := git.GetObjectFormatOfRepo(ctx, m.Repo.RepoPath()) 483 - if err != nil { 484 - log.Error("SyncMirrors [repo: %-v]: unable to GetHashTypeOfRepo: %v", m.Repo, err) 485 - } 482 + objectFormat := git.ObjectFormatFromName(m.Repo.ObjectFormatName) 486 483 notify_service.SyncPushCommits(ctx, m.Repo.MustOwner(ctx), m.Repo, &repo_module.PushUpdateOptions{ 487 484 RefFullName: result.refName, 488 485 OldCommitID: objectFormat.EmptyObjectID().String(),
+1 -1
services/pull/pull.go
··· 351 351 } 352 352 if err == nil { 353 353 for _, pr := range prs { 354 - objectFormat, _ := git.GetObjectFormatOfRepo(ctx, pr.BaseRepo.RepoPath()) 354 + objectFormat := git.ObjectFormatFromName(pr.BaseRepo.ObjectFormatName) 355 355 if newCommitID != "" && newCommitID != objectFormat.EmptyObjectID().String() { 356 356 changed, err := checkIfPRContentChanged(ctx, pr, oldCommitID, newCommitID) 357 357 if err != nil {
+1 -4
services/release/release.go
··· 326 326 } 327 327 328 328 refName := git.RefNameFromTag(rel.TagName) 329 - objectFormat, err := git.GetObjectFormatOfRepo(ctx, repo.RepoPath()) 330 - if err != nil { 331 - return err 332 - } 329 + objectFormat := git.ObjectFormatFromName(repo.ObjectFormatName) 333 330 notify_service.PushCommits( 334 331 ctx, doer, repo, 335 332 &repository.PushUpdateOptions{
+1
templates/base/head_script.tmpl
··· 41 41 remove_label_str: {{ctx.Locale.Tr "remove_label_str"}}, 42 42 modal_confirm: {{ctx.Locale.Tr "modal.confirm"}}, 43 43 modal_cancel: {{ctx.Locale.Tr "modal.cancel"}}, 44 + more_items: {{ctx.Locale.Tr "more_items"}}, 44 45 }, 45 46 }; 46 47 {{/* in case some pages don't render the pageData, we make sure it is an object to prevent null access */}}
+40 -3
templates/devtest/gitea-ui.tmpl
··· 105 105 </div> 106 106 107 107 <div> 108 - <h1>GiteaOriginUrl</h1> 109 - <div><gitea-origin-url data-url="test/url"></gitea-origin-url></div> 110 - <div><gitea-origin-url data-url="/test/url"></gitea-origin-url></div> 108 + <h1>&lt;origin-url&gt;</h1> 109 + <div><origin-url data-url="test/url"></origin-url></div> 110 + <div><origin-url data-url="/test/url"></origin-url></div> 111 + </div> 112 + 113 + <div> 114 + <h1>&lt;overflow-menu&gt;</h1> 115 + <overflow-menu class="ui secondary pointing tabular borderless menu"> 116 + <div class="overflow-menu-items"> 117 + <a class="active item">item</a> 118 + <a class="item">item 1</a> 119 + <a class="item">item 2</a> 120 + <a class="item">item 3</a> 121 + <a class="item">item 4</a> 122 + <a class="item">item 5</a> 123 + <a class="item">item 6</a> 124 + <a class="item">item 7</a> 125 + <a class="item">item 8</a> 126 + <a class="item">item 9</a> 127 + <a class="item">item 10</a> 128 + <a class="item">item 11</a> 129 + <a class="item">item 12</a> 130 + <a class="item">item 13</a> 131 + <a class="item">item 14</a> 132 + <a class="item">item 15</a> 133 + <a class="item">item 16</a> 134 + <a class="item">item 17</a> 135 + <a class="item">item 18</a> 136 + </div> 137 + </overflow-menu> 138 + </div> 139 + 140 + <div> 141 + <h1>GiteaAbsoluteDate</h1> 142 + <div><absolute-date date="2024-03-11" year="numeric" day="numeric" month="short"></absolute-date></div> 143 + <div><absolute-date date="2024-03-11" year="numeric" day="numeric" month="long"></absolute-date></div> 144 + <div><absolute-date date="2024-03-11" year="" day="numeric" month="numeric"></absolute-date></div> 145 + <div><absolute-date date="2024-03-11" year="" day="numeric" month="numeric" weekday="long"></absolute-date></div> 146 + <div><absolute-date date="2024-03-11T19:00:00-05:00" year="" day="numeric" month="numeric" weekday="long"></absolute-date></div> 147 + <div class="tw-text-text-light-2">relative-time: <relative-time format="datetime" datetime="2024-03-11" year="" day="numeric" month="numeric"></relative-time></div> 111 148 </div> 112 149 113 150 <div>
+3 -3
templates/explore/navbar.tmpl
··· 1 - <div class="ui secondary pointing tabular top attached borderless menu new-menu navbar"> 2 - <div class="new-menu-inner"> 1 + <overflow-menu class="ui secondary pointing tabular top attached borderless menu navbar"> 2 + <div class="overflow-menu-items tw-justify-center"> 3 3 <a class="{{if .PageIsExploreRepositories}}active {{end}}item" href="{{AppSubUrl}}/explore/repos"> 4 4 {{svg "octicon-repo"}} {{ctx.Locale.Tr "explore.repos"}} 5 5 </a> ··· 17 17 </a> 18 18 {{end}} 19 19 </div> 20 - </div> 20 + </overflow-menu>
+35 -36
templates/org/menu.tmpl
··· 1 1 <div class="ui container"> 2 - <div class="ui secondary stackable pointing menu"> 3 - <a class="{{if .PageIsViewRepositories}}active {{end}}item" href="{{$.Org.HomeLink}}"> 4 - {{svg "octicon-repo"}} {{ctx.Locale.Tr "user.repositories"}} 5 - {{if .RepoCount}} 6 - <div class="ui small label">{{.RepoCount}}</div> 2 + <overflow-menu class="ui secondary pointing tabular borderless menu"> 3 + <div class="overflow-menu-items"> 4 + <a class="{{if .PageIsViewRepositories}}active {{end}}item" href="{{$.Org.HomeLink}}"> 5 + {{svg "octicon-repo"}} {{ctx.Locale.Tr "user.repositories"}} 6 + {{if .RepoCount}} 7 + <div class="ui small label">{{.RepoCount}}</div> 8 + {{end}} 9 + </a> 10 + {{if .CanReadProjects}} 11 + <a class="{{if .PageIsViewProjects}}active {{end}}item" href="{{$.Org.HomeLink}}/-/projects"> 12 + {{svg "octicon-project-symlink"}} {{ctx.Locale.Tr "user.projects"}} 13 + {{if .ProjectCount}} 14 + <div class="ui small label">{{.ProjectCount}}</div> 15 + {{end}} 16 + </a> 7 17 {{end}} 8 - </a> 9 - {{if .CanReadProjects}} 10 - <a class="{{if .PageIsViewProjects}}active {{end}}item" href="{{$.Org.HomeLink}}/-/projects"> 11 - {{svg "octicon-project-symlink"}} {{ctx.Locale.Tr "user.projects"}} 12 - {{if .ProjectCount}} 13 - <div class="ui small label">{{.ProjectCount}}</div> 18 + {{if and .IsPackageEnabled .CanReadPackages}} 19 + <a class="{{if .IsPackagesPage}}active {{end}}item" href="{{$.Org.HomeLink}}/-/packages"> 20 + {{svg "octicon-package"}} {{ctx.Locale.Tr "packages.title"}} 21 + </a> 14 22 {{end}} 15 - </a> 16 - {{end}} 17 - {{if and .IsPackageEnabled .CanReadPackages}} 18 - <a class="{{if .IsPackagesPage}}active {{end}}item" href="{{$.Org.HomeLink}}/-/packages"> 19 - {{svg "octicon-package"}} {{ctx.Locale.Tr "packages.title"}} 20 - </a> 21 - {{end}} 22 - {{if and .IsRepoIndexerEnabled .CanReadCode}} 23 - <a class="{{if .IsCodePage}}active {{end}}item" href="{{$.Org.HomeLink}}/-/code"> 24 - {{svg "octicon-code"}} {{ctx.Locale.Tr "org.code"}} 25 - </a> 26 - {{end}} 27 - {{if .NumMembers}} 23 + {{if and .IsRepoIndexerEnabled .CanReadCode}} 24 + <a class="{{if .IsCodePage}}active {{end}}item" href="{{$.Org.HomeLink}}/-/code"> 25 + {{svg "octicon-code"}} {{ctx.Locale.Tr "org.code"}} 26 + </a> 27 + {{end}} 28 + {{if .NumMembers}} 28 29 <a class="{{if $.PageIsOrgMembers}}active {{end}}item" href="{{$.OrgLink}}/members"> 29 30 {{svg "octicon-person"}} {{ctx.Locale.Tr "org.members"}} 30 31 <div class="ui small label">{{.NumMembers}}</div> 31 32 </a> 32 - {{end}} 33 - {{if .IsOrganizationMember}} 33 + {{end}} 34 + {{if .IsOrganizationMember}} 34 35 <a class="{{if $.PageIsOrgTeams}}active {{end}}item" href="{{$.OrgLink}}/teams"> 35 36 {{svg "octicon-people"}} {{ctx.Locale.Tr "org.teams"}} 36 37 {{if .NumTeams}} 37 38 <div class="ui small label">{{.NumTeams}}</div> 38 39 {{end}} 39 40 </a> 40 - {{end}} 41 - 42 - {{if .IsOrganizationOwner}} 43 - <div class="right menu"> 44 - <a class="{{if .PageIsOrgSettings}}active {{end}}item" href="{{.OrgLink}}/settings"> 45 - {{svg "octicon-tools"}} {{ctx.Locale.Tr "repo.settings"}} 46 - </a> 47 - </div> 48 - {{end}} 49 - </div> 41 + {{end}} 42 + {{if .IsOrganizationOwner}} 43 + <a class="{{if .PageIsOrgSettings}}active {{end}}item" href="{{.OrgLink}}/settings"> 44 + {{svg "octicon-tools"}} {{ctx.Locale.Tr "repo.settings"}} 45 + </a> 46 + {{end}} 47 + </div> 48 + </overflow-menu> 50 49 </div>
+2 -2
templates/package/content/alpine.tmpl
··· 4 4 <div class="ui form"> 5 5 <div class="field"> 6 6 <label>{{svg "octicon-code"}} {{ctx.Locale.Tr "packages.alpine.registry"}}</label> 7 - <div class="markup"><pre class="code-block"><code><gitea-origin-url data-url="{{AppSubUrl}}/api/packages/{{$.PackageDescriptor.Owner.Name}}/alpine"></gitea-origin-url>/$branch/$repository</code></pre></div> 7 + <div class="markup"><pre class="code-block"><code><origin-url data-url="{{AppSubUrl}}/api/packages/{{$.PackageDescriptor.Owner.Name}}/alpine"></origin-url>/$branch/$repository</code></pre></div> 8 8 <p>{{ctx.Locale.Tr "packages.alpine.registry.info"}}</p> 9 9 </div> 10 10 <div class="field"> 11 11 <label>{{svg "octicon-terminal"}} {{ctx.Locale.Tr "packages.alpine.registry.key"}}</label> 12 - <div class="markup"><pre class="code-block"><code>curl -JO <gitea-origin-url data-url="{{AppSubUrl}}/api/packages/{{$.PackageDescriptor.Owner.Name}}/alpine/key"></gitea-origin-url></code></pre></div> 12 + <div class="markup"><pre class="code-block"><code>curl -JO <origin-url data-url="{{AppSubUrl}}/api/packages/{{$.PackageDescriptor.Owner.Name}}/alpine/key"></origin-url></code></pre></div> 13 13 </div> 14 14 <div class="field"> 15 15 <label>{{svg "octicon-terminal"}} {{ctx.Locale.Tr "packages.alpine.install"}}</label>
+2 -2
templates/package/content/cargo.tmpl
··· 8 8 default = "forgejo" 9 9 10 10 [registries.forgejo] 11 - index = "sparse+<gitea-origin-url data-url="{{AppSubUrl}}/api/packages/{{.PackageDescriptor.Owner.Name}}/cargo/"></gitea-origin-url>" # Sparse index 12 - # index = "<gitea-origin-url data-url="{{AppSubUrl}}/{{.PackageDescriptor.Owner.Name}}/_cargo-index.git"></gitea-origin-url>" # Git 11 + index = "sparse+<origin-url data-url="{{AppSubUrl}}/api/packages/{{.PackageDescriptor.Owner.Name}}/cargo/"></origin-url>" # Sparse index 12 + # index = "<origin-url data-url="{{AppSubUrl}}/{{.PackageDescriptor.Owner.Name}}/_cargo-index.git"></origin-url>" # Git 13 13 14 14 [net] 15 15 git-fetch-with-cli = true</code></pre></div>
+1 -1
templates/package/content/chef.tmpl
··· 4 4 <div class="ui form"> 5 5 <div class="field"> 6 6 <label>{{svg "octicon-code"}} {{ctx.Locale.Tr "packages.chef.registry"}}</label> 7 - <div class="markup"><pre class="code-block"><code>knife[:supermarket_site] = '<gitea-origin-url data-url="{{AppSubUrl}}/api/packages/{{.PackageDescriptor.Owner.Name}}/chef"></gitea-origin-url>'</code></pre></div> 7 + <div class="markup"><pre class="code-block"><code>knife[:supermarket_site] = '<origin-url data-url="{{AppSubUrl}}/api/packages/{{.PackageDescriptor.Owner.Name}}/chef"></origin-url>'</code></pre></div> 8 8 </div> 9 9 <div class="field"> 10 10 <label>{{svg "octicon-terminal"}} {{ctx.Locale.Tr "packages.chef.install"}}</label>
+1 -1
templates/package/content/composer.tmpl
··· 7 7 <div class="markup"><pre class="code-block"><code>{ 8 8 "repositories": [{ 9 9 "type": "composer", 10 - "url": "<gitea-origin-url data-url="{{AppSubUrl}}/api/packages/{{.PackageDescriptor.Owner.Name}}/composer"></gitea-origin-url>" 10 + "url": "<origin-url data-url="{{AppSubUrl}}/api/packages/{{.PackageDescriptor.Owner.Name}}/composer"></origin-url>" 11 11 } 12 12 ] 13 13 }</code></pre></div>
+1 -1
templates/package/content/conan.tmpl
··· 4 4 <div class="ui form"> 5 5 <div class="field"> 6 6 <label>{{svg "octicon-terminal"}} {{ctx.Locale.Tr "packages.conan.registry"}}</label> 7 - <div class="markup"><pre class="code-block"><code>conan remote add gitea <gitea-origin-url data-url="{{AppSubUrl}}/api/packages/{{.PackageDescriptor.Owner.Name}}/conan"></gitea-origin-url></code></pre></div> 7 + <div class="markup"><pre class="code-block"><code>conan remote add gitea <origin-url data-url="{{AppSubUrl}}/api/packages/{{.PackageDescriptor.Owner.Name}}/conan"></origin-url></code></pre></div> 8 8 </div> 9 9 <div class="field"> 10 10 <label>{{svg "octicon-terminal"}} {{ctx.Locale.Tr "packages.conan.install"}}</label>
+3 -3
templates/package/content/conda.tmpl
··· 4 4 <div class="ui form"> 5 5 <div class="field"> 6 6 <label>{{svg "octicon-code"}} {{ctx.Locale.Tr "packages.conda.registry"}}</label> 7 - <div class="markup"><pre class="code-block"><code>channel_alias: <gitea-origin-url data-url="{{AppSubUrl}}/api/packages/{{.PackageDescriptor.Owner.Name}}/conda"></gitea-origin-url> 7 + <div class="markup"><pre class="code-block"><code>channel_alias: <origin-url data-url="{{AppSubUrl}}/api/packages/{{.PackageDescriptor.Owner.Name}}/conda"></origin-url> 8 8 channels: 9 - &#32;&#32;- <gitea-origin-url data-url="{{AppSubUrl}}/api/packages/{{.PackageDescriptor.Owner.Name}}/conda"></gitea-origin-url> 9 + &#32;&#32;- <origin-url data-url="{{AppSubUrl}}/api/packages/{{.PackageDescriptor.Owner.Name}}/conda"></origin-url> 10 10 default_channels: 11 - &#32;&#32;- <gitea-origin-url data-url="{{AppSubUrl}}/api/packages/{{.PackageDescriptor.Owner.Name}}/conda"></gitea-origin-url></code></pre></div> 11 + &#32;&#32;- <origin-url data-url="{{AppSubUrl}}/api/packages/{{.PackageDescriptor.Owner.Name}}/conda"></origin-url></code></pre></div> 12 12 </div> 13 13 <div class="field"> 14 14 <label>{{svg "octicon-terminal"}} {{ctx.Locale.Tr "packages.conda.install"}}</label>
+1 -1
templates/package/content/cran.tmpl
··· 4 4 <div class="ui form"> 5 5 <div class="field"> 6 6 <label>{{svg "octicon-code"}} {{ctx.Locale.Tr "packages.cran.registry"}}</label> 7 - <div class="markup"><pre class="code-block"><code>options("repos" = c(getOption("repos"), c(forgejo="<gitea-origin-url data-url="{{AppSubUrl}}/api/packages/{{.PackageDescriptor.Owner.Name}}/cran"></gitea-origin-url>")))</code></pre></div> 7 + <div class="markup"><pre class="code-block"><code>options("repos" = c(getOption("repos"), c(forgejo="<origin-url data-url="{{AppSubUrl}}/api/packages/{{.PackageDescriptor.Owner.Name}}/cran"></origin-url>")))</code></pre></div> 8 8 </div> 9 9 <div class="field"> 10 10 <label>{{svg "octicon-terminal"}} {{ctx.Locale.Tr "packages.cran.install"}}</label>
+2 -2
templates/package/content/debian.tmpl
··· 4 4 <div class="ui form"> 5 5 <div class="field"> 6 6 <label>{{svg "octicon-terminal"}} {{ctx.Locale.Tr "packages.debian.registry"}}</label> 7 - <div class="markup"><pre class="code-block"><code>sudo curl <gitea-origin-url data-url="{{AppSubUrl}}/api/packages/{{$.PackageDescriptor.Owner.Name}}/debian/repository.key"></gitea-origin-url> -o /etc/apt/keyrings/forgejo-{{$.PackageDescriptor.Owner.Name}}.asc 8 - echo "deb [signed-by=/etc/apt/keyrings/forgejo-{{$.PackageDescriptor.Owner.Name}}.asc] <gitea-origin-url data-url="{{AppSubUrl}}/api/packages/{{$.PackageDescriptor.Owner.Name}}/debian"></gitea-origin-url> $distribution $component" | sudo tee -a /etc/apt/sources.list.d/forgejo.list 7 + <div class="markup"><pre class="code-block"><code>sudo curl <origin-url data-url="{{AppSubUrl}}/api/packages/{{$.PackageDescriptor.Owner.Name}}/debian/repository.key"></origin-url> -o /etc/apt/keyrings/forgejo-{{$.PackageDescriptor.Owner.Name}}.asc 8 + echo "deb [signed-by=/etc/apt/keyrings/forgejo-{{$.PackageDescriptor.Owner.Name}}.asc] <origin-url data-url="{{AppSubUrl}}/api/packages/{{$.PackageDescriptor.Owner.Name}}/debian"></origin-url> $distribution $component" | sudo tee -a /etc/apt/sources.list.d/forgejo.list 9 9 sudo apt update</code></pre></div> 10 10 <p>{{ctx.Locale.Tr "packages.debian.registry.info"}}</p> 11 11 </div>
+1 -1
templates/package/content/generic.tmpl
··· 6 6 <label>{{svg "octicon-terminal"}} {{ctx.Locale.Tr "packages.generic.download"}}</label> 7 7 <div class="markup"><pre class="code-block"><code> 8 8 {{- range .PackageDescriptor.Files -}} 9 - curl -OJ <gitea-origin-url data-url="{{AppSubUrl}}/api/packages/{{$.PackageDescriptor.Owner.Name}}/generic/{{$.PackageDescriptor.Package.Name}}/{{$.PackageDescriptor.Version.Version}}/{{.File.Name}}"></gitea-origin-url> 9 + curl -OJ <origin-url data-url="{{AppSubUrl}}/api/packages/{{$.PackageDescriptor.Owner.Name}}/generic/{{$.PackageDescriptor.Package.Name}}/{{$.PackageDescriptor.Version.Version}}/{{.File.Name}}"></origin-url> 10 10 {{end -}} 11 11 </code></pre></div> 12 12 </div>
+1 -1
templates/package/content/go.tmpl
··· 4 4 <div class="ui form"> 5 5 <div class="field"> 6 6 <label>{{svg "octicon-terminal"}} {{ctx.Locale.Tr "packages.go.install"}}</label> 7 - <div class="markup"><pre class="code-block"><code>GOPROXY=<gitea-origin-url data-url="{{AppSubUrl}}/api/packages/{{$.PackageDescriptor.Owner.Name}}/go"></gitea-origin-url> go install {{$.PackageDescriptor.Package.Name}}@{{$.PackageDescriptor.Version.Version}}</code></pre></div> 7 + <div class="markup"><pre class="code-block"><code>GOPROXY=<origin-url data-url="{{AppSubUrl}}/api/packages/{{$.PackageDescriptor.Owner.Name}}/go"></origin-url> go install {{$.PackageDescriptor.Package.Name}}@{{$.PackageDescriptor.Version.Version}}</code></pre></div> 8 8 </div> 9 9 <div class="field"> 10 10 <label>{{ctx.Locale.Tr "packages.registry.documentation" "Go" "https://forgejo.org/docs/latest/user/packages/go/"}}</label>
+1 -1
templates/package/content/helm.tmpl
··· 4 4 <div class="ui form"> 5 5 <div class="field"> 6 6 <label>{{svg "octicon-terminal"}} {{ctx.Locale.Tr "packages.helm.registry"}}</label> 7 - <div class="markup"><pre class="code-block"><code>helm repo add {{AppDomain}} <gitea-origin-url data-url="{{AppSubUrl}}/api/packages/{{.PackageDescriptor.Owner.Name}}/helm"></gitea-origin-url> 7 + <div class="markup"><pre class="code-block"><code>helm repo add {{AppDomain}} <origin-url data-url="{{AppSubUrl}}/api/packages/{{.PackageDescriptor.Owner.Name}}/helm"></origin-url> 8 8 helm repo update</code></pre></div> 9 9 </div> 10 10 <div class="field">
+4 -4
templates/package/content/maven.tmpl
··· 7 7 <div class="markup"><pre class="code-block"><code>&lt;repositories&gt; 8 8 &lt;repository&gt; 9 9 &lt;id&gt;gitea&lt;/id&gt; 10 - &lt;url&gt;<gitea-origin-url data-url="{{AppSubUrl}}/api/packages/{{.PackageDescriptor.Owner.Name}}/maven"></gitea-origin-url>&lt;/url&gt; 10 + &lt;url&gt;<origin-url data-url="{{AppSubUrl}}/api/packages/{{.PackageDescriptor.Owner.Name}}/maven"></origin-url>&lt;/url&gt; 11 11 &lt;/repository&gt; 12 12 &lt;/repositories&gt; 13 13 14 14 &lt;distributionManagement&gt; 15 15 &lt;repository&gt; 16 16 &lt;id&gt;gitea&lt;/id&gt; 17 - &lt;url&gt;<gitea-origin-url data-url="{{AppSubUrl}}/api/packages/{{.PackageDescriptor.Owner.Name}}/maven"></gitea-origin-url>&lt;/url&gt; 17 + &lt;url&gt;<origin-url data-url="{{AppSubUrl}}/api/packages/{{.PackageDescriptor.Owner.Name}}/maven"></origin-url>&lt;/url&gt; 18 18 &lt;/repository&gt; 19 19 20 20 &lt;snapshotRepository&gt; 21 21 &lt;id&gt;gitea&lt;/id&gt; 22 - &lt;url&gt;<gitea-origin-url data-url="{{AppSubUrl}}/api/packages/{{.PackageDescriptor.Owner.Name}}/maven"></gitea-origin-url>&lt;/url&gt; 22 + &lt;url&gt;<origin-url data-url="{{AppSubUrl}}/api/packages/{{.PackageDescriptor.Owner.Name}}/maven"></origin-url>&lt;/url&gt; 23 23 &lt;/snapshotRepository&gt; 24 24 &lt;/distributionManagement&gt;</code></pre></div> 25 25 </div> ··· 37 37 </div> 38 38 <div class="field"> 39 39 <label>{{svg "octicon-terminal"}} {{ctx.Locale.Tr "packages.maven.download"}}</label> 40 - <div class="markup"><pre class="code-block"><code>mvn dependency:get -DremoteRepositories=<gitea-origin-url data-url="{{AppSubUrl}}/api/packages/{{.PackageDescriptor.Owner.Name}}/maven"></gitea-origin-url> -Dartifact={{.PackageDescriptor.Metadata.GroupID}}:{{.PackageDescriptor.Metadata.ArtifactID}}:{{.PackageDescriptor.Version.Version}}</code></pre></div> 40 + <div class="markup"><pre class="code-block"><code>mvn dependency:get -DremoteRepositories=<origin-url data-url="{{AppSubUrl}}/api/packages/{{.PackageDescriptor.Owner.Name}}/maven"></origin-url> -Dartifact={{.PackageDescriptor.Metadata.GroupID}}:{{.PackageDescriptor.Metadata.ArtifactID}}:{{.PackageDescriptor.Version.Version}}</code></pre></div> 41 41 </div> 42 42 <div class="field"> 43 43 <label>{{ctx.Locale.Tr "packages.registry.documentation" "Maven" "https://forgejo.org/docs/latest/user/packages/maven/"}}</label>
+1 -1
templates/package/content/npm.tmpl
··· 4 4 <div class="ui form"> 5 5 <div class="field"> 6 6 <label>{{svg "octicon-code"}} {{ctx.Locale.Tr "packages.npm.registry"}}</label> 7 - <div class="markup"><pre class="code-block"><code>{{if .PackageDescriptor.Metadata.Scope}}{{.PackageDescriptor.Metadata.Scope}}:{{end}}registry=<gitea-origin-url data-url="{{AppSubUrl}}/api/packages/{{.PackageDescriptor.Owner.Name}}/npm/"></gitea-origin-url></code></pre></div> 7 + <div class="markup"><pre class="code-block"><code>{{if .PackageDescriptor.Metadata.Scope}}{{.PackageDescriptor.Metadata.Scope}}:{{end}}registry=<origin-url data-url="{{AppSubUrl}}/api/packages/{{.PackageDescriptor.Owner.Name}}/npm/"></origin-url></code></pre></div> 8 8 </div> 9 9 <div class="field"> 10 10 <label>{{svg "octicon-terminal"}} {{ctx.Locale.Tr "packages.npm.install"}}</label>
+1 -1
templates/package/content/nuget.tmpl
··· 4 4 <div class="ui form"> 5 5 <div class="field"> 6 6 <label>{{svg "octicon-terminal"}} {{ctx.Locale.Tr "packages.nuget.registry"}}</label> 7 - <div class="markup"><pre class="code-block"><code>dotnet nuget add source --name {{.PackageDescriptor.Owner.Name}} --username your_username --password your_token <gitea-origin-url data-url="{{AppSubUrl}}/api/packages/{{.PackageDescriptor.Owner.Name}}/nuget/index.json"></gitea-origin-url></code></pre></div> 7 + <div class="markup"><pre class="code-block"><code>dotnet nuget add source --name {{.PackageDescriptor.Owner.Name}} --username your_username --password your_token <origin-url data-url="{{AppSubUrl}}/api/packages/{{.PackageDescriptor.Owner.Name}}/nuget/index.json"></origin-url></code></pre></div> 8 8 </div> 9 9 <div class="field"> 10 10 <label>{{svg "octicon-terminal"}} {{ctx.Locale.Tr "packages.nuget.install"}}</label>
+1 -1
templates/package/content/pub.tmpl
··· 4 4 <div class="ui form"> 5 5 <div class="field"> 6 6 <label>{{svg "octicon-terminal"}} {{ctx.Locale.Tr "packages.pub.install"}}</label> 7 - <div class="markup"><pre class="code-block"><code>dart pub add {{.PackageDescriptor.Package.Name}}:{{.PackageDescriptor.Version.Version}} --hosted-url=<gitea-origin-url data-url="{{AppSubUrl}}/api/packages/{{.PackageDescriptor.Owner.Name}}/pub/"></gitea-origin-url></code></pre></div> 7 + <div class="markup"><pre class="code-block"><code>dart pub add {{.PackageDescriptor.Package.Name}}:{{.PackageDescriptor.Version.Version}} --hosted-url=<origin-url data-url="{{AppSubUrl}}/api/packages/{{.PackageDescriptor.Owner.Name}}/pub/"></origin-url></code></pre></div> 8 8 </div> 9 9 <div class="field"> 10 10 <label>{{ctx.Locale.Tr "packages.registry.documentation" "Pub" "https://forgejo.org/docs/latest/user/packages/pub/"}}</label>
+1 -1
templates/package/content/pypi.tmpl
··· 4 4 <div class="ui form"> 5 5 <div class="field"> 6 6 <label>{{svg "octicon-terminal"}} {{ctx.Locale.Tr "packages.pypi.install"}}</label> 7 - <div class="markup"><pre class="code-block"><code>pip install --index-url <gitea-origin-url data-url="{{AppSubUrl}}/api/packages/{{.PackageDescriptor.Owner.Name}}/pypi/simple/"></gitea-origin-url> {{.PackageDescriptor.Package.Name}}</code></pre></div> 7 + <div class="markup"><pre class="code-block"><code>pip install --index-url <origin-url data-url="{{AppSubUrl}}/api/packages/{{.PackageDescriptor.Owner.Name}}/pypi/simple/"></origin-url> {{.PackageDescriptor.Package.Name}}</code></pre></div> 8 8 </div> 9 9 <div class="field"> 10 10 <label>{{ctx.Locale.Tr "packages.registry.documentation" "PyPI" "https://forgejo.org/docs/latest/user/packages/pypi/"}}</label>
+2 -2
templates/package/content/rpm.tmpl
··· 11 11 # {{ctx.Locale.Tr "packages.rpm.distros.redhat"}} 12 12 {{- range $group := .Groups}} 13 13 {{- if $group}}{{$group = print "/" $group}}{{end}} 14 - dnf config-manager --add-repo <gitea-origin-url data-url="{{AppSubUrl}}/api/packages/{{$.PackageDescriptor.Owner.Name}}/rpm{{$group}}.repo"></gitea-origin-url> 14 + dnf config-manager --add-repo <origin-url data-url="{{AppSubUrl}}/api/packages/{{$.PackageDescriptor.Owner.Name}}/rpm{{$group}}.repo"></origin-url> 15 15 {{- end}} 16 16 17 17 # {{ctx.Locale.Tr "packages.rpm.distros.suse"}} 18 18 {{- range $group := .Groups}} 19 19 {{- if $group}}{{$group = print "/" $group}}{{end}} 20 - zypper addrepo <gitea-origin-url data-url="{{AppSubUrl}}/api/packages/{{$.PackageDescriptor.Owner.Name}}/rpm{{$group}}.repo"></gitea-origin-url> 20 + zypper addrepo <origin-url data-url="{{AppSubUrl}}/api/packages/{{$.PackageDescriptor.Owner.Name}}/rpm{{$group}}.repo"></origin-url> 21 21 {{- end}}</code></pre></div> 22 22 </div> 23 23 <div class="field">
+2 -2
templates/package/content/rubygems.tmpl
··· 4 4 <div class="ui form"> 5 5 <div class="field"> 6 6 <label>{{svg "octicon-terminal"}} {{ctx.Locale.Tr "packages.rubygems.install"}}:</label> 7 - <div class="markup"><pre class="code-block"><code>gem install {{.PackageDescriptor.Package.Name}} --version &quot;{{.PackageDescriptor.Version.Version}}&quot; --source &quot;<gitea-origin-url data-url="{{AppSubUrl}}/api/packages/{{.PackageDescriptor.Owner.Name}}/rubygems"></gitea-origin-url>&quot;</code></pre></div> 7 + <div class="markup"><pre class="code-block"><code>gem install {{.PackageDescriptor.Package.Name}} --version &quot;{{.PackageDescriptor.Version.Version}}&quot; --source &quot;<origin-url data-url="{{AppSubUrl}}/api/packages/{{.PackageDescriptor.Owner.Name}}/rubygems"></origin-url>&quot;</code></pre></div> 8 8 </div> 9 9 <div class="field"> 10 10 <label>{{svg "octicon-code"}} {{ctx.Locale.Tr "packages.rubygems.install2"}}:</label> 11 - <div class="markup"><pre class="code-block"><code>source "<gitea-origin-url data-url="{{AppSubUrl}}/api/packages/{{.PackageDescriptor.Owner.Name}}/rubygems"></gitea-origin-url>" do 11 + <div class="markup"><pre class="code-block"><code>source "<origin-url data-url="{{AppSubUrl}}/api/packages/{{.PackageDescriptor.Owner.Name}}/rubygems"></origin-url>" do 12 12 gem "{{.PackageDescriptor.Package.Name}}", "{{.PackageDescriptor.Version.Version}}" 13 13 end</code></pre></div> 14 14 </div>
+1 -1
templates/package/content/swift.tmpl
··· 4 4 <div class="ui form"> 5 5 <div class="field"> 6 6 <label>{{svg "octicon-terminal"}} {{ctx.Locale.Tr "packages.swift.registry"}}</label> 7 - <div class="markup"><pre class="code-block"><code>swift package-registry set <gitea-origin-url data-url="{{AppSubUrl}}/api/packages/{{.PackageDescriptor.Owner.Name}}/swift"></gitea-origin-url></code></pre></div> 7 + <div class="markup"><pre class="code-block"><code>swift package-registry set <origin-url data-url="{{AppSubUrl}}/api/packages/{{.PackageDescriptor.Owner.Name}}/swift"></origin-url></code></pre></div> 8 8 </div> 9 9 <div class="field"> 10 10 <label>{{svg "octicon-code"}} {{ctx.Locale.Tr "packages.swift.install"}}</label>
+1 -1
templates/package/content/vagrant.tmpl
··· 4 4 <div class="ui form"> 5 5 <div class="field"> 6 6 <label>{{svg "octicon-terminal"}} {{ctx.Locale.Tr "packages.vagrant.install"}}</label> 7 - <div class="markup"><pre class="code-block"><code>vagrant box add --box-version {{.PackageDescriptor.Version.Version}} "<gitea-origin-url data-url="{{AppSubUrl}}/api/packages/{{.PackageDescriptor.Owner.Name}}/vagrant/{{.PackageDescriptor.Package.Name}}"></gitea-origin-url>"</code></pre></div> 7 + <div class="markup"><pre class="code-block"><code>vagrant box add --box-version {{.PackageDescriptor.Version.Version}} "<origin-url data-url="{{AppSubUrl}}/api/packages/{{.PackageDescriptor.Owner.Name}}/vagrant/{{.PackageDescriptor.Package.Name}}"></origin-url>"</code></pre></div> 8 8 </div> 9 9 <div class="field"> 10 10 <label>{{ctx.Locale.Tr "packages.registry.documentation" "Vagrant" "https://forgejo.org/docs/latest/user/packages/vagrant/"}}</label>
+1 -1
templates/projects/view.tmpl
··· 41 41 </div> 42 42 43 43 <div class="field color-field"> 44 - <label for="new_project_column_color">{{ctx.Locale.Tr "repo.projects.column.color"}}</label> 44 + <label for="new_project_column_color_picker">{{ctx.Locale.Tr "repo.projects.column.color"}}</label> 45 45 <div class="color picker column"> 46 46 <input class="color-picker" maxlength="7" placeholder="#c320f6" id="new_project_column_color_picker" name="color"> 47 47 {{template "repo/issue/label_precolors"}}
+1 -1
templates/repo/cite/cite_buttons.tmpl
··· 6 6 </button> 7 7 <!-- the value will be updated by initCitationFileCopyContent, the code below is used to avoid UI flicking --> 8 8 <input id="citation-copy-content" value="" size="1" readonly> 9 - <button class="ui icon button" id="citation-clipboard-btn" data-tooltip-content="{{ctx.Locale.Tr "copy"}}" data-clipboard-text="" data-clipboard-target="#citation-copy-content"> 9 + <button class="ui icon button" id="citation-clipboard-btn" data-tooltip-content="{{ctx.Locale.Tr "copy"}}" data-clipboard-target="#citation-copy-content"> 10 10 {{svg "octicon-copy"}} 11 11 </button>
+6 -8
templates/repo/cite/cite_modal.tmpl
··· 1 - <div class="ui tiny modal" id="cite-repo-modal"> 1 + <div class="ui small modal" id="cite-repo-modal"> 2 2 <div class="header"> 3 3 {{ctx.Locale.Tr "repo.cite_this_repo"}} 4 4 </div> 5 5 <div class="content"> 6 6 <div class="ui stackable secondary menu"> 7 - <div class="fitted item"> 8 - <div class="ui action input" id="citation-panel"> 9 - {{template "repo/cite/cite_buttons" .}} 10 - <a id="goto-citation-btn" class="ui basic jump icon button" href="{{$.RepoLink}}/src/{{$.BranchName}}/CITATION.cff" data-tooltip-content="{{ctx.Locale.Tr "repo.find_file.go_to_file"}}"> 11 - {{svg "octicon-file-moved"}} 12 - </a> 13 - </div> 7 + <div class="ui action input" id="citation-panel"> 8 + {{template "repo/cite/cite_buttons" .}} 9 + <a id="goto-citation-btn" class="ui basic jump icon button" href="{{$.RepoLink}}/src/{{$.BranchName}}/CITATION.cff" data-tooltip-content="{{ctx.Locale.Tr "repo.find_file.go_to_file"}}"> 10 + {{svg "octicon-file-moved"}} 11 + </a> 14 12 </div> 15 13 </div> 16 14 </div>
+3 -3
templates/repo/diff/image_diff.tmpl
··· 7 7 data-mime-before="{{.sniffedTypeBase.GetMimeType}}" 8 8 data-mime-after="{{.sniffedTypeHead.GetMimeType}}" 9 9 > 10 - <div class="ui secondary pointing tabular top attached borderless menu new-menu"> 11 - <div class="new-menu-inner"> 10 + <overflow-menu class="ui secondary pointing tabular top attached borderless menu"> 11 + <div class="overflow-menu-items tw-justify-center"> 12 12 <a class="item active" data-tab="diff-side-by-side-{{.file.Index}}">{{ctx.Locale.Tr "repo.diff.image.side_by_side"}}</a> 13 13 {{if and .blobBase .blobHead}} 14 14 <a class="item" data-tab="diff-swipe-{{.file.Index}}">{{ctx.Locale.Tr "repo.diff.image.swipe"}}</a> 15 15 <a class="item" data-tab="diff-overlay-{{.file.Index}}">{{ctx.Locale.Tr "repo.diff.image.overlay"}}</a> 16 16 {{end}} 17 17 </div> 18 - </div> 18 + </overflow-menu> 19 19 <div class="image-diff-tabs is-loading"> 20 20 <div class="ui bottom attached tab image-diff-container active" data-tab="diff-side-by-side-{{.file.Index}}"> 21 21 <div class="diff-side-by-side">
+4 -4
templates/repo/header.tmpl
··· 79 79 {{if .IsGenerated}}<div class="fork-flag">{{ctx.Locale.Tr "repo.generated_from"}} <a href="{{(.TemplateRepo ctx).Link}}">{{(.TemplateRepo ctx).FullName}}</a></div>{{end}} 80 80 </div> 81 81 {{end}} 82 - <div class="ui container secondary pointing tabular top attached borderless menu new-menu navbar"> 82 + <overflow-menu class="ui container secondary pointing tabular top attached borderless menu navbar tw-pt-0 tw-my-0"> 83 83 {{if not (or .Repository.IsBeingCreated .Repository.IsBroken)}} 84 - <div class="new-menu-inner"> 84 + <div class="overflow-menu-items"> 85 85 {{if .Permission.CanRead $.UnitTypeCode}} 86 86 <a class="{{if .PageIsViewCode}}active {{end}}item" href="{{.RepoLink}}{{if and (ne .BranchName .Repository.DefaultBranch) (not $.PageIsWiki)}}/src/{{.BranchNameSubURL}}{{end}}"> 87 87 {{svg "octicon-code"}} {{ctx.Locale.Tr "repo.code"}} ··· 183 183 {{end}} 184 184 </div> 185 185 {{else if .Permission.IsAdmin}} 186 - <div class="new-menu-inner"> 186 + <div class="overflow-menu-items"> 187 187 <a class="{{if .PageIsRepoSettings}}active {{end}} item" href="{{.RepoLink}}/settings"> 188 188 {{svg "octicon-tools"}} {{ctx.Locale.Tr "repo.settings"}} 189 189 </a> 190 190 </div> 191 191 {{end}} 192 - </div> 192 + </overflow-menu> 193 193 <div class="ui tabs divider"></div> 194 194 </div>
+8 -8
templates/repo/issue/view_content/comments.tmpl
··· 148 148 </span> 149 149 {{if eq .RefAction 3}}</del>{{end}} 150 150 151 - <div class="detail"> 151 + <div class="detail flex-text-block"> 152 152 <span class="text grey muted-links"><a href="{{.RefIssueLink ctx}}"><b>{{.RefIssueTitle ctx}}</b> {{.RefIssueIdent ctx}}</a></span> 153 153 </div> 154 154 </div> ··· 164 164 {{ctx.Locale.Tr "repo.issues.commit_ref_at" .EventTag $createdStr}} 165 165 {{end}} 166 166 </span> 167 - <div class="detail"> 167 + <div class="detail flex-text-block"> 168 168 {{svg "octicon-git-commit"}} 169 169 <span class="text grey muted-links">{{.Content | SanitizeHTML}}</span> 170 170 </div> ··· 256 256 {{ctx.Locale.Tr "repo.issues.stop_tracking_history" $createdStr}} 257 257 </span> 258 258 {{template "repo/issue/view_content/comments_delete_time" dict "ctxData" $ "comment" .}} 259 - <div class="detail"> 259 + <div class="detail flex-text-block"> 260 260 {{svg "octicon-clock"}} 261 261 {{if .RenderedContent}} 262 262 {{/* compatibility with time comments made before v1.21 */}} ··· 275 275 {{ctx.Locale.Tr "repo.issues.add_time_history" $createdStr}} 276 276 </span> 277 277 {{template "repo/issue/view_content/comments_delete_time" dict "ctxData" $ "comment" .}} 278 - <div class="detail"> 278 + <div class="detail flex-text-block"> 279 279 {{svg "octicon-clock"}} 280 280 {{if .RenderedContent}} 281 281 {{/* compatibility with time comments made before v1.21 */}} ··· 335 335 {{ctx.Locale.Tr "repo.issues.dependency.added_dependency" $createdStr}} 336 336 </span> 337 337 {{if .DependentIssue}} 338 - <div class="detail"> 338 + <div class="detail flex-text-block"> 339 339 {{svg "octicon-plus"}} 340 340 <span class="text grey muted-links"> 341 341 <a href="{{.DependentIssue.Link}}"> ··· 358 358 {{ctx.Locale.Tr "repo.issues.dependency.removed_dependency" $createdStr}} 359 359 </span> 360 360 {{if .DependentIssue}} 361 - <div class="detail"> 362 - <span class="text grey muted-links">{{svg "octicon-trash"}}</span> 361 + <div class="detail flex-text-block"> 362 + {{svg "octicon-trash"}} 363 363 <span class="text grey muted-links"> 364 364 <a href="{{.DependentIssue.Link}}"> 365 365 {{if eq .DependentIssue.RepoID .Issue.RepoID}} ··· 510 510 511 511 {{ctx.Locale.Tr "repo.issues.del_time_history" $createdStr}} 512 512 </span> 513 - <div class="detail"> 513 + <div class="detail flex-text-block"> 514 514 {{svg "octicon-clock"}} 515 515 {{if .RenderedContent}} 516 516 {{/* compatibility with time comments made before v1.21 */}}
+1 -1
templates/repo/issue/view_content/pull_merge_instruction.tmpl
··· 8 8 {{end}} 9 9 <div class="ui secondary segment"> 10 10 {{if eq .PullRequest.Flow 0}} 11 - <div>git fetch -u {{if ne .PullRequest.HeadRepo.ID .PullRequest.BaseRepo.ID}}<gitea-origin-url data-url="{{.PullRequest.HeadRepo.Link}}"></gitea-origin-url>{{else}}origin{{end}} {{.PullRequest.HeadBranch}}:{{$localBranch}}</div> 11 + <div>git fetch -u {{if ne .PullRequest.HeadRepo.ID .PullRequest.BaseRepo.ID}}<origin-url data-url="{{.PullRequest.HeadRepo.Link}}"></origin-url>{{else}}origin{{end}} {{.PullRequest.HeadBranch}}:{{$localBranch}}</div> 12 12 <div>git checkout {{$localBranch}}</div> 13 13 {{else}} 14 14 <div>git fetch -u origin {{.GetGitRefName}}:{{$localBranch}}</div>
+4 -4
templates/repo/view_file.tmpl
··· 144 144 {{end}} 145 145 </tbody> 146 146 </table> 147 - <div class="code-line-menu ui vertical pointing menu tippy-target"> 147 + <div class="code-line-menu tippy-target"> 148 148 {{if $.Permission.CanRead $.UnitTypeIssues}} 149 - <a class="item ref-in-new-issue" data-url-issue-new="{{.RepoLink}}/issues/new" data-url-param-body-link="{{.Repository.Link}}/src/commit/{{PathEscape .CommitID}}/{{PathEscapeSegments .TreePath}}{{if $.HasSourceRenderedToggle}}?display=source{{end}}" rel="nofollow noindex">{{ctx.Locale.Tr "repo.issues.context.reference_issue"}}</a> 149 + <a class="item ref-in-new-issue" role="menuitem" data-url-issue-new="{{.RepoLink}}/issues/new" data-url-param-body-link="{{.Repository.Link}}/src/commit/{{PathEscape .CommitID}}/{{PathEscapeSegments .TreePath}}{{if $.HasSourceRenderedToggle}}?display=source{{end}}" rel="nofollow noindex">{{ctx.Locale.Tr "repo.issues.context.reference_issue"}}</a> 150 150 {{end}} 151 - <a class="item view_git_blame" href="{{.Repository.Link}}/blame/commit/{{PathEscape .CommitID}}/{{PathEscapeSegments .TreePath}}">{{ctx.Locale.Tr "repo.view_git_blame"}}</a> 152 - <a class="item copy-line-permalink" data-url="{{.Repository.Link}}/src/commit/{{PathEscape .CommitID}}/{{PathEscapeSegments .TreePath}}{{if $.HasSourceRenderedToggle}}?display=source{{end}}">{{ctx.Locale.Tr "repo.file_copy_permalink"}}</a> 151 + <a class="item view_git_blame" role="menuitem" href="{{.Repository.Link}}/blame/commit/{{PathEscape .CommitID}}/{{PathEscapeSegments .TreePath}}">{{ctx.Locale.Tr "repo.view_git_blame"}}</a> 152 + <a class="item copy-line-permalink" role="menuitem" data-url="{{.Repository.Link}}/src/commit/{{PathEscape .CommitID}}/{{PathEscapeSegments .TreePath}}{{if $.HasSourceRenderedToggle}}?display=source{{end}}">{{ctx.Locale.Tr "repo.file_copy_permalink"}}</a> 153 153 </div> 154 154 {{end}} 155 155 {{end}}
+1 -1
templates/swagger/v1_json.tmpl
··· 6552 6552 }, 6553 6553 { 6554 6554 "type": "string", 6555 - "description": "Only show items which were created by the the given user", 6555 + "description": "Only show items which were created by the given user", 6556 6556 "name": "created_by", 6557 6557 "in": "query" 6558 6558 },
+3 -3
templates/user/auth/signin_navbar.tmpl
··· 1 1 {{if or .EnableOpenIDSignIn .EnableSSPI}} 2 - <div class="ui secondary pointing tabular top attached borderless menu new-menu navbar"> 3 - <div class="new-menu-inner"> 2 + <overflow-menu class="ui secondary pointing tabular top attached borderless menu navbar tw-bg-header-wrapper"> 3 + <div class="overflow-menu-items tw-justify-center"> 4 4 <a class="{{if .PageIsLogin}}active {{end}}item" rel="nofollow" href="{{AppSubUrl}}/user/login"> 5 5 {{ctx.Locale.Tr "auth.tab_signin"}} 6 6 </a> ··· 20 20 </a> 21 21 {{end}} 22 22 </div> 23 - </div> 23 + </overflow-menu> 24 24 {{end}}
+3 -3
templates/user/auth/signup_openid_navbar.tmpl
··· 1 - <div class="ui secondary pointing tabular top attached borderless menu new-menu navbar"> 2 - <div class="new-menu-inner"> 1 + <overflow-menu class="ui secondary pointing tabular top attached borderless menu navbar tw-bg-header-wrapper"> 2 + <div class="overflow-menu-items tw-justify-center"> 3 3 <a class="{{if .PageIsOpenIDConnect}}active {{end}}item" href="{{AppSubUrl}}/user/openid/connect"> 4 4 {{ctx.Locale.Tr "auth.openid_connect_title"}} 5 5 </a> ··· 9 9 </a> 10 10 {{end}} 11 11 </div> 12 - </div> 12 + </overflow-menu>
+37 -36
templates/user/overview/header.tmpl
··· 1 - <div class="ui secondary stackable pointing menu"> 2 - {{if and .HasProfileReadme .ContextUser.IsIndividual}} 3 - <a class="{{if eq .TabName "overview"}}active {{end}}item" href="{{.ContextUser.HomeLink}}?tab=overview"> 4 - {{svg "octicon-info"}} {{ctx.Locale.Tr "user.overview"}} 5 - </a> 6 - {{end}} 7 - <a class="{{if eq .TabName "repositories"}}active {{end}} item" href="{{.ContextUser.HomeLink}}?tab=repositories"> 8 - {{svg "octicon-repo"}} {{ctx.Locale.Tr "user.repositories"}} 9 - {{if .RepoCount}} 10 - <div class="ui small label">{{.RepoCount}}</div> 1 + <overflow-menu class="ui secondary pointing tabular borderless menu"> 2 + <div class="overflow-menu-items"> 3 + {{if and .HasProfileReadme .ContextUser.IsIndividual}} 4 + <a class="{{if eq .TabName "overview"}}active {{end}}item" href="{{.ContextUser.HomeLink}}?tab=overview"> 5 + {{svg "octicon-info"}} {{ctx.Locale.Tr "user.overview"}} 6 + </a> 11 7 {{end}} 12 - </a> 13 - {{if or .ContextUser.IsIndividual .CanReadProjects}} 14 - <a href="{{.ContextUser.HomeLink}}/-/projects" class="{{if .PageIsViewProjects}}active {{end}}item"> 15 - {{svg "octicon-project-symlink"}} {{ctx.Locale.Tr "user.projects"}} 16 - {{if .ProjectCount}} 17 - <div class="ui small label">{{.ProjectCount}}</div> 18 - {{end}} 19 - </a> 20 - {{end}} 21 - {{if and .IsPackageEnabled (or .ContextUser.IsIndividual .CanReadPackages)}} 22 - <a href="{{.ContextUser.HomeLink}}/-/packages" class="{{if .IsPackagesPage}}active {{end}}item"> 23 - {{svg "octicon-package"}} {{ctx.Locale.Tr "packages.title"}} 8 + <a class="{{if eq .TabName "repositories"}}active {{end}} item" href="{{.ContextUser.HomeLink}}?tab=repositories"> 9 + {{svg "octicon-repo"}} {{ctx.Locale.Tr "user.repositories"}} 10 + {{if .RepoCount}} 11 + <div class="ui small label">{{.RepoCount}}</div> 12 + {{end}} 24 13 </a> 25 - {{end}} 26 - {{if and .IsRepoIndexerEnabled (or .ContextUser.IsIndividual .CanReadCode)}} 27 - <a href="{{.ContextUser.HomeLink}}/-/code" class="{{if .IsCodePage}}active {{end}}item"> 28 - {{svg "octicon-code"}} {{ctx.Locale.Tr "user.code"}} 29 - </a> 30 - {{end}} 31 - 32 - {{if .ContextUser.IsIndividual}} 33 - <a class="{{if eq .TabName "activity"}}active {{end}}item" href="{{.ContextUser.HomeLink}}?tab=activity"> 34 - {{svg "octicon-rss"}} {{ctx.Locale.Tr "user.activity"}} 14 + {{if or .ContextUser.IsIndividual .CanReadProjects}} 15 + <a href="{{.ContextUser.HomeLink}}/-/projects" class="{{if .PageIsViewProjects}}active {{end}}item"> 16 + {{svg "octicon-project-symlink"}} {{ctx.Locale.Tr "user.projects"}} 17 + {{if .ProjectCount}} 18 + <div class="ui small label">{{.ProjectCount}}</div> 19 + {{end}} 35 20 </a> 36 - {{if not .DisableStars}} 21 + {{end}} 22 + {{if and .IsPackageEnabled (or .ContextUser.IsIndividual .CanReadPackages)}} 23 + <a href="{{.ContextUser.HomeLink}}/-/packages" class="{{if .IsPackagesPage}}active {{end}}item"> 24 + {{svg "octicon-package"}} {{ctx.Locale.Tr "packages.title"}} 25 + </a> 26 + {{end}} 27 + {{if and .IsRepoIndexerEnabled (or .ContextUser.IsIndividual .CanReadCode)}} 28 + <a href="{{.ContextUser.HomeLink}}/-/code" class="{{if .IsCodePage}}active {{end}}item"> 29 + {{svg "octicon-code"}} {{ctx.Locale.Tr "user.code"}} 30 + </a> 31 + {{end}} 32 + {{if .ContextUser.IsIndividual}} 33 + <a class="{{if eq .TabName "activity"}}active {{end}}item" href="{{.ContextUser.HomeLink}}?tab=activity"> 34 + {{svg "octicon-rss"}} {{ctx.Locale.Tr "user.activity"}} 35 + </a> 36 + {{if not .DisableStars}} 37 37 <a class="{{if eq .TabName "stars"}}active {{end}}item" href="{{.ContextUser.HomeLink}}?tab=stars"> 38 38 {{svg "octicon-star"}} {{ctx.Locale.Tr "user.starred"}} 39 39 {{if .ContextUser.NumStars}} 40 40 <div class="ui small label">{{.ContextUser.NumStars}}</div> 41 41 {{end}} 42 42 </a> 43 - {{else}} 43 + {{else}} 44 44 <a class="{{if eq .TabName "watching"}}active {{end}}item" href="{{.ContextUser.HomeLink}}?tab=watching"> 45 45 {{svg "octicon-eye"}} {{ctx.Locale.Tr "user.watched"}} 46 46 </a> 47 + {{end}} 47 48 {{end}} 48 - {{end}} 49 - </div> 49 + </div> 50 + </overflow-menu>
+2 -2
templates/user/settings/keys_gpg.tmpl
··· 22 22 <input readonly="" value="{{.TokenToSign}}"> 23 23 <div class="help"> 24 24 <p>{{ctx.Locale.Tr "settings.gpg_token_help"}}</p> 25 - <p><code>{{ctx.Locale.Tr "settings.gpg_token_code" .TokenToSign .PaddedKeyID}}</code></p> 25 + <p><code>{{printf `echo "%s" | gpg -a --default-key %s --detach-sig` .TokenToSign .PaddedKeyID}}</code></p> 26 26 </div> 27 27 </div> 28 28 <div class="field"> ··· 90 90 <input readonly="" value="{{$.TokenToSign}}"> 91 91 <div class="help"> 92 92 <p>{{ctx.Locale.Tr "settings.gpg_token_help"}}</p> 93 - <p><code>{{ctx.Locale.Tr "settings.gpg_token_code" $.TokenToSign .PaddedKeyID}}</code></p> 93 + <p><code>{{printf `echo "%s" | gpg -a --default-key %s --detach-sig` $.TokenToSign .PaddedKeyID}}</code></p> 94 94 </div> 95 95 <br> 96 96 </div>
+4 -2
tests/integration/api_admin_test.go
··· 354 354 "password": "allowedUser1_pass", 355 355 "must_change_password": "true", 356 356 }).AddTokenAuth(token) 357 - MakeRequest(t, req, http.StatusCreated) 357 + resp := MakeRequest(t, req, http.StatusCreated) 358 + assert.Equal(t, "the domain of user email allowedUser1@example1.org conflicts with EMAIL_DOMAIN_ALLOWLIST or EMAIL_DOMAIN_BLOCKLIST", resp.Header().Get("X-Gitea-Warning")) 358 359 359 360 req = NewRequest(t, "DELETE", "/api/v1/admin/users/allowedUser1").AddTokenAuth(token) 360 361 MakeRequest(t, req, http.StatusNoContent) ··· 378 379 SourceID: 0, 379 380 Email: &newEmail, 380 381 }).AddTokenAuth(token) 381 - MakeRequest(t, req, http.StatusOK) 382 + resp := MakeRequest(t, req, http.StatusOK) 383 + assert.Equal(t, "the domain of user email user2@example1.com conflicts with EMAIL_DOMAIN_ALLOWLIST or EMAIL_DOMAIN_BLOCKLIST", resp.Header().Get("X-Gitea-Warning")) 382 384 383 385 originalEmail := "user2@example.com" 384 386 req = NewRequestWithJSON(t, "PATCH", urlStr, api.EditUserOption{
+17
tests/integration/api_pull_test.go
··· 126 126 MakeRequest(t, req, http.StatusUnprocessableEntity) // second request should fail 127 127 } 128 128 129 + func TestAPICreatePullSameRepoSuccess(t *testing.T) { 130 + defer tests.PrepareTestEnv(t)() 131 + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) 132 + owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) 133 + 134 + session := loginUser(t, owner.Name) 135 + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) 136 + 137 + req := NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls", owner.Name, repo.Name), &api.CreatePullRequestOption{ 138 + Head: fmt.Sprintf("%s:pr-to-update", owner.Name), 139 + Base: "master", 140 + Title: "successfully create a PR between branches of the same repository", 141 + }).AddTokenAuth(token) 142 + MakeRequest(t, req, http.StatusCreated) 143 + MakeRequest(t, req, http.StatusUnprocessableEntity) // second request should fail 144 + } 145 + 129 146 func TestAPICreatePullWithFieldsSuccess(t *testing.T) { 130 147 defer tests.PrepareTestEnv(t)() 131 148 // repo10 have code, pulls units.
+1 -1
tests/integration/repo_test.go
··· 166 166 }) 167 167 168 168 // convert "2017-06-14 21:54:21 +0800" to "Wed, 14 Jun 2017 13:54:21 UTC" 169 - htmlTimeString, _ := s.Find("relative-time.time-since").Attr("datetime") 169 + htmlTimeString, _ := s.Find("relative-time").Attr("datetime") 170 170 htmlTime, _ := time.Parse(time.RFC3339, htmlTimeString) 171 171 f.commitTime = htmlTime.In(time.Local).Format(time.RFC1123) 172 172 items = append(items, f)
+41
tests/integration/repo_webhook_test.go
··· 1 + // Copyright 2024 The Gitea Authors. All rights reserved. 2 + // SPDX-License-Identifier: MIT 3 + 4 + package integration 5 + 6 + import ( 7 + "net/http" 8 + "strings" 9 + "testing" 10 + 11 + "code.gitea.io/gitea/tests" 12 + 13 + "github.com/PuerkitoBio/goquery" 14 + "github.com/stretchr/testify/assert" 15 + ) 16 + 17 + func TestNewWebHookLink(t *testing.T) { 18 + defer tests.PrepareTestEnv(t)() 19 + session := loginUser(t, "user2") 20 + 21 + baseurl := "/user2/repo1/settings/hooks" 22 + tests := []string{ 23 + // webhook list page 24 + baseurl, 25 + // new webhook page 26 + baseurl + "/gitea/new", 27 + // edit webhook page 28 + baseurl + "/1", 29 + } 30 + 31 + for _, url := range tests { 32 + resp := session.MakeRequest(t, NewRequest(t, "GET", url), http.StatusOK) 33 + htmlDoc := NewHTMLParser(t, resp.Body) 34 + menus := htmlDoc.doc.Find(".ui.top.attached.header .ui.dropdown .menu a") 35 + menus.Each(func(i int, menu *goquery.Selection) { 36 + url, exist := menu.Attr("href") 37 + assert.True(t, exist) 38 + assert.True(t, strings.HasPrefix(url, baseurl)) 39 + }) 40 + } 41 + }
+6
updates.config.js
··· 1 + export default { 2 + exclude: [ 3 + '@mcaptcha/vanilla-glue', // breaking changes in rc versions need to be handled 4 + 'eslint-plugin-array-func', // need to migrate to eslint flat config first 5 + ], 6 + };
+18 -156
web_src/css/base.css
··· 116 116 } 117 117 118 118 * { 119 - scrollbar-color: var(--color-primary) transparent; 120 119 caret-color: var(--color-caret); 121 120 } 122 121 123 - ::-webkit-scrollbar { 124 - width: 10px; 125 - height: 10px; 126 - } 127 - 128 - ::-webkit-scrollbar-thumb { 129 - box-shadow: inset 0 0 0 6px var(--color-primary); 130 - border: 2px solid transparent; 131 - border-radius: var(--border-radius); 132 - } 133 - 134 - ::-webkit-scrollbar-thumb:window-inactive { 135 - box-shadow: inset 0 0 0 6px var(--color-primary); 136 - } 137 - 138 - ::-webkit-scrollbar-thumb:hover { 139 - box-shadow: inset 0 0 0 6px var(--color-primary-dark-2); 140 - } 141 - 142 - ::-webkit-scrollbar-corner { 143 - background: transparent; 144 - } 145 - 146 122 ::file-selector-button { 147 123 border: 1px solid var(--color-light-border); 148 124 color: var(--color-text-light); ··· 184 160 text-decoration-skip-ink: all; 185 161 } 186 162 187 - /* muted link = only colored when hovered */ 188 - /* silenced link = never colored */ 163 + /* a = always colored, underlined on hover */ 164 + /* a.muted = colored on hover, underlined on hover */ 165 + /* a.suppressed = never colored, underlined on hover */ 166 + /* a.silenced = never colored, never underlined */ 189 167 190 168 a.muted, 169 + a.suppressed, 191 170 a.silenced, 192 171 .muted-links a { 193 172 color: inherit; 194 173 } 195 174 196 175 a:hover, 176 + a.suppressed:hover, 197 177 a.muted:hover, 198 178 a.muted:hover [class*="color-text"], 199 179 .muted-links a:hover { 200 180 color: var(--color-primary); 201 181 } 202 182 203 - a.silenced:hover { 183 + a.silenced:hover, 184 + a.suppressed:hover { 204 185 color: inherit; 186 + } 187 + 188 + a.silenced:hover { 205 189 text-decoration: none; 206 190 } 207 191 ··· 259 243 } 260 244 261 245 .page-content .header-wrapper, 262 - .page-content .new-menu { 246 + .page-content overflow-menu { 263 247 margin-top: -15px !important; 264 248 padding-top: 15px !important; 265 249 } ··· 527 511 visibility: visible !important; 528 512 } 529 513 530 - .ui.message { 531 - background: var(--color-box-body); 532 - color: var(--color-text); 533 - box-shadow: none !important; 534 - border: 1px solid var(--color-secondary); 535 - } 536 - 537 - .ui.info.message .header, 538 - .ui.blue.message .header { 539 - color: var(--color-blue); 540 - } 541 - 542 - .ui.info.message, 543 - .ui.attached.info.message, 544 - .ui.blue.message, 545 - .ui.attached.blue.message { 546 - background: var(--color-info-bg); 547 - color: var(--color-info-text); 548 - border-color: var(--color-info-border); 549 - } 550 - 551 - .ui.success.message .header, 552 - .ui.positive.message .header, 553 - .ui.green.message .header { 554 - color: var(--color-green); 555 - } 556 - 557 - .ui.success.message, 558 - .ui.attached.success.message, 559 - .ui.positive.message, 560 - .ui.attached.positive.message { 561 - background: var(--color-success-bg); 562 - color: var(--color-success-text); 563 - border-color: var(--color-success-border); 564 - } 565 - 566 - .ui.error.message .header, 567 - .ui.negative.message .header, 568 - .ui.red.message .header { 569 - color: var(--color-red); 570 - } 571 - 572 - .ui.error.message, 573 - .ui.attached.error.message, 574 - .ui.red.message, 575 - .ui.attached.red.message, 576 - .ui.negative.message, 577 - .ui.attached.negative.message { 578 - background: var(--color-error-bg); 579 - color: var(--color-error-text); 580 - border-color: var(--color-error-border); 581 - } 582 - 583 - .ui.warning.message .header, 584 - .ui.yellow.message .header { 585 - color: var(--color-yellow); 586 - } 587 - 588 - .ui.warning.message, 589 - .ui.attached.warning.message, 590 - .ui.yellow.message, 591 - .ui.attached.yellow.message { 592 - background: var(--color-warning-bg); 593 - color: var(--color-warning-text); 594 - border-color: var(--color-warning-border); 595 - } 596 - 597 514 .ui.error.header { 598 515 background: var(--color-error-bg) !important; 599 516 color: var(--color-error-text) !important; ··· 1358 1275 } 1359 1276 } 1360 1277 1361 - .ui.menu.new-menu { 1362 - margin-bottom: 15px; 1363 - background: var(--color-header-wrapper); 1278 + overflow-menu { 1279 + margin-bottom: 15px !important; 1364 1280 border-bottom: 1px solid var(--color-secondary) !important; 1365 - overflow: auto; 1281 + display: flex; 1366 1282 } 1367 1283 1368 - .ui.menu.new-menu .new-menu-inner { 1284 + overflow-menu .overflow-menu-items { 1369 1285 display: flex; 1370 - margin-left: auto; 1371 - margin-right: auto; 1372 - overflow-x: auto; 1373 - width: 100%; 1374 - mask-image: linear-gradient(to right, #000 0%, #000 calc(100% - 60px), transparent 100%); 1375 - -webkit-mask-image: linear-gradient(to right, #000 0%, #000 calc(100% - 60px), transparent 100%); 1286 + flex: 1; 1376 1287 } 1377 1288 1378 - .ui.menu.new-menu .item { 1289 + overflow-menu .overflow-menu-items .item { 1379 1290 margin-bottom: 0 !important; /* reset fomantic's margin, because the active menu has special bottom border */ 1380 - } 1381 - 1382 - @media (max-width: 767.98px) { 1383 - .ui.menu.new-menu .item { 1384 - width: auto !important; 1385 - } 1386 - } 1387 - 1388 - .ui.menu.new-menu .item:first-child { 1389 - margin-left: auto; /* "justify-content: center" doesn't work with "overflow: auto", so use margin: auto */ 1390 - } 1391 - 1392 - .ui.menu.new-menu .item:last-child { 1393 - padding-right: 30px !important; 1394 - margin-right: auto; 1395 - } 1396 - 1397 - .ui.menu.new-menu::-webkit-scrollbar { 1398 - height: 6px; 1399 - display: none; 1400 - } 1401 - 1402 - .ui.menu.new-menu::-webkit-scrollbar-track { 1403 - background: none !important; 1404 - } 1405 - 1406 - .ui.menu.new-menu::-webkit-scrollbar-thumb { 1407 - box-shadow: none !important; 1408 - } 1409 - 1410 - .ui.menu.new-menu:hover::-webkit-scrollbar { 1411 - display: block; 1412 - } 1413 - 1414 - .repos-search { 1415 - padding-bottom: 0 !important; 1416 - } 1417 - 1418 - .repos-filter { 1419 - margin-top: 0 !important; 1420 - border-bottom-width: 0 !important; 1421 - margin-bottom: 2px !important; 1422 - justify-content: space-evenly; 1423 - } 1424 - 1425 - .ui.secondary.pointing.menu.repos-filter .item { 1426 - padding-left: 4.5px; 1427 - padding-right: 4.5px; 1428 1291 } 1429 1292 1430 1293 .activity-bar-graph { ··· 1932 1795 background: var(--color-body); 1933 1796 border-color: var(--color-secondary); 1934 1797 color: var(--color-text); 1935 - margin-top: 1px; /* offset fomantic's margin-bottom: -1px */ 1936 1798 } 1937 1799 1938 1800 .ui.segment .ui.tabular.menu .active.item,
+1
web_src/css/index.css
··· 12 12 @import "./modules/divider.css"; 13 13 @import "./modules/svg.css"; 14 14 @import "./modules/flexcontainer.css"; 15 + @import "./modules/message.css"; 15 16 16 17 @import "./shared/flex-list.css"; 17 18 @import "./shared/milestone.css";
+5
web_src/css/modules/animations.css
··· 13 13 opacity: 0.3; 14 14 } 15 15 16 + .button.is-loading > * { 17 + opacity: 0; 18 + } 19 + 16 20 .is-loading::after { 17 21 content: ""; 18 22 position: absolute; ··· 20 24 left: 50%; 21 25 top: 50%; 22 26 height: min(4em, 66.6%); 27 + width: fit-content; /* compat: safari - https://bugs.webkit.org/show_bug.cgi?id=267625 */ 23 28 aspect-ratio: 1; 24 29 transform: translate(-50%, -50%); 25 30 animation: isloadingspin 1000ms infinite linear;
+99
web_src/css/modules/message.css
··· 1 + .ui.message { 2 + background: var(--color-box-body); 3 + color: var(--color-text); 4 + border: 1px solid var(--color-secondary); 5 + position: relative; 6 + min-height: 1em; 7 + margin: 1em 0; 8 + padding: 1em 1.5em; 9 + border-radius: var(--border-radius); 10 + } 11 + 12 + .ui.message:first-child { 13 + margin-top: 0; 14 + } 15 + 16 + .ui.message:last-child { 17 + margin-bottom: 0; 18 + } 19 + 20 + .ui.attached.message { 21 + margin-bottom: -1px; 22 + border-radius: var(--border-radius) var(--border-radius) 0 0; 23 + margin-left: -1px; 24 + margin-right: -1px; 25 + } 26 + 27 + .ui.attached + .ui.attached.message:not(.top):not(.bottom) { 28 + margin-top: -1px; 29 + border-radius: 0; 30 + } 31 + 32 + .ui.bottom.attached.message { 33 + margin-top: -1px; 34 + border-radius: 0 0 var(--border-radius) var(--border-radius); 35 + } 36 + 37 + .ui.bottom.attached.message:not(:last-child) { 38 + margin-bottom: 1em; 39 + } 40 + 41 + .ui.info.message .header, 42 + .ui.blue.message .header { 43 + color: var(--color-blue); 44 + } 45 + 46 + .ui.info.message, 47 + .ui.attached.info.message, 48 + .ui.blue.message, 49 + .ui.attached.blue.message { 50 + background: var(--color-info-bg); 51 + color: var(--color-info-text); 52 + border-color: var(--color-info-border); 53 + } 54 + 55 + .ui.success.message .header, 56 + .ui.positive.message .header, 57 + .ui.green.message .header { 58 + color: var(--color-green); 59 + } 60 + 61 + .ui.success.message, 62 + .ui.attached.success.message, 63 + .ui.positive.message, 64 + .ui.attached.positive.message { 65 + background: var(--color-success-bg); 66 + color: var(--color-success-text); 67 + border-color: var(--color-success-border); 68 + } 69 + 70 + .ui.error.message .header, 71 + .ui.negative.message .header, 72 + .ui.red.message .header { 73 + color: var(--color-red); 74 + } 75 + 76 + .ui.error.message, 77 + .ui.attached.error.message, 78 + .ui.red.message, 79 + .ui.attached.red.message, 80 + .ui.negative.message, 81 + .ui.attached.negative.message { 82 + background: var(--color-error-bg); 83 + color: var(--color-error-text); 84 + border-color: var(--color-error-border); 85 + } 86 + 87 + .ui.warning.message .header, 88 + .ui.yellow.message .header { 89 + color: var(--color-yellow); 90 + } 91 + 92 + .ui.warning.message, 93 + .ui.attached.warning.message, 94 + .ui.yellow.message, 95 + .ui.attached.yellow.message { 96 + background: var(--color-warning-bg); 97 + color: var(--color-warning-text); 98 + border-color: var(--color-warning-border); 99 + }
+28 -1
web_src/css/modules/tippy.css
··· 5 5 display: none !important; 6 6 } 7 7 8 + /* show target element once it's been moved by tippy.js */ 9 + .tippy-content .tippy-target { 10 + display: unset !important; 11 + } 12 + 8 13 [data-tippy-root] { 9 14 max-width: calc(100vw - 32px); 10 15 } ··· 46 51 .tippy-box[data-theme="menu"] { 47 52 background-color: var(--color-menu); 48 53 color: var(--color-text); 54 + box-shadow: 0 6px 18px var(--color-shadow); 49 55 } 50 56 51 57 .tippy-box[data-theme="menu"] .tippy-content { 52 - padding: 0; 58 + padding: 4px 0; 53 59 } 54 60 55 61 .tippy-box[data-theme="menu"] .tippy-svg-arrow-inner { 56 62 fill: var(--color-menu); 57 63 } 58 64 65 + .tippy-box[data-theme="menu"] .item { 66 + display: flex; 67 + align-items: center; 68 + padding: 9px 18px; 69 + color: inherit; 70 + text-decoration: none; 71 + gap: 10px; 72 + } 73 + 74 + .tippy-box[data-theme="menu"] .item:hover { 75 + background: var(--color-hover); 76 + } 77 + 78 + .tippy-box[data-theme="menu"] .item:focus { 79 + background: var(--color-active); 80 + } 81 + 59 82 /* box-with-header theme to look like .ui.attached.segment. can contain .ui.attached.header */ 83 + 84 + .tippy-box[data-theme="box-with-header"] { 85 + box-shadow: 0 6px 18px var(--color-shadow); 86 + } 60 87 61 88 .tippy-box[data-theme="box-with-header"] .tippy-content { 62 89 background: var(--color-box-body);
+4 -22
web_src/css/repo.css
··· 1065 1065 1066 1066 .repository.view.issue .comment-list .event .detail { 1067 1067 margin-top: 4px; 1068 - margin-left: 14px; 1069 - } 1070 - 1071 - .repository.view.issue .comment-list .event .detail .svg { 1072 - margin-right: 2px; 1068 + margin-left: 15px; 1073 1069 } 1074 1070 1075 1071 .repository.view.issue .comment-list .event .segments { ··· 2038 2034 } 2039 2035 2040 2036 #cite-repo-modal #citation-panel { 2041 - width: 500px; 2042 - } 2043 - 2044 - @media (max-width: 767.98px) { 2045 - #cite-repo-modal #citation-panel { 2046 - width: 100%; 2047 - } 2037 + display: flex; 2038 + width: 100%; 2048 2039 } 2049 2040 2050 2041 #cite-repo-modal #citation-panel input { ··· 2064 2055 padding: 5px 10px; 2065 2056 font-size: 1.2em; 2066 2057 line-height: 1.4; 2058 + flex: 1; 2067 2059 } 2068 2060 2069 2061 #cite-repo-modal #citation-panel #citation-copy-apa, ··· 2784 2776 .code-diff-split tbody tr td:nth-child(5), 2785 2777 .code-diff-split tbody tr td.add-comment-right { 2786 2778 border-left: 1px solid var(--color-secondary); 2787 - } 2788 - 2789 - .repository .ui.menu.new-menu { 2790 - background: none !important; 2791 - } 2792 - 2793 - @media (max-width: 1200px) { 2794 - .repository .ui.menu.new-menu::after { 2795 - background: none !important; 2796 - } 2797 2779 } 2798 2780 2799 2781 .migrate-entries {
-11
web_src/css/repo/header.css
··· 74 74 background-color: var(--color-header-wrapper); 75 75 } 76 76 77 - .repository .header-wrapper .new-menu { 78 - padding-top: 0 !important; 79 - margin-top: 0 !important; 80 - margin-bottom: 0 !important; 81 - } 82 - 83 - .repository .header-wrapper .new-menu .item { 84 - margin-left: 0 !important; 85 - margin-right: 0 !important; 86 - } 87 - 88 77 @media (max-width: 767.98px) { 89 78 .repo-header .flex-item { 90 79 flex-grow: 1;
-5
web_src/css/repo/linebutton.css
··· 2 2 color: var(--color-text-dark) !important; 3 3 } 4 4 5 - .code-line-menu { 6 - width: auto !important; 7 - border: none !important; /* the border is provided by tippy, not using the `.ui.menu` border */ 8 - } 9 - 10 5 .code-line-button { 11 6 background-color: var(--color-menu); 12 7 color: var(--color-text-light);
-7
web_src/css/shared/repoorg.css
··· 5 5 margin-left: 15px; 6 6 } 7 7 8 - .repository .ui.secondary.stackable.pointing.menu, 9 - .organization .ui.secondary.stackable.pointing.menu { 10 - flex-wrap: wrap; 11 - margin-top: 5px; 12 - margin-bottom: 10px; 13 - } 14 - 15 8 .repository .ui.tabs.container, 16 9 .organization .ui.tabs.container { 17 10 margin-top: 14px;
+70 -70
web_src/css/themes/theme-gitea-dark.css
··· 30 30 --color-primary-alpha-90: #4183c4e1; 31 31 --color-primary-hover: var(--color-primary-light-1); 32 32 --color-primary-active: var(--color-primary-light-2); 33 - --color-secondary: #3f4346; 34 - --color-secondary-dark-1: #464a4d; 35 - --color-secondary-dark-2: #4f5356; 36 - --color-secondary-dark-3: #5f6366; 37 - --color-secondary-dark-4: #72767a; 38 - --color-secondary-dark-5: #7f8488; 39 - --color-secondary-dark-6: #8d9297; 40 - --color-secondary-dark-7: #999ea3; 41 - --color-secondary-dark-8: #a6abaf; 42 - --color-secondary-dark-9: #aeb3b8; 43 - --color-secondary-dark-10: #babfc4; 44 - --color-secondary-dark-11: #c5cbd0; 45 - --color-secondary-dark-12: #ced4da; 46 - --color-secondary-dark-13: #d1d7dd; 47 - --color-secondary-light-1: #313538; 48 - --color-secondary-light-2: #272b2e; 49 - --color-secondary-light-3: #1e2225; 50 - --color-secondary-light-4: #171b1e; 51 - --color-secondary-alpha-10: #3f434619; 52 - --color-secondary-alpha-20: #3f434633; 53 - --color-secondary-alpha-30: #3f43464b; 54 - --color-secondary-alpha-40: #3f434666; 55 - --color-secondary-alpha-50: #3f434680; 56 - --color-secondary-alpha-60: #3f434699; 57 - --color-secondary-alpha-70: #3f4346b3; 58 - --color-secondary-alpha-80: #3f4346cc; 59 - --color-secondary-alpha-90: #3f4346e1; 33 + --color-secondary: #3b444a; 34 + --color-secondary-dark-1: #424b51; 35 + --color-secondary-dark-2: #4a545b; 36 + --color-secondary-dark-3: #59646c; 37 + --color-secondary-dark-4: #6b7681; 38 + --color-secondary-dark-5: #78858f; 39 + --color-secondary-dark-6: #87929d; 40 + --color-secondary-dark-7: #939ea9; 41 + --color-secondary-dark-8: #a1acb4; 42 + --color-secondary-dark-9: #aab3bc; 43 + --color-secondary-dark-10: #b6bfc8; 44 + --color-secondary-dark-11: #c2cbd3; 45 + --color-secondary-dark-12: #ccd4dc; 46 + --color-secondary-dark-13: #cfd7df; 47 + --color-secondary-light-1: #2e353b; 48 + --color-secondary-light-2: #2b353e; 49 + --color-secondary-light-3: #1c2227; 50 + --color-secondary-light-4: #161b1f; 51 + --color-secondary-alpha-10: #3b444a19; 52 + --color-secondary-alpha-20: #3b444a33; 53 + --color-secondary-alpha-30: #3b444a4b; 54 + --color-secondary-alpha-40: #3b444a66; 55 + --color-secondary-alpha-50: #3b444a80; 56 + --color-secondary-alpha-60: #3b444a99; 57 + --color-secondary-alpha-70: #3b444ab3; 58 + --color-secondary-alpha-80: #3b444acc; 59 + --color-secondary-alpha-90: #3b444ae1; 60 60 --color-secondary-button: var(--color-secondary-dark-4); 61 61 --color-secondary-hover: var(--color-secondary-dark-3); 62 62 --color-secondary-active: var(--color-secondary-dark-2); 63 63 /* console colors - used for actions console and console files */ 64 64 --color-console-fg: #f8f8f9; 65 65 --color-console-fg-subtle: #bec4c8; 66 - --color-console-bg: #181b1d; 67 - --color-console-border: #313538; 68 - --color-console-hover-bg: #ffffff16; 69 - --color-console-active-bg: #313538; 70 - --color-console-menu-bg: #272b2e; 71 - --color-console-menu-border: #464a4d; 66 + --color-console-bg: #171b1e; 67 + --color-console-border: #2e353b; 68 + --color-console-hover-bg: #e8e8ff16; 69 + --color-console-active-bg: #2e353b; 70 + --color-console-menu-bg: #252b30; 71 + --color-console-menu-border: #424b51; 72 72 /* named colors */ 73 73 --color-red: #cc4848; 74 74 --color-orange: #cc580c; ··· 81 81 --color-purple: #b259d0; 82 82 --color-pink: #d22e8b; 83 83 --color-brown: #a47252; 84 - --color-black: #1f2326; 84 + --color-black: #1d2328; 85 85 /* light variants - produced via Sass scale-color(color, $lightness: +10%) */ 86 86 --color-red-light: #d15a5a; 87 87 --color-orange-light: #f6a066; ··· 94 94 --color-purple-light: #ba6ad5; 95 95 --color-pink-light: #d74397; 96 96 --color-brown-light: #b08061; 97 - --color-black-light: #46494d; 97 + --color-black-light: #424851; 98 98 /* dark 1 variants - produced via Sass scale-color(color, $lightness: -10%) */ 99 99 --color-red-dark-1: #c23636; 100 100 --color-orange-dark-1: #f38236; ··· 107 107 --color-purple-dark-1: #a742c9; 108 108 --color-pink-dark-1: #be297d; 109 109 --color-brown-dark-1: #94674a; 110 - --color-black-dark-1: #2c2f35; 110 + --color-black-dark-1: #292e38; 111 111 /* dark 2 variants - produced via Sass scale-color(color, $lightness: -20%) */ 112 112 --color-red-dark-2: #ad3030; 113 113 --color-orange-dark-2: #f16e17; ··· 120 120 --color-purple-dark-2: #9834b9; 121 121 --color-pink-dark-2: #a9246f; 122 122 --color-brown-dark-2: #835b42; 123 - --color-black-dark-2: #292a2e; 123 + --color-black-dark-2: #272930; 124 124 /* ansi colors used for actions console and console files */ 125 - --color-ansi-black: #1f2326; 125 + --color-ansi-black: #1d2328; 126 126 --color-ansi-red: #cc4848; 127 127 --color-ansi-green: #87ab63; 128 128 --color-ansi-yellow: #cc9903; ··· 130 130 --color-ansi-magenta: #d22e8b; 131 131 --color-ansi-cyan: #00918a; 132 132 --color-ansi-white: var(--color-console-fg-subtle); 133 - --color-ansi-bright-black: #46494d; 133 + --color-ansi-bright-black: #424851; 134 134 --color-ansi-bright-red: #d15a5a; 135 135 --color-ansi-bright-green: #93b373; 136 136 --color-ansi-bright-yellow: #eaaf03; ··· 139 139 --color-ansi-bright-cyan: #00b6ad; 140 140 --color-ansi-bright-white: var(--color-console-fg); 141 141 /* other colors */ 142 - --color-grey: #3c4043; 143 - --color-grey-light: #898e92; 142 + --color-grey: #384147; 143 + --color-grey-light: #828f99; 144 144 --color-gold: #b1983b; 145 145 --color-white: #ffffff; 146 146 --color-diff-removed-word-bg: #6f3333; ··· 151 151 --color-diff-removed-row-border: #634343; 152 152 --color-diff-moved-row-border: #bcca6f; 153 153 --color-diff-added-row-border: #314a37; 154 - --color-diff-inactive: #24282b; 154 + --color-diff-inactive: #22282d; 155 155 --color-error-border: #a04141; 156 156 --color-error-bg: #522; 157 157 --color-error-bg-active: #744; ··· 180 180 --color-orange-badge-hover-bg: #f2711c4d; 181 181 --color-git: #f05133; 182 182 /* target-based colors */ 183 - --color-body: #1f2326; 184 - --color-box-header: #202427; 185 - --color-box-body: #191d20; 186 - --color-box-body-highlight: #1d2124; 183 + --color-body: #1e2224; 184 + --color-box-header: #1a1d1f; 185 + --color-box-body: #14171a; 186 + --color-box-body-highlight: #121517; 187 187 --color-text-dark: #f8f8f9; 188 188 --color-text: #ced2d5; 189 189 --color-text-light: #bec4c8; ··· 191 191 --color-text-light-2: #8d969c; 192 192 --color-text-light-3: #747f87; 193 193 --color-footer: var(--color-nav-bg); 194 - --color-timeline: #383c3f; 194 + --color-timeline: #353c42; 195 195 --color-input-text: var(--color-text-dark); 196 - --color-input-background: #161a1d; 197 - --color-input-toggle-background: #313538; 196 + --color-input-background: #151a1e; 197 + --color-input-toggle-background: #2e353b; 198 198 --color-input-border: var(--color-secondary); 199 199 --color-input-border-hover: var(--color-secondary-dark-1); 200 - --color-header-wrapper: #191d20; 201 - --color-light: #00000028; 200 + --color-header-wrapper: #181c20; 201 + --color-light: #00001728; 202 202 --color-light-mimic-enabled: rgba(0, 0, 0, calc(40 / 255 * 222 / 255 / var(--opacity-disabled))); 203 - --color-light-border: #ffffff28; 204 - --color-hover: #ffffff19; 205 - --color-active: #ffffff24; 206 - --color-menu: #161a1d; 207 - --color-card: #161a1d; 208 - --color-markup-table-row: #ffffff06; 209 - --color-markup-code-block: #ffffff16; 210 - --color-button: #161a1d; 203 + --color-light-border: #e8e8ff28; 204 + --color-hover: #e8e8ff19; 205 + --color-active: #e8e8ff24; 206 + --color-menu: #151a1e; 207 + --color-card: #151a1e; 208 + --color-markup-table-row: #e8e8ff06; 209 + --color-markup-code-block: #e8e8ff16; 210 + --color-button: #151a1e; 211 211 --color-code-bg: #191d20; 212 212 --color-code-sidebar-bg: #1b1f22; 213 - --color-shadow: #00000058; 213 + --color-shadow: #00001758; 214 214 --color-secondary-bg: #2f3135; 215 215 --color-expand-button: #414348; 216 216 --color-placeholder-text: var(--color-text-light-3); 217 217 --color-editor-line-highlight: var(--color-primary-light-5); 218 218 --color-project-board-bg: var(--color-secondary-light-2); 219 - --color-project-board-dark-label: #111111; 220 - --color-project-board-light-label: #eeeeee; 219 + --color-project-board-dark-label: #0e1011; 220 + --color-project-board-light-label: #dde0e2; 221 221 --color-caret: var(--color-text); /* should ideally be --color-text-dark, see #15651 */ 222 - --color-reaction-bg: #ffffff12; 222 + --color-reaction-bg: #e8e8ff12; 223 223 --color-reaction-hover-bg: var(--color-primary-light-4); 224 224 --color-reaction-active-bg: var(--color-primary-light-5); 225 - --color-tooltip-text: #ffffff; 226 - --color-tooltip-bg: #000000f0; 227 - --color-nav-bg: #1b1f22; 228 - --color-nav-hover-bg: #272b2e; 225 + --color-tooltip-text: #fafafb; 226 + --color-tooltip-bg: #000017f0; 227 + --color-nav-bg: #16191c; 228 + --color-nav-hover-bg: var(--color-secondary-light-1); 229 229 --color-nav-text: var(--color-text); 230 230 --color-label-text: var(--color-text); 231 - --color-label-bg: #7a7f834b; 232 - --color-label-hover-bg: #7a7f83a0; 233 - --color-label-active-bg: #7a7f83ff; 231 + --color-label-bg: #73828e4b; 232 + --color-label-hover-bg: #73828ea0; 233 + --color-label-active-bg: #73828eff; 234 234 --color-accent: var(--color-primary-light-1); 235 235 --color-small-accent: var(--color-primary-light-5); 236 236 --color-active-line: #534d1b;
+68 -68
web_src/css/themes/theme-gitea-light.css
··· 30 30 --color-primary-alpha-90: #4183c4e1; 31 31 --color-primary-hover: var(--color-primary-dark-1); 32 32 --color-primary-active: var(--color-primary-dark-2); 33 - --color-secondary: #dedede; 34 - --color-secondary-dark-1: #cecece; 35 - --color-secondary-dark-2: #bfbfbf; 36 - --color-secondary-dark-3: #a0a0a0; 37 - --color-secondary-dark-4: #909090; 38 - --color-secondary-dark-5: #818181; 39 - --color-secondary-dark-6: #717171; 40 - --color-secondary-dark-7: #626262; 41 - --color-secondary-dark-8: #525252; 42 - --color-secondary-dark-9: #434343; 43 - --color-secondary-dark-10: #333333; 44 - --color-secondary-dark-11: #242424; 45 - --color-secondary-dark-12: #141414; 46 - --color-secondary-dark-13: #040404; 47 - --color-secondary-light-1: #e5e5e5; 48 - --color-secondary-light-2: #ebebeb; 49 - --color-secondary-light-3: #f2f2f2; 50 - --color-secondary-light-4: #f8f8f8; 51 - --color-secondary-alpha-10: #dedede19; 52 - --color-secondary-alpha-20: #dedede33; 53 - --color-secondary-alpha-30: #dedede4b; 54 - --color-secondary-alpha-40: #dedede66; 55 - --color-secondary-alpha-50: #dedede80; 56 - --color-secondary-alpha-60: #dedede99; 57 - --color-secondary-alpha-70: #dededeb3; 58 - --color-secondary-alpha-80: #dededecc; 59 - --color-secondary-alpha-90: #dededee1; 33 + --color-secondary: #d0d7de; 34 + --color-secondary-dark-1: #c7ced5; 35 + --color-secondary-dark-2: #b9c0c7; 36 + --color-secondary-dark-3: #99a0a7; 37 + --color-secondary-dark-4: #899097; 38 + --color-secondary-dark-5: #7a8188; 39 + --color-secondary-dark-6: #6a7178; 40 + --color-secondary-dark-7: #5b6269; 41 + --color-secondary-dark-8: #4b5259; 42 + --color-secondary-dark-9: #3c434a; 43 + --color-secondary-dark-10: #2c333a; 44 + --color-secondary-dark-11: #1d242b; 45 + --color-secondary-dark-12: #0d141b; 46 + --color-secondary-dark-13: #00040b; 47 + --color-secondary-light-1: #dee5ec; 48 + --color-secondary-light-2: #e4ebf2; 49 + --color-secondary-light-3: #ebf2f9; 50 + --color-secondary-light-4: #f1f8ff; 51 + --color-secondary-alpha-10: #d0d7de19; 52 + --color-secondary-alpha-20: #d0d7de33; 53 + --color-secondary-alpha-30: #d0d7de4b; 54 + --color-secondary-alpha-40: #d0d7de66; 55 + --color-secondary-alpha-50: #d0d7de80; 56 + --color-secondary-alpha-60: #d0d7de99; 57 + --color-secondary-alpha-70: #d0d7deb3; 58 + --color-secondary-alpha-80: #d0d7decc; 59 + --color-secondary-alpha-90: #d0d7dee1; 60 60 --color-secondary-button: var(--color-secondary-dark-4); 61 61 --color-secondary-hover: var(--color-secondary-dark-5); 62 62 --color-secondary-active: var(--color-secondary-dark-6); ··· 81 81 --color-purple: #a333c8; 82 82 --color-pink: #e03997; 83 83 --color-brown: #a5673f; 84 - --color-black: #1b1c1d; 84 + --color-black: #191c1d; 85 85 /* light variants - produced via Sass scale-color(color, $lightness: +25%) */ 86 86 --color-red-light: #e45e5e; 87 87 --color-orange-light: #f59555; ··· 107 107 --color-purple-dark-1: #932eb4; 108 108 --color-pink-dark-1: #db228a; 109 109 --color-brown-dark-1: #955d39; 110 - --color-black-dark-1: #18191a; 110 + --color-black-dark-1: #16191c; 111 111 /* dark 2 variants - produced via Sass scale-color(color, $lightness: -20%) */ 112 112 --color-red-dark-2: #b11e1e; 113 113 --color-orange-dark-2: #cc580c; ··· 120 120 --color-purple-dark-2: #8229a0; 121 121 --color-pink-dark-2: #c21e7b; 122 122 --color-brown-dark-2: #845232; 123 - --color-black-dark-2: #161617; 123 + --color-black-dark-2: #131619; 124 124 /* ansi colors used for actions console and console files */ 125 125 --color-ansi-black: #1f2326; 126 126 --color-ansi-red: #cc4848; ··· 139 139 --color-ansi-bright-cyan: #00b6ad; 140 140 --color-ansi-bright-white: var(--color-console-fg); 141 141 /* other colors */ 142 - --color-grey: #707070; 143 - --color-grey-light: #838383; 142 + --color-grey: #697077; 143 + --color-grey-light: #7c838a; 144 144 --color-gold: #a1882b; 145 145 --color-white: #ffffff; 146 146 --color-diff-removed-word-bg: #fdb8c0; ··· 151 151 --color-diff-removed-row-border: #f1c0c0; 152 152 --color-diff-moved-row-border: #d0e27f; 153 153 --color-diff-added-row-border: #e6ffed; 154 - --color-diff-inactive: #f2f2f2; 154 + --color-diff-inactive: #f0f2f4; 155 155 --color-error-border: #e0b4b4; 156 156 --color-error-bg: #fff6f6; 157 157 --color-error-bg-active: #fbb; ··· 181 181 --color-git: #f05133; 182 182 /* target-based colors */ 183 183 --color-body: #ffffff; 184 - --color-box-header: #f7f7f7; 184 + --color-box-header: #f1f3f5; 185 185 --color-box-body: #ffffff; 186 - --color-box-body-highlight: #fafafa; 187 - --color-text-dark: #080808; 188 - --color-text: #212121; 189 - --color-text-light: #555555; 190 - --color-text-light-1: #6a6a6a; 191 - --color-text-light-2: #808080; 192 - --color-text-light-3: #a0a0a0; 186 + --color-box-body-highlight: #f4faff; 187 + --color-text-dark: #03080d; 188 + --color-text: #1c2126; 189 + --color-text-light: #3c434a; 190 + --color-text-light-1: #4b5259; 191 + --color-text-light-2: #6a7178; 192 + --color-text-light-3: #899097; 193 193 --color-footer: var(--color-nav-bg); 194 - --color-timeline: #ececec; 194 + --color-timeline: #d0d7de; 195 195 --color-input-text: var(--color-text-dark); 196 - --color-input-background: #fafafa; 197 - --color-input-toggle-background: #dedede; 196 + --color-input-background: #f8f9fb; 197 + --color-input-toggle-background: #d0d7de; 198 198 --color-input-border: var(--color-secondary); 199 199 --color-input-border-hover: var(--color-secondary-dark-1); 200 - --color-header-wrapper: transparent; 201 - --color-light: #00000006; 200 + --color-header-wrapper: #fafbfc; 201 + --color-light: #00001706; 202 202 --color-light-mimic-enabled: rgba(0, 0, 0, calc(6 / 255 * 222 / 255 / var(--opacity-disabled))); 203 - --color-light-border: #0000001d; 204 - --color-hover: #00000014; 205 - --color-active: #0000001b; 206 - --color-menu: #fafafa; 207 - --color-card: #fafafa; 208 - --color-markup-table-row: #00000008; 209 - --color-markup-code-block: #00000010; 210 - --color-button: #fafafa; 211 - --color-code-bg: #ffffff; 212 - --color-code-sidebar-bg: #f5f5f5; 213 - --color-shadow: #00000026; 214 - --color-secondary-bg: #f4f4f4; 203 + --color-light-border: #0000171d; 204 + --color-hover: #00001708; 205 + --color-active: #00001714; 206 + --color-menu: #f8f9fb; 207 + --color-card: #f8f9fb; 208 + --color-markup-table-row: #00001708; 209 + --color-markup-code-block: #00001710; 210 + --color-button: #f8f9fb; 211 + --color-code-bg: #fafdff; 212 + --color-code-sidebar-bg: #f2f5f8; 213 + --color-shadow: #00001726; 214 + --color-secondary-bg: #f2f5f8; 215 215 --color-expand-button: #d8efff; 216 216 --color-placeholder-text: var(--color-text-light-3); 217 217 --color-editor-line-highlight: var(--color-primary-light-6); 218 218 --color-project-board-bg: var(--color-secondary-light-4); 219 - --color-project-board-dark-label: #111111; 220 - --color-project-board-light-label: #eeeeee; 219 + --color-project-board-dark-label: #0e1114; 220 + --color-project-board-light-label: #eaeef2; 221 221 --color-caret: var(--color-text-dark); 222 - --color-reaction-bg: #0000000a; 222 + --color-reaction-bg: #0000170a; 223 223 --color-reaction-hover-bg: var(--color-primary-light-5); 224 224 --color-reaction-active-bg: var(--color-primary-light-6); 225 - --color-tooltip-text: #ffffff; 226 - --color-tooltip-bg: #000000f0; 227 - --color-nav-bg: #ffffff; 228 - --color-nav-hover-bg: #ebebeb; 225 + --color-tooltip-text: #fbfdff; 226 + --color-tooltip-bg: #000017f0; 227 + --color-nav-bg: #f8f9fb; 228 + --color-nav-hover-bg: var(--color-secondary-light-1); 229 229 --color-nav-text: var(--color-text); 230 230 --color-label-text: var(--color-text); 231 - --color-label-bg: #9d9d9d4b; 232 - --color-label-hover-bg: #9d9d9da0; 233 - --color-label-active-bg: #9d9d9dff; 231 + --color-label-bg: #949da64b; 232 + --color-label-hover-bg: #949da6a0; 233 + --color-label-active-bg: #949da6ff; 234 234 --color-accent: var(--color-primary-light-1); 235 235 --color-small-accent: var(--color-primary-light-6); 236 236 --color-active-line: #fffbdd;
-684
web_src/fomantic/build/semantic.css
··· 14927 14927 Site Overrides 14928 14928 *******************************/ 14929 14929 /*! 14930 - * # Fomantic-UI - Message 14931 - * http://github.com/fomantic/Fomantic-UI/ 14932 - * 14933 - * 14934 - * Released under the MIT license 14935 - * http://opensource.org/licenses/MIT 14936 - * 14937 - */ 14938 - 14939 - /******************************* 14940 - Message 14941 - *******************************/ 14942 - 14943 - .ui.message { 14944 - position: relative; 14945 - min-height: 1em; 14946 - margin: 1em 0; 14947 - background: #F8F8F9; 14948 - padding: 1em 1.5em; 14949 - line-height: 1.4285em; 14950 - color: rgba(0, 0, 0, 0.87); 14951 - transition: opacity 0.1s ease, color 0.1s ease, background 0.1s ease, box-shadow 0.1s ease; 14952 - border-radius: 0.28571429rem; 14953 - box-shadow: 0 0 0 1px rgba(34, 36, 38, 0.22) inset, 0 0 0 0 rgba(0, 0, 0, 0); 14954 - } 14955 - 14956 - .ui.message:first-child { 14957 - margin-top: 0; 14958 - } 14959 - 14960 - .ui.message:last-child { 14961 - margin-bottom: 0; 14962 - } 14963 - 14964 - /*-------------- 14965 - Content 14966 - ---------------*/ 14967 - 14968 - /* Header */ 14969 - 14970 - .ui.message .header { 14971 - display: block; 14972 - font-family: var(--fonts-regular); 14973 - font-weight: 500; 14974 - margin: -0.14285714em 0 0 0; 14975 - } 14976 - 14977 - /* Default font size */ 14978 - 14979 - .ui.message .header:not(.ui) { 14980 - font-size: 1.14285714em; 14981 - } 14982 - 14983 - /* Paragraph */ 14984 - 14985 - .ui.message p { 14986 - opacity: 0.85; 14987 - margin: 0.75em 0; 14988 - } 14989 - 14990 - .ui.message p:first-child { 14991 - margin-top: 0; 14992 - } 14993 - 14994 - .ui.message p:last-child { 14995 - margin-bottom: 0; 14996 - } 14997 - 14998 - .ui.message .header + p { 14999 - margin-top: 0.25em; 15000 - } 15001 - 15002 - /* List */ 15003 - 15004 - .ui.message .list:not(.ui) { 15005 - text-align: left; 15006 - padding: 0; 15007 - opacity: 0.85; 15008 - list-style-position: inside; 15009 - margin: 0.5em 0 0; 15010 - } 15011 - 15012 - .ui.message .list:not(.ui):first-child { 15013 - margin-top: 0; 15014 - } 15015 - 15016 - .ui.message .list:not(.ui):last-child { 15017 - margin-bottom: 0; 15018 - } 15019 - 15020 - .ui.message .list:not(.ui) li { 15021 - position: relative; 15022 - list-style-type: none; 15023 - margin: 0 0 0.3em 1em; 15024 - padding: 0; 15025 - } 15026 - 15027 - .ui.message .list:not(.ui) li:before { 15028 - position: absolute; 15029 - content: '•'; 15030 - left: -1em; 15031 - height: 100%; 15032 - vertical-align: baseline; 15033 - } 15034 - 15035 - .ui.message .list:not(.ui) li:last-child { 15036 - margin-bottom: 0; 15037 - } 15038 - 15039 - /* Icon */ 15040 - 15041 - .ui.message > i.icon { 15042 - margin-right: 0.6em; 15043 - } 15044 - 15045 - /* Close Icon */ 15046 - 15047 - .ui.message > .close.icon { 15048 - cursor: pointer; 15049 - position: absolute; 15050 - margin: 0; 15051 - top: 0.78575em; 15052 - right: 0.5em; 15053 - opacity: 0.7; 15054 - transition: opacity 0.1s ease; 15055 - } 15056 - 15057 - .ui.message > .close.icon:hover { 15058 - opacity: 1; 15059 - } 15060 - 15061 - /* First / Last Element */ 15062 - 15063 - .ui.message > :first-child { 15064 - margin-top: 0; 15065 - } 15066 - 15067 - .ui.message > :last-child { 15068 - margin-bottom: 0; 15069 - } 15070 - 15071 - /******************************* 15072 - Coupling 15073 - *******************************/ 15074 - 15075 - .ui.dropdown .menu > .message { 15076 - margin: 0 -1px; 15077 - } 15078 - 15079 - /******************************* 15080 - States 15081 - *******************************/ 15082 - 15083 - /*-------------- 15084 - Visible 15085 - ---------------*/ 15086 - 15087 - .ui.visible.visible.visible.visible.message { 15088 - display: block; 15089 - } 15090 - 15091 - .ui.icon.visible.visible.visible.visible.message { 15092 - display: flex; 15093 - } 15094 - 15095 - /*-------------- 15096 - Hidden 15097 - ---------------*/ 15098 - 15099 - .ui.hidden.hidden.hidden.hidden.message { 15100 - display: none; 15101 - } 15102 - 15103 - /******************************* 15104 - Variations 15105 - *******************************/ 15106 - 15107 - /*-------------- 15108 - Compact 15109 - ---------------*/ 15110 - 15111 - .ui.compact.message { 15112 - display: inline-block; 15113 - } 15114 - 15115 - .ui.compact.icon.message { 15116 - display: inline-flex; 15117 - width: auto; 15118 - } 15119 - 15120 - /*-------------- 15121 - Attached 15122 - ---------------*/ 15123 - 15124 - .ui.attached.message { 15125 - margin-bottom: -1px; 15126 - border-radius: 0.28571429rem 0.28571429rem 0 0; 15127 - box-shadow: 0 0 0 1px rgba(34, 36, 38, 0.15) inset; 15128 - margin-left: -1px; 15129 - margin-right: -1px; 15130 - } 15131 - 15132 - .ui.attached + .ui.attached.message:not(.top):not(.bottom) { 15133 - margin-top: -1px; 15134 - border-radius: 0; 15135 - } 15136 - 15137 - .ui.bottom.attached.message { 15138 - margin-top: -1px; 15139 - border-radius: 0 0 0.28571429rem 0.28571429rem; 15140 - box-shadow: 0 0 0 1px rgba(34, 36, 38, 0.15) inset, 0 1px 2px 0 rgba(34, 36, 38, 0.15); 15141 - } 15142 - 15143 - .ui.bottom.attached.message:not(:last-child) { 15144 - margin-bottom: 1em; 15145 - } 15146 - 15147 - .ui.attached.icon.message { 15148 - width: auto; 15149 - } 15150 - 15151 - /*-------------- 15152 - Icon 15153 - ---------------*/ 15154 - 15155 - .ui.icon.message { 15156 - display: flex; 15157 - width: 100%; 15158 - align-items: center; 15159 - } 15160 - 15161 - .ui.icon.message > i.icon:not(.close) { 15162 - display: block; 15163 - flex: 0 0 auto; 15164 - width: auto; 15165 - line-height: 1; 15166 - vertical-align: middle; 15167 - font-size: 3em; 15168 - opacity: 0.8; 15169 - } 15170 - 15171 - .ui.icon.message > .content { 15172 - display: block; 15173 - flex: 1 1 auto; 15174 - vertical-align: middle; 15175 - } 15176 - 15177 - .ui.icon.message > i.icon:not(.close) + .content { 15178 - padding-left: 0; 15179 - } 15180 - 15181 - .ui.icon.message > i.circular.icon { 15182 - width: 1em; 15183 - } 15184 - 15185 - /*-------------- 15186 - Floating 15187 - ---------------*/ 15188 - 15189 - .ui.floating.message { 15190 - box-shadow: 0 0 0 1px rgba(34, 36, 38, 0.22) inset, 0 2px 4px 0 rgba(34, 36, 38, 0.12), 0 2px 10px 0 rgba(34, 36, 38, 0.15); 15191 - } 15192 - 15193 - /*-------------- 15194 - Colors 15195 - ---------------*/ 15196 - 15197 - /*-------------- 15198 - Types 15199 - ---------------*/ 15200 - 15201 - .ui.positive.message { 15202 - background-color: #FCFFF5; 15203 - color: #2C662D; 15204 - } 15205 - 15206 - .ui.positive.message, 15207 - .ui.attached.positive.message { 15208 - box-shadow: 0 0 0 1px #A3C293 inset, 0 0 0 0 rgba(0, 0, 0, 0); 15209 - } 15210 - 15211 - .ui.floating.positive.message { 15212 - box-shadow: 0 0 0 1px #A3C293 inset, 0 2px 4px 0 rgba(34, 36, 38, 0.12), 0 2px 10px 0 rgba(34, 36, 38, 0.15); 15213 - } 15214 - 15215 - .ui.positive.message .header { 15216 - color: #1A531B; 15217 - } 15218 - 15219 - .ui.negative.message { 15220 - background-color: #FFF6F6; 15221 - color: #9F3A38; 15222 - } 15223 - 15224 - .ui.negative.message, 15225 - .ui.attached.negative.message { 15226 - box-shadow: 0 0 0 1px #E0B4B4 inset, 0 0 0 0 rgba(0, 0, 0, 0); 15227 - } 15228 - 15229 - .ui.floating.negative.message { 15230 - box-shadow: 0 0 0 1px #E0B4B4 inset, 0 2px 4px 0 rgba(34, 36, 38, 0.12), 0 2px 10px 0 rgba(34, 36, 38, 0.15); 15231 - } 15232 - 15233 - .ui.negative.message .header { 15234 - color: #912D2B; 15235 - } 15236 - 15237 - .ui.info.message { 15238 - background-color: #F8FFFF; 15239 - color: #276F86; 15240 - } 15241 - 15242 - .ui.info.message, 15243 - .ui.attached.info.message { 15244 - box-shadow: 0 0 0 1px #A9D5DE inset, 0 0 0 0 rgba(0, 0, 0, 0); 15245 - } 15246 - 15247 - .ui.floating.info.message { 15248 - box-shadow: 0 0 0 1px #A9D5DE inset, 0 2px 4px 0 rgba(34, 36, 38, 0.12), 0 2px 10px 0 rgba(34, 36, 38, 0.15); 15249 - } 15250 - 15251 - .ui.info.message .header { 15252 - color: #0E566C; 15253 - } 15254 - 15255 - .ui.warning.message { 15256 - background-color: #FFFAF3; 15257 - color: #573A08; 15258 - } 15259 - 15260 - .ui.warning.message, 15261 - .ui.attached.warning.message { 15262 - box-shadow: 0 0 0 1px #C9BA9B inset, 0 0 0 0 rgba(0, 0, 0, 0); 15263 - } 15264 - 15265 - .ui.floating.warning.message { 15266 - box-shadow: 0 0 0 1px #C9BA9B inset, 0 2px 4px 0 rgba(34, 36, 38, 0.12), 0 2px 10px 0 rgba(34, 36, 38, 0.15); 15267 - } 15268 - 15269 - .ui.warning.message .header { 15270 - color: #794B02; 15271 - } 15272 - 15273 - .ui.error.message { 15274 - background-color: #FFF6F6; 15275 - color: #9F3A38; 15276 - } 15277 - 15278 - .ui.error.message, 15279 - .ui.attached.error.message { 15280 - box-shadow: 0 0 0 1px #E0B4B4 inset, 0 0 0 0 rgba(0, 0, 0, 0); 15281 - } 15282 - 15283 - .ui.floating.error.message { 15284 - box-shadow: 0 0 0 1px #E0B4B4 inset, 0 2px 4px 0 rgba(34, 36, 38, 0.12), 0 2px 10px 0 rgba(34, 36, 38, 0.15); 15285 - } 15286 - 15287 - .ui.error.message .header { 15288 - color: #912D2B; 15289 - } 15290 - 15291 - .ui.success.message { 15292 - background-color: #FCFFF5; 15293 - color: #2C662D; 15294 - } 15295 - 15296 - .ui.success.message, 15297 - .ui.attached.success.message { 15298 - box-shadow: 0 0 0 1px #A3C293 inset, 0 0 0 0 rgba(0, 0, 0, 0); 15299 - } 15300 - 15301 - .ui.floating.success.message { 15302 - box-shadow: 0 0 0 1px #A3C293 inset, 0 2px 4px 0 rgba(34, 36, 38, 0.12), 0 2px 10px 0 rgba(34, 36, 38, 0.15); 15303 - } 15304 - 15305 - .ui.success.message .header { 15306 - color: #1A531B; 15307 - } 15308 - 15309 - .ui.primary.message { 15310 - background-color: #DFF0FF; 15311 - color: rgba(255, 255, 255, 0.9); 15312 - } 15313 - 15314 - .ui.primary.message, 15315 - .ui.attached.primary.message { 15316 - box-shadow: 0 0 0 1px #2185D0 inset, 0 0 0 0 rgba(0, 0, 0, 0); 15317 - } 15318 - 15319 - .ui.floating.primary.message { 15320 - box-shadow: 0 0 0 1px #2185D0 inset, 0 2px 4px 0 rgba(34, 36, 38, 0.12), 0 2px 10px 0 rgba(34, 36, 38, 0.15); 15321 - } 15322 - 15323 - .ui.primary.message .header { 15324 - color: rgba(242, 242, 242, 0.9); 15325 - } 15326 - 15327 - .ui.secondary.message { 15328 - background-color: #F4F4F4; 15329 - color: rgba(255, 255, 255, 0.9); 15330 - } 15331 - 15332 - .ui.secondary.message, 15333 - .ui.attached.secondary.message { 15334 - box-shadow: 0 0 0 1px #1B1C1D inset, 0 0 0 0 rgba(0, 0, 0, 0); 15335 - } 15336 - 15337 - .ui.floating.secondary.message { 15338 - box-shadow: 0 0 0 1px #1B1C1D inset, 0 2px 4px 0 rgba(34, 36, 38, 0.12), 0 2px 10px 0 rgba(34, 36, 38, 0.15); 15339 - } 15340 - 15341 - .ui.secondary.message .header { 15342 - color: rgba(242, 242, 242, 0.9); 15343 - } 15344 - 15345 - .ui.red.message { 15346 - background-color: #FFE8E6; 15347 - color: #DB2828; 15348 - } 15349 - 15350 - .ui.red.message, 15351 - .ui.attached.red.message { 15352 - box-shadow: 0 0 0 1px #DB2828 inset, 0 0 0 0 rgba(0, 0, 0, 0); 15353 - } 15354 - 15355 - .ui.floating.red.message { 15356 - box-shadow: 0 0 0 1px #DB2828 inset, 0 2px 4px 0 rgba(34, 36, 38, 0.12), 0 2px 10px 0 rgba(34, 36, 38, 0.15); 15357 - } 15358 - 15359 - .ui.red.message .header { 15360 - color: #c82121; 15361 - } 15362 - 15363 - .ui.orange.message { 15364 - background-color: #FFEDDE; 15365 - color: #F2711C; 15366 - } 15367 - 15368 - .ui.orange.message, 15369 - .ui.attached.orange.message { 15370 - box-shadow: 0 0 0 1px #F2711C inset, 0 0 0 0 rgba(0, 0, 0, 0); 15371 - } 15372 - 15373 - .ui.floating.orange.message { 15374 - box-shadow: 0 0 0 1px #F2711C inset, 0 2px 4px 0 rgba(34, 36, 38, 0.12), 0 2px 10px 0 rgba(34, 36, 38, 0.15); 15375 - } 15376 - 15377 - .ui.orange.message .header { 15378 - color: #e7640d; 15379 - } 15380 - 15381 - .ui.yellow.message { 15382 - background-color: #FFF8DB; 15383 - color: #B58105; 15384 - } 15385 - 15386 - .ui.yellow.message, 15387 - .ui.attached.yellow.message { 15388 - box-shadow: 0 0 0 1px #B58105 inset, 0 0 0 0 rgba(0, 0, 0, 0); 15389 - } 15390 - 15391 - .ui.floating.yellow.message { 15392 - box-shadow: 0 0 0 1px #B58105 inset, 0 2px 4px 0 rgba(34, 36, 38, 0.12), 0 2px 10px 0 rgba(34, 36, 38, 0.15); 15393 - } 15394 - 15395 - .ui.yellow.message .header { 15396 - color: #9c6f04; 15397 - } 15398 - 15399 - .ui.olive.message { 15400 - background-color: #FBFDEF; 15401 - color: #8ABC1E; 15402 - } 15403 - 15404 - .ui.olive.message, 15405 - .ui.attached.olive.message { 15406 - box-shadow: 0 0 0 1px #8ABC1E inset, 0 0 0 0 rgba(0, 0, 0, 0); 15407 - } 15408 - 15409 - .ui.floating.olive.message { 15410 - box-shadow: 0 0 0 1px #8ABC1E inset, 0 2px 4px 0 rgba(34, 36, 38, 0.12), 0 2px 10px 0 rgba(34, 36, 38, 0.15); 15411 - } 15412 - 15413 - .ui.olive.message .header { 15414 - color: #7aa61a; 15415 - } 15416 - 15417 - .ui.green.message { 15418 - background-color: #E5F9E7; 15419 - color: #1EBC30; 15420 - } 15421 - 15422 - .ui.green.message, 15423 - .ui.attached.green.message { 15424 - box-shadow: 0 0 0 1px #1EBC30 inset, 0 0 0 0 rgba(0, 0, 0, 0); 15425 - } 15426 - 15427 - .ui.floating.green.message { 15428 - box-shadow: 0 0 0 1px #1EBC30 inset, 0 2px 4px 0 rgba(34, 36, 38, 0.12), 0 2px 10px 0 rgba(34, 36, 38, 0.15); 15429 - } 15430 - 15431 - .ui.green.message .header { 15432 - color: #1aa62a; 15433 - } 15434 - 15435 - .ui.teal.message { 15436 - background-color: #E1F7F7; 15437 - color: #10A3A3; 15438 - } 15439 - 15440 - .ui.teal.message, 15441 - .ui.attached.teal.message { 15442 - box-shadow: 0 0 0 1px #10A3A3 inset, 0 0 0 0 rgba(0, 0, 0, 0); 15443 - } 15444 - 15445 - .ui.floating.teal.message { 15446 - box-shadow: 0 0 0 1px #10A3A3 inset, 0 2px 4px 0 rgba(34, 36, 38, 0.12), 0 2px 10px 0 rgba(34, 36, 38, 0.15); 15447 - } 15448 - 15449 - .ui.teal.message .header { 15450 - color: #0e8c8c; 15451 - } 15452 - 15453 - .ui.blue.message { 15454 - background-color: #DFF0FF; 15455 - color: #2185D0; 15456 - } 15457 - 15458 - .ui.blue.message, 15459 - .ui.attached.blue.message { 15460 - box-shadow: 0 0 0 1px #2185D0 inset, 0 0 0 0 rgba(0, 0, 0, 0); 15461 - } 15462 - 15463 - .ui.floating.blue.message { 15464 - box-shadow: 0 0 0 1px #2185D0 inset, 0 2px 4px 0 rgba(34, 36, 38, 0.12), 0 2px 10px 0 rgba(34, 36, 38, 0.15); 15465 - } 15466 - 15467 - .ui.blue.message .header { 15468 - color: #1e77ba; 15469 - } 15470 - 15471 - .ui.violet.message { 15472 - background-color: #EAE7FF; 15473 - color: #6435C9; 15474 - } 15475 - 15476 - .ui.violet.message, 15477 - .ui.attached.violet.message { 15478 - box-shadow: 0 0 0 1px #6435C9 inset, 0 0 0 0 rgba(0, 0, 0, 0); 15479 - } 15480 - 15481 - .ui.floating.violet.message { 15482 - box-shadow: 0 0 0 1px #6435C9 inset, 0 2px 4px 0 rgba(34, 36, 38, 0.12), 0 2px 10px 0 rgba(34, 36, 38, 0.15); 15483 - } 15484 - 15485 - .ui.violet.message .header { 15486 - color: #5a30b5; 15487 - } 15488 - 15489 - .ui.purple.message { 15490 - background-color: #F6E7FF; 15491 - color: #A333C8; 15492 - } 15493 - 15494 - .ui.purple.message, 15495 - .ui.attached.purple.message { 15496 - box-shadow: 0 0 0 1px #A333C8 inset, 0 0 0 0 rgba(0, 0, 0, 0); 15497 - } 15498 - 15499 - .ui.floating.purple.message { 15500 - box-shadow: 0 0 0 1px #A333C8 inset, 0 2px 4px 0 rgba(34, 36, 38, 0.12), 0 2px 10px 0 rgba(34, 36, 38, 0.15); 15501 - } 15502 - 15503 - .ui.purple.message .header { 15504 - color: #922eb4; 15505 - } 15506 - 15507 - .ui.pink.message { 15508 - background-color: #FFE3FB; 15509 - color: #E03997; 15510 - } 15511 - 15512 - .ui.pink.message, 15513 - .ui.attached.pink.message { 15514 - box-shadow: 0 0 0 1px #E03997 inset, 0 0 0 0 rgba(0, 0, 0, 0); 15515 - } 15516 - 15517 - .ui.floating.pink.message { 15518 - box-shadow: 0 0 0 1px #E03997 inset, 0 2px 4px 0 rgba(34, 36, 38, 0.12), 0 2px 10px 0 rgba(34, 36, 38, 0.15); 15519 - } 15520 - 15521 - .ui.pink.message .header { 15522 - color: #dd238b; 15523 - } 15524 - 15525 - .ui.brown.message { 15526 - background-color: #F1E2D3; 15527 - color: #A5673F; 15528 - } 15529 - 15530 - .ui.brown.message, 15531 - .ui.attached.brown.message { 15532 - box-shadow: 0 0 0 1px #A5673F inset, 0 0 0 0 rgba(0, 0, 0, 0); 15533 - } 15534 - 15535 - .ui.floating.brown.message { 15536 - box-shadow: 0 0 0 1px #A5673F inset, 0 2px 4px 0 rgba(34, 36, 38, 0.12), 0 2px 10px 0 rgba(34, 36, 38, 0.15); 15537 - } 15538 - 15539 - .ui.brown.message .header { 15540 - color: #935b38; 15541 - } 15542 - 15543 - .ui.grey.message { 15544 - background-color: #F4F4F4; 15545 - color: #767676; 15546 - } 15547 - 15548 - .ui.grey.message, 15549 - .ui.attached.grey.message { 15550 - box-shadow: 0 0 0 1px #767676 inset, 0 0 0 0 rgba(0, 0, 0, 0); 15551 - } 15552 - 15553 - .ui.floating.grey.message { 15554 - box-shadow: 0 0 0 1px #767676 inset, 0 2px 4px 0 rgba(34, 36, 38, 0.12), 0 2px 10px 0 rgba(34, 36, 38, 0.15); 15555 - } 15556 - 15557 - .ui.grey.message .header { 15558 - color: #696969; 15559 - } 15560 - 15561 - .ui.black.message { 15562 - background-color: #1B1C1D; 15563 - color: rgba(255, 255, 255, 0.9); 15564 - } 15565 - 15566 - .ui.black.message .header { 15567 - color: rgba(255, 255, 255, 0.9); 15568 - } 15569 - 15570 - /*-------------- 15571 - Sizes 15572 - ---------------*/ 15573 - 15574 - .ui.message { 15575 - font-size: 1em; 15576 - } 15577 - 15578 - .ui.mini.message { 15579 - font-size: 0.78571429em; 15580 - } 15581 - 15582 - .ui.tiny.message { 15583 - font-size: 0.85714286em; 15584 - } 15585 - 15586 - .ui.small.message { 15587 - font-size: 0.92857143em; 15588 - } 15589 - 15590 - .ui.large.message { 15591 - font-size: 1.14285714em; 15592 - } 15593 - 15594 - .ui.big.message { 15595 - font-size: 1.28571429em; 15596 - } 15597 - 15598 - .ui.huge.message { 15599 - font-size: 1.42857143em; 15600 - } 15601 - 15602 - .ui.massive.message { 15603 - font-size: 1.71428571em; 15604 - } 15605 - 15606 - /******************************* 15607 - Theme Overrides 15608 - *******************************/ 15609 - 15610 - /******************************* 15611 - Site Overrides 15612 - *******************************/ 15613 - /*! 15614 14930 * # Fomantic-UI - Modal 15615 14931 * http://github.com/fomantic/Fomantic-UI/ 15616 14932 *
-1
web_src/fomantic/semantic.json
··· 34 34 "label", 35 35 "list", 36 36 "menu", 37 - "message", 38 37 "modal", 39 38 "search", 40 39 "segment",
+40 -22
web_src/js/components/DashboardRepoList.vue
··· 384 384 </div> 385 385 </div> 386 386 </div> 387 - <div class="ui secondary tiny pointing borderless menu center grid repos-filter"> 388 - <a class="item" :class="{active: reposFilter === 'all'}" @click="changeReposFilter('all')"> 389 - {{ textAll }} 390 - <div v-show="reposFilter === 'all'" class="ui circular mini grey label">{{ repoTypeCount }}</div> 391 - </a> 392 - <a class="item" :class="{active: reposFilter === 'sources'}" @click="changeReposFilter('sources')"> 393 - {{ textSources }} 394 - <div v-show="reposFilter === 'sources'" class="ui circular mini grey label">{{ repoTypeCount }}</div> 395 - </a> 396 - <a class="item" :class="{active: reposFilter === 'forks'}" @click="changeReposFilter('forks')"> 397 - {{ textForks }} 398 - <div v-show="reposFilter === 'forks'" class="ui circular mini grey label">{{ repoTypeCount }}</div> 399 - </a> 400 - <a class="item" :class="{active: reposFilter === 'mirrors'}" @click="changeReposFilter('mirrors')" v-if="isMirrorsEnabled"> 401 - {{ textMirrors }} 402 - <div v-show="reposFilter === 'mirrors'" class="ui circular mini grey label">{{ repoTypeCount }}</div> 403 - </a> 404 - <a class="item" :class="{active: reposFilter === 'collaborative'}" @click="changeReposFilter('collaborative')"> 405 - {{ textCollaborative }} 406 - <div v-show="reposFilter === 'collaborative'" class="ui circular mini grey label">{{ repoTypeCount }}</div> 407 - </a> 408 - </div> 387 + <overflow-menu class="ui secondary pointing tabular borderless menu repos-filter"> 388 + <div class="overflow-menu-items tw-justify-center"> 389 + <a class="item" tabindex="0" :class="{active: reposFilter === 'all'}" @click="changeReposFilter('all')"> 390 + {{ textAll }} 391 + <div v-show="reposFilter === 'all'" class="ui circular mini grey label">{{ repoTypeCount }}</div> 392 + </a> 393 + <a class="item" tabindex="0" :class="{active: reposFilter === 'sources'}" @click="changeReposFilter('sources')"> 394 + {{ textSources }} 395 + <div v-show="reposFilter === 'sources'" class="ui circular mini grey label">{{ repoTypeCount }}</div> 396 + </a> 397 + <a class="item" tabindex="0" :class="{active: reposFilter === 'forks'}" @click="changeReposFilter('forks')"> 398 + {{ textForks }} 399 + <div v-show="reposFilter === 'forks'" class="ui circular mini grey label">{{ repoTypeCount }}</div> 400 + </a> 401 + <a class="item" tabindex="0" :class="{active: reposFilter === 'mirrors'}" @click="changeReposFilter('mirrors')" v-if="isMirrorsEnabled"> 402 + {{ textMirrors }} 403 + <div v-show="reposFilter === 'mirrors'" class="ui circular mini grey label">{{ repoTypeCount }}</div> 404 + </a> 405 + <a class="item" tabindex="0" :class="{active: reposFilter === 'collaborative'}" @click="changeReposFilter('collaborative')"> 406 + {{ textCollaborative }} 407 + <div v-show="reposFilter === 'collaborative'" class="ui circular mini grey label">{{ repoTypeCount }}</div> 408 + </a> 409 + </div> 410 + </overflow-menu> 409 411 </div> 410 412 <div v-if="repos.length" class="ui attached table segment gt-rounded-bottom"> 411 413 <ul class="repo-owner-name-list"> ··· 499 501 500 502 ul li:not(:last-child) { 501 503 border-bottom: 1px solid var(--color-secondary); 504 + } 505 + 506 + .repos-search { 507 + padding-bottom: 0 !important; 508 + } 509 + 510 + .repos-filter { 511 + padding-top: 0 !important; 512 + margin-top: 0 !important; 513 + border-bottom-width: 0 !important; 514 + margin-bottom: 2px !important; 515 + } 516 + 517 + .repos-filter .item { 518 + padding-left: 6px !important; 519 + padding-right: 6px !important; 502 520 } 503 521 504 522 .repo-list-link {
+1 -1
web_src/js/components/DiffCommitSelector.vue
··· 248 248 {{ commit.committer_or_author_name }} 249 249 <span class="text right"> 250 250 <!-- TODO: make this respect the PreferredTimestampTense setting --> 251 - <relative-time class="time-since" prefix="" :datetime="commit.time" data-tooltip-content data-tooltip-interactive="true">{{ commit.time }}</relative-time> 251 + <relative-time prefix="" :datetime="commit.time" data-tooltip-content data-tooltip-interactive="true">{{ commit.time }}</relative-time> 252 252 </span> 253 253 </div> 254 254 </div>
+9 -5
web_src/js/components/RepoActionView.vue
··· 5 5 import {toggleElem} from '../utils/dom.js'; 6 6 import {formatDatetime} from '../utils/time.js'; 7 7 import {renderAnsi} from '../render/ansi.js'; 8 - import {POST, DELETE} from '../modules/fetch.js'; 8 + import {GET, POST, DELETE} from '../modules/fetch.js'; 9 9 10 10 const sfc = { 11 11 name: 'RepoActionView', ··· 199 199 }, 200 200 201 201 async fetchArtifacts() { 202 - const resp = await POST(`${this.actionsURL}/runs/${this.runIndex}/artifacts`); 202 + const resp = await GET(`${this.actionsURL}/runs/${this.runIndex}/artifacts`); 203 203 return await resp.json(); 204 204 }, 205 205 ··· 466 466 </div> 467 467 </div> 468 468 </div> 469 - <div class="job-step-container" ref="steps"> 469 + <div class="job-step-container" ref="steps" v-if="currentJob.steps.length"> 470 470 <div class="job-step-section" v-for="(jobStep, i) in currentJob.steps" :key="i"> 471 471 <div class="job-step-summary" @click.stop="toggleStepLogs(i)" :class="currentJobStepsStates[i].expanded ? 'selected' : ''"> 472 472 <!-- If the job is done and the job step log is loaded for the first time, show the loading icon ··· 533 533 width: 30%; 534 534 max-width: 400px; 535 535 position: sticky; 536 - top: 0; 536 + top: 12px; 537 537 max-height: 100vh; 538 538 overflow-y: auto; 539 539 } ··· 695 695 background-color: var(--color-console-bg); 696 696 position: sticky; 697 697 top: 0; 698 - border-radius: var(--border-radius) var(--border-radius) 0 0; 698 + border-radius: var(--border-radius); 699 699 height: 60px; 700 700 z-index: 1; 701 + } 702 + 703 + .job-info-header:has(+ .job-step-container) { 704 + border-radius: var(--border-radius) var(--border-radius) 0 0; 701 705 } 702 706 703 707 .job-info-header .job-info-header-title {
+1 -1
web_src/js/components/RepoBranchTagSelector.vue
··· 123 123 return -1; 124 124 }, 125 125 scrollToActive() { 126 - let el = this.$refs[`listItem${this.active}`]; 126 + let el = this.$refs[`listItem${this.active}`]; // eslint-disable-line no-jquery/variable-pattern 127 127 if (!el || !el.length) return; 128 128 if (Array.isArray(el)) { 129 129 el = el[0];
+1 -1
web_src/js/features/admin/common.js
··· 49 49 } 50 50 51 51 function onUsePagedSearchChange() { 52 - if ($('#use_paged_search').prop('checked')) { 52 + if (document.getElementById('use_paged_search').checked) { 53 53 showElem('.search-page-size'); 54 54 $('.search-page-size').find('input').attr('required', 'required'); 55 55 } else {
+2 -2
web_src/js/features/admin/users.js
··· 12 12 if (searchForm.StatusFilterMap) { 13 13 for (const [k, v] of Object.entries(searchForm.StatusFilterMap)) { 14 14 if (!v) continue; 15 - $form.find(`input[name="status_filter[${k}]"][value=${v}]`).prop('checked', true); 15 + $form.find(`input[name="status_filter[${k}]"][value=${v}]`).checked = true; 16 16 } 17 17 } 18 18 ··· 25 25 $form.find(`input[type=radio]`).each((_, e) => { 26 26 const $e = $(e); 27 27 if ($e.attr('name').startsWith('status_filter[')) { 28 - $e.prop('checked', false); 28 + $e.checked = false; 29 29 } 30 30 }); 31 31 $form.trigger('submit');
+26 -19
web_src/js/features/citation.js
··· 40 40 $citationCopyApa.toggleClass('primary', !isBibtex); 41 41 }; 42 42 43 - try { 44 - await initInputCitationValue($citationCopyApa, $citationCopyBibtex); 45 - } catch (e) { 46 - console.error(`initCitationFileCopyContent error: ${e}`, e); 47 - return; 48 - } 49 - updateUi(); 43 + $('#cite-repo-button').on('click', async (e) => { 44 + const dropdownBtn = e.target.closest('.ui.dropdown.button'); 45 + dropdownBtn.classList.add('is-loading'); 46 + 47 + try { 48 + try { 49 + await initInputCitationValue($citationCopyApa, $citationCopyBibtex); 50 + } catch (e) { 51 + console.error(`initCitationFileCopyContent error: ${e}`, e); 52 + return; 53 + } 54 + updateUi(); 50 55 51 - $citationCopyApa.on('click', () => { 52 - localStorage.setItem('citation-copy-format', 'apa'); 53 - updateUi(); 54 - }); 55 - $citationCopyBibtex.on('click', () => { 56 - localStorage.setItem('citation-copy-format', 'bibtex'); 57 - updateUi(); 58 - }); 56 + $citationCopyApa.on('click', () => { 57 + localStorage.setItem('citation-copy-format', 'apa'); 58 + updateUi(); 59 + }); 60 + $citationCopyBibtex.on('click', () => { 61 + localStorage.setItem('citation-copy-format', 'bibtex'); 62 + updateUi(); 63 + }); 59 64 60 - $inputContent.on('click', () => { 61 - $inputContent.trigger('select'); 62 - }); 65 + $inputContent.on('click', () => { 66 + $inputContent.trigger('select'); 67 + }); 68 + } finally { 69 + dropdownBtn.classList.remove('is-loading'); 70 + } 63 71 64 - $('#cite-repo-button').on('click', () => { 65 72 $('#cite-repo-modal').modal('show'); 66 73 }); 67 74 }
+2 -4
web_src/js/features/clipboard.js
··· 15 15 16 16 e.preventDefault(); 17 17 18 - let text; 19 - if (target.hasAttribute('data-clipboard-text')) { 20 - text = target.getAttribute('data-clipboard-text'); 21 - } else { 18 + let text = target.getAttribute('data-clipboard-text'); 19 + if (!text) { 22 20 text = document.querySelector(target.getAttribute('data-clipboard-target'))?.value; 23 21 } 24 22
+8 -8
web_src/js/features/common-global.js
··· 231 231 init() { 232 232 this.on('success', (file, data) => { 233 233 file.uuid = data.uuid; 234 - const input = $(`<input id="${data.uuid}" name="files" type="hidden">`).val(data.uuid); 235 - $dropzone.find('.files').append(input); 234 + const $input = $(`<input id="${data.uuid}" name="files" type="hidden">`).val(data.uuid); 235 + $dropzone.find('.files').append($input); 236 236 // Create a "Copy Link" element, to conveniently copy the image 237 237 // or file link as Markdown to the clipboard 238 238 const copyLinkElement = document.createElement('div'); ··· 305 305 filter += `#${$this.attr('data-modal-id')}`; 306 306 } 307 307 308 - const dialog = $(`.delete.modal${filter}`); 309 - dialog.find('.name').text($this.data('name')); 308 + const $dialog = $(`.delete.modal${filter}`); 309 + $dialog.find('.name').text($this.data('name')); 310 310 for (const [key, value] of Object.entries(dataArray)) { 311 311 if (key && key.startsWith('data')) { 312 - dialog.find(`.${key}`).text(value); 312 + $dialog.find(`.${key}`).text(value); 313 313 } 314 314 } 315 315 316 - dialog.modal({ 316 + $dialog.modal({ 317 317 closable: false, 318 318 onApprove: async () => { 319 319 if ($this.data('type') === 'form') { ··· 380 380 $attrTarget.text(attrib.value); // FIXME: it should be more strict here, only handle div/span/p 381 381 } 382 382 } 383 - const colorPickers = $modal.find('.color-picker'); 384 - if (colorPickers.length > 0) { 383 + const $colorPickers = $modal.find('.color-picker'); 384 + if ($colorPickers.length > 0) { 385 385 initCompColorPicker(); // FIXME: this might cause duplicate init 386 386 } 387 387 $modal.modal('setting', {
+21 -21
web_src/js/features/comp/LabelEdit.js
··· 6 6 } 7 7 8 8 function updateExclusiveLabelEdit(form) { 9 - const nameInput = $(`${form} .label-name-input`); 10 - const exclusiveField = $(`${form} .label-exclusive-input-field`); 11 - const exclusiveCheckbox = $(`${form} .label-exclusive-input`); 12 - const exclusiveWarning = $(`${form} .label-exclusive-warning`); 9 + const $nameInput = $(`${form} .label-name-input`); 10 + const $exclusiveField = $(`${form} .label-exclusive-input-field`); 11 + const $exclusiveCheckbox = $(`${form} .label-exclusive-input`); 12 + const $exclusiveWarning = $(`${form} .label-exclusive-warning`); 13 13 14 - if (isExclusiveScopeName(nameInput.val())) { 15 - exclusiveField.removeClass('muted'); 16 - exclusiveField.removeAttr('aria-disabled'); 17 - if (exclusiveCheckbox.prop('checked') && exclusiveCheckbox.data('exclusive-warn')) { 18 - exclusiveWarning.removeClass('gt-hidden'); 14 + if (isExclusiveScopeName($nameInput.val())) { 15 + $exclusiveField.removeClass('muted'); 16 + $exclusiveField.removeAttr('aria-disabled'); 17 + if ($exclusiveCheckbox[0].checked && $exclusiveCheckbox.data('exclusive-warn')) { 18 + $exclusiveWarning.removeClass('gt-hidden'); 19 19 } else { 20 - exclusiveWarning.addClass('gt-hidden'); 20 + $exclusiveWarning.addClass('gt-hidden'); 21 21 } 22 22 } else { 23 - exclusiveField.addClass('muted'); 24 - exclusiveField.attr('aria-disabled', 'true'); 25 - exclusiveWarning.addClass('gt-hidden'); 23 + $exclusiveField.addClass('muted'); 24 + $exclusiveField.attr('aria-disabled', 'true'); 25 + $exclusiveWarning.addClass('gt-hidden'); 26 26 } 27 27 } 28 28 ··· 46 46 $('.edit-label .color-picker').minicolors('value', $(this).data('color')); 47 47 $('#label-modal-id').val($(this).data('id')); 48 48 49 - const nameInput = $('.edit-label .label-name-input'); 50 - nameInput.val($(this).data('title')); 49 + const $nameInput = $('.edit-label .label-name-input'); 50 + $nameInput.val($(this).data('title')); 51 51 52 - const isArchivedCheckbox = $('.edit-label .label-is-archived-input'); 53 - isArchivedCheckbox.prop('checked', this.hasAttribute('data-is-archived')); 52 + const $isArchivedCheckbox = $('.edit-label .label-is-archived-input'); 53 + $isArchivedCheckbox[0].checked = this.hasAttribute('data-is-archived'); 54 54 55 - const exclusiveCheckbox = $('.edit-label .label-exclusive-input'); 56 - exclusiveCheckbox.prop('checked', this.hasAttribute('data-exclusive')); 55 + const $exclusiveCheckbox = $('.edit-label .label-exclusive-input'); 56 + $exclusiveCheckbox[0].checked = this.hasAttribute('data-exclusive'); 57 57 // Warn when label was previously not exclusive and used in issues 58 - exclusiveCheckbox.data('exclusive-warn', 58 + $exclusiveCheckbox.data('exclusive-warn', 59 59 $(this).data('num-issues') > 0 && 60 - (!this.hasAttribute('data-exclusive') || !isExclusiveScopeName(nameInput.val()))); 60 + (!this.hasAttribute('data-exclusive') || !isExclusiveScopeName($nameInput.val()))); 61 61 updateExclusiveLabelEdit('.edit-label'); 62 62 63 63 $('.edit-label .label-desc-input').val($(this).data('description'));
+11 -11
web_src/js/features/comp/ReactionSelector.js
··· 17 17 18 18 const data = await res.json(); 19 19 if (data && (data.html || data.empty)) { 20 - const content = $(this).closest('.content'); 21 - let react = content.find('.segment.reactions'); 22 - if ((!data.empty || data.html === '') && react.length > 0) { 23 - react.remove(); 20 + const $content = $(this).closest('.content'); 21 + let $react = $content.find('.segment.reactions'); 22 + if ((!data.empty || data.html === '') && $react.length > 0) { 23 + $react.remove(); 24 24 } 25 25 if (!data.empty) { 26 - const attachments = content.find('.segment.bottom:first'); 27 - react = $(data.html); 28 - if (attachments.length > 0) { 29 - react.insertBefore(attachments); 26 + const $attachments = $content.find('.segment.bottom:first'); 27 + $react = $(data.html); 28 + if ($attachments.length > 0) { 29 + $react.insertBefore($attachments); 30 30 } else { 31 - react.appendTo(content); 31 + $react.appendTo($content); 32 32 } 33 - react.find('.dropdown').dropdown(); 34 - initCompReactionSelector(react); 33 + $react.find('.dropdown').dropdown(); 34 + initCompReactionSelector($react); 35 35 } 36 36 } 37 37 });
+42 -68
web_src/js/features/notification.js
··· 1 1 import $ from 'jquery'; 2 + import {GET} from '../modules/fetch.js'; 2 3 3 - const {appSubUrl, csrfToken, notificationSettings, assetVersionEncoded} = window.config; 4 + const {appSubUrl, notificationSettings, assetVersionEncoded} = window.config; 4 5 let notificationSequenceNumber = 0; 5 6 6 7 export function initNotificationsTable() { ··· 27 28 e.target.closest('.notifications-item').setAttribute('data-remove', 'true'); 28 29 }); 29 30 } 30 - 31 - $('#notification_table .button').on('click', function () { 32 - (async () => { 33 - const data = await updateNotification( 34 - $(this).data('url'), 35 - $(this).data('status'), 36 - $(this).data('page'), 37 - $(this).data('q'), 38 - $(this).data('notification-id'), 39 - ); 40 - 41 - if ($(data).data('sequence-number') === notificationSequenceNumber) { 42 - $('#notification_div').replaceWith(data); 43 - initNotificationsTable(); 44 - } 45 - await updateNotificationCount(); 46 - })(); 47 - return false; 48 - }); 49 31 } 50 32 51 33 async function receiveUpdateCount(event) { ··· 63 45 } 64 46 65 47 export function initNotificationCount() { 66 - const notificationCount = $('.notification_count'); 48 + const $notificationCount = $('.notification_count'); 67 49 68 - if (!notificationCount.length) { 50 + if (!$notificationCount.length) { 69 51 return; 70 52 } 71 53 ··· 73 55 const startPeriodicPoller = (timeout, lastCount) => { 74 56 if (timeout <= 0 || !Number.isFinite(timeout)) return; 75 57 usingPeriodicPoller = true; 76 - lastCount = lastCount ?? notificationCount.text(); 58 + lastCount = lastCount ?? $notificationCount.text(); 77 59 setTimeout(async () => { 78 60 await updateNotificationCountWithCallback(startPeriodicPoller, timeout, lastCount); 79 61 }, timeout); ··· 161 143 } 162 144 163 145 async function updateNotificationTable() { 164 - const notificationDiv = $('#notification_div'); 165 - if (notificationDiv.length > 0) { 166 - const data = await $.ajax({ 167 - type: 'GET', 168 - url: `${appSubUrl}/notifications${window.location.search}`, 169 - data: { 170 - 'div-only': true, 171 - 'sequence-number': ++notificationSequenceNumber, 146 + const notificationDiv = document.getElementById('notification_div'); 147 + if (notificationDiv) { 148 + try { 149 + const params = new URLSearchParams(window.location.search); 150 + params.set('div-only', true); 151 + params.set('sequence-number', ++notificationSequenceNumber); 152 + const url = `${appSubUrl}/notifications?${params.toString()}`; 153 + const response = await GET(url); 154 + 155 + if (!response.ok) { 156 + throw new Error('Failed to fetch notification table'); 172 157 } 173 - }); 174 - if ($(data).data('sequence-number') === notificationSequenceNumber) { 175 - notificationDiv.replaceWith(data); 176 - initNotificationsTable(); 158 + 159 + const data = await response.text(); 160 + if ($(data).data('sequence-number') === notificationSequenceNumber) { 161 + notificationDiv.outerHTML = data; 162 + initNotificationsTable(); 163 + } 164 + } catch (error) { 165 + console.error(error); 177 166 } 178 167 } 179 168 } 180 169 181 170 async function updateNotificationCount() { 182 - const data = await $.ajax({ 183 - type: 'GET', 184 - url: `${appSubUrl}/notifications/new`, 185 - headers: { 186 - 'X-Csrf-Token': csrfToken, 187 - }, 188 - }); 171 + try { 172 + const response = await GET(`${appSubUrl}/notifications/new`); 189 173 190 - const notificationCount = $('.notification_count'); 191 - if (data.new === 0) { 192 - notificationCount.addClass('gt-hidden'); 193 - } else { 194 - notificationCount.removeClass('gt-hidden'); 195 - } 174 + if (!response.ok) { 175 + throw new Error('Failed to fetch notification count'); 176 + } 196 177 197 - notificationCount.text(`${data.new}`); 178 + const data = await response.json(); 179 + 180 + const $notificationCount = $('.notification_count'); 181 + if (data.new === 0) { 182 + $notificationCount.addClass('gt-hidden'); 183 + } else { 184 + $notificationCount.removeClass('gt-hidden'); 185 + } 198 186 199 - return `${data.new}`; 200 - } 187 + $notificationCount.text(`${data.new}`); 201 188 202 - async function updateNotification(url, status, page, q, notificationID) { 203 - if (status !== 'pinned') { 204 - $(`#notification_${notificationID}`).remove(); 189 + return `${data.new}`; 190 + } catch (error) { 191 + console.error(error); 192 + return '0'; 205 193 } 206 - 207 - return $.ajax({ 208 - type: 'POST', 209 - url, 210 - data: { 211 - _csrf: csrfToken, 212 - notification_id: notificationID, 213 - status, 214 - page, 215 - q, 216 - noredirect: true, 217 - 'sequence-number': ++notificationSequenceNumber, 218 - }, 219 - }); 220 194 }
+40 -28
web_src/js/features/repo-diff.js
··· 8 8 import {initImageDiff} from './imagediff.js'; 9 9 import {showErrorToast} from '../modules/toast.js'; 10 10 import {submitEventSubmitter} from '../utils/dom.js'; 11 + import {POST, GET} from '../modules/fetch.js'; 11 12 12 - const {csrfToken, pageData, i18n} = window.config; 13 + const {pageData, i18n} = window.config; 13 14 14 15 function initRepoDiffReviewButton() { 15 16 const $reviewBox = $('#review-box'); ··· 65 66 if (isSubmittedByButton && submitter.name) { 66 67 formData.append(submitter.name, submitter.value); 67 68 } 68 - const formDataString = String(new URLSearchParams(formData)); 69 - const $newConversationHolder = $(await $.post($form.attr('action'), formDataString)); 69 + 70 + const response = await POST($form.attr('action'), {data: formData}); 71 + const $newConversationHolder = $(await response.text()); 70 72 const {path, side, idx} = $newConversationHolder.data(); 71 73 72 74 $form.closest('.conversation-holder').replaceWith($newConversationHolder); ··· 92 94 const action = $(this).data('action'); 93 95 const url = $(this).data('update-url'); 94 96 95 - const data = await $.post(url, {_csrf: csrfToken, origin, action, comment_id}); 97 + try { 98 + const response = await POST(url, {data: new URLSearchParams({origin, action, comment_id})}); 99 + const data = await response.text(); 96 100 97 - if ($(this).closest('.conversation-holder').length) { 98 - const conversation = $(data); 99 - $(this).closest('.conversation-holder').replaceWith(conversation); 100 - conversation.find('.dropdown').dropdown(); 101 - initCompReactionSelector(conversation); 102 - } else { 103 - window.location.reload(); 101 + if ($(this).closest('.conversation-holder').length) { 102 + const $conversation = $(data); 103 + $(this).closest('.conversation-holder').replaceWith($conversation); 104 + $conversation.find('.dropdown').dropdown(); 105 + initCompReactionSelector($conversation); 106 + } else { 107 + window.location.reload(); 108 + } 109 + } catch (error) { 110 + console.error('Error:', error); 104 111 } 105 112 }); 106 113 } ··· 135 142 initImageDiff(); 136 143 } 137 144 138 - export function loadMoreFiles(url) { 145 + export async function loadMoreFiles(url) { 139 146 const $target = $('a#diff-show-more-files'); 140 147 if ($target.hasClass('disabled') || pageData.diffFileInfo.isLoadingNewData) { 141 148 return; ··· 143 150 144 151 pageData.diffFileInfo.isLoadingNewData = true; 145 152 $target.addClass('disabled'); 146 - $.ajax({ 147 - type: 'GET', 148 - url, 149 - }).done((resp) => { 153 + 154 + try { 155 + const response = await GET(url); 156 + const resp = await response.text(); 150 157 const $resp = $(resp); 151 158 // the response is a full HTML page, we need to extract the relevant contents: 152 159 // 1. append the newly loaded file list items to the existing list ··· 155 162 $('body').append($resp.find('script#diff-data-script')); 156 163 157 164 onShowMoreFiles(); 158 - }).always(() => { 165 + } catch (error) { 166 + console.error('Error:', error); 167 + showErrorToast('An error occurred while loading more files.'); 168 + } finally { 159 169 $target.removeClass('disabled'); 160 170 pageData.diffFileInfo.isLoadingNewData = false; 161 - }); 171 + } 162 172 } 163 173 164 174 function initRepoDiffShowMore() { ··· 170 180 loadMoreFiles(linkLoadMore); 171 181 }); 172 182 173 - $(document).on('click', 'a.diff-load-button', (e) => { 183 + $(document).on('click', 'a.diff-load-button', async (e) => { 174 184 e.preventDefault(); 175 185 const $target = $(e.target); 176 186 ··· 181 191 $target.addClass('disabled'); 182 192 183 193 const url = $target.data('href'); 184 - $.ajax({ 185 - type: 'GET', 186 - url, 187 - }).done((resp) => { 194 + 195 + try { 196 + const response = await GET(url); 197 + const resp = await response.text(); 198 + 188 199 if (!resp) { 189 - $target.removeClass('disabled'); 190 200 return; 191 201 } 192 202 $target.parent().replaceWith($(resp).find('#diff-file-boxes .diff-file-body .file-body').children()); 193 203 onShowMoreFiles(); 194 - }).fail(() => { 204 + } catch (error) { 205 + console.error('Error:', error); 206 + } finally { 195 207 $target.removeClass('disabled'); 196 - }); 208 + } 197 209 }); 198 210 } 199 211 200 212 export function initRepoDiffView() { 201 213 initRepoDiffConversationForm(); 202 - const diffFileList = $('#diff-file-list'); 203 - if (diffFileList.length === 0) return; 214 + const $diffFileList = $('#diff-file-list'); 215 + if ($diffFileList.length === 0) return; 204 216 initDiffFileTree(); 205 217 initDiffCommitSelect(); 206 218 initRepoDiffShowMore();
+17 -17
web_src/js/features/repo-editor.js
··· 15 15 const $this = $(this); 16 16 let context = `${$this.data('context')}/`; 17 17 const mode = $this.data('markup-mode') || 'comment'; 18 - const treePathEl = $form.find('input#tree_path'); 19 - if (treePathEl.length > 0) { 20 - context += treePathEl.val(); 18 + const $treePathEl = $form.find('input#tree_path'); 19 + if ($treePathEl.length > 0) { 20 + context += $treePathEl.val(); 21 21 } 22 22 context = context.substring(0, context.lastIndexOf('/')); 23 23 ··· 25 25 formData.append('mode', mode); 26 26 formData.append('context', context); 27 27 formData.append('text', $form.find(`.tab[data-tab="${$tabMenu.data('write')}"] textarea`).val()); 28 - formData.append('file_path', treePathEl.val()); 28 + formData.append('file_path', $treePathEl.val()); 29 29 try { 30 30 const response = await POST($this.data('url'), {data: formData}); 31 31 const data = await response.text(); ··· 67 67 $('.js-quick-pull-choice-option').on('change', function () { 68 68 if ($(this).val() === 'commit-to-new-branch') { 69 69 showElem($('.quick-pull-branch-name')); 70 - $('.quick-pull-branch-name input').prop('required', true); 70 + document.querySelector('.quick-pull-branch-name input').required = true; 71 71 } else { 72 72 hideElem($('.quick-pull-branch-name')); 73 - $('.quick-pull-branch-name input').prop('required', false); 73 + document.querySelector('.quick-pull-branch-name input').required = false; 74 74 } 75 75 $('#commit-button').text($(this).attr('button_text')); 76 76 }); ··· 78 78 const joinTreePath = ($fileNameEl) => { 79 79 const parts = []; 80 80 $('.breadcrumb span.section').each(function () { 81 - const element = $(this); 82 - if (element.find('a').length) { 83 - parts.push(element.find('a').text()); 81 + const $element = $(this); 82 + if ($element.find('a').length) { 83 + parts.push($element.find('a').text()); 84 84 } else { 85 - parts.push(element.text()); 85 + parts.push($element.text()); 86 86 } 87 87 }); 88 88 if ($fileNameEl.val()) parts.push($fileNameEl.val()); ··· 135 135 136 136 // Using events from https://github.com/codedance/jquery.AreYouSure#advanced-usage 137 137 // to enable or disable the commit button 138 - const $commitButton = $('#commit-button'); 138 + const commitButton = document.getElementById('commit-button'); 139 139 const $editForm = $('.ui.edit.form'); 140 140 const dirtyFileClass = 'dirty-file'; 141 141 142 142 // Disabling the button at the start 143 143 if ($('input[name="page_has_posted"]').val() !== 'true') { 144 - $commitButton.prop('disabled', true); 144 + commitButton.disabled = true; 145 145 } 146 146 147 147 // Registering a custom listener for the file path and the file content ··· 151 151 fieldSelector: ':input:not(.commit-form-wrapper :input)', 152 152 change() { 153 153 const dirty = $(this).hasClass(dirtyFileClass); 154 - $commitButton.prop('disabled', !dirty); 154 + commitButton.disabled = !dirty; 155 155 }, 156 156 }); 157 157 ··· 163 163 editor.setValue(value); 164 164 } 165 165 166 - $commitButton.on('click', (event) => { 166 + commitButton?.addEventListener('click', (e) => { 167 167 // A modal which asks if an empty file should be committed 168 168 if ($editArea.val().length === 0) { 169 169 $('#edit-empty-content-modal').modal({ ··· 171 171 $('.edit.form').trigger('submit'); 172 172 }, 173 173 }).modal('show'); 174 - event.preventDefault(); 174 + e.preventDefault(); 175 175 } 176 176 }); 177 177 })(); ··· 181 181 $panelPreviewer.html(data); 182 182 initMarkupContent(); 183 183 184 - const refIssues = $panelPreviewer.find('p .ref-issue'); 185 - attachRefIssueContextPopup(refIssues); 184 + const $refIssues = $panelPreviewer.find('p .ref-issue'); 185 + attachRefIssueContextPopup($refIssues); 186 186 }
+4 -4
web_src/js/features/repo-graph.js
··· 63 63 (async () => { 64 64 const response = await GET(String(ajaxUrl)); 65 65 const html = await response.text(); 66 - const div = $(html); 67 - $('#pagination').html(div.find('#pagination').html()); 68 - $('#rel-container').html(div.find('#rel-container').html()); 69 - $('#rev-container').html(div.find('#rev-container').html()); 66 + const $div = $(html); 67 + $('#pagination').html($div.find('#pagination').html()); 68 + $('#rel-container').html($div.find('#rel-container').html()); 69 + $('#rev-container').html($div.find('#rev-container').html()); 70 70 $('#loading-indicator').addClass('gt-hidden'); 71 71 $('#rel-container').removeClass('gt-hidden'); 72 72 $('#rev-container').removeClass('gt-hidden');
+36 -36
web_src/js/features/repo-home.js
··· 6 6 const {appSubUrl} = window.config; 7 7 8 8 export function initRepoTopicBar() { 9 - const mgrBtn = $('#manage_topic'); 10 - if (!mgrBtn.length) return; 11 - const editDiv = $('#topic_edit'); 12 - const viewDiv = $('#repo-topics'); 13 - const saveBtn = $('#save_topic'); 14 - const topicDropdown = $('#topic_edit .dropdown'); 15 - const topicForm = editDiv; // the old logic, editDiv is topicForm 16 - const topicDropdownSearch = topicDropdown.find('input.search'); 9 + const $mgrBtn = $('#manage_topic'); 10 + if (!$mgrBtn.length) return; 11 + const $editDiv = $('#topic_edit'); 12 + const $viewDiv = $('#repo-topics'); 13 + const $saveBtn = $('#save_topic'); 14 + const $topicDropdown = $('#topic_edit .dropdown'); 15 + const $topicForm = $editDiv; // the old logic, $editDiv is topicForm 16 + const $topicDropdownSearch = $topicDropdown.find('input.search'); 17 17 const topicPrompts = { 18 - countPrompt: topicDropdown.attr('data-text-count-prompt'), 19 - formatPrompt: topicDropdown.attr('data-text-format-prompt'), 18 + countPrompt: $topicDropdown.attr('data-text-count-prompt'), 19 + formatPrompt: $topicDropdown.attr('data-text-format-prompt'), 20 20 }; 21 21 22 - mgrBtn.on('click', () => { 23 - hideElem(viewDiv); 24 - showElem(editDiv); 25 - topicDropdownSearch.focus(); 22 + $mgrBtn.on('click', () => { 23 + hideElem($viewDiv); 24 + showElem($editDiv); 25 + $topicDropdownSearch.trigger('focus'); 26 26 }); 27 27 28 28 $('#cancel_topic_edit').on('click', () => { 29 - hideElem(editDiv); 30 - showElem(viewDiv); 31 - mgrBtn.focus(); 29 + hideElem($editDiv); 30 + showElem($viewDiv); 31 + $mgrBtn.trigger('focus'); 32 32 }); 33 33 34 - saveBtn.on('click', async () => { 34 + $saveBtn.on('click', async () => { 35 35 const topics = $('input[name=topics]').val(); 36 36 37 37 const data = new FormData(); 38 38 data.append('topics', topics); 39 39 40 - const response = await POST(saveBtn.attr('data-link'), {data}); 40 + const response = await POST($saveBtn.attr('data-link'), {data}); 41 41 42 42 if (response.ok) { 43 43 const responseData = await response.json(); 44 44 if (responseData.status === 'ok') { 45 - viewDiv.children('.topic').remove(); 45 + $viewDiv.children('.topic').remove(); 46 46 if (topics.length) { 47 47 const topicArray = topics.split(','); 48 48 topicArray.sort(); 49 49 for (const topic of topicArray) { 50 - const link = $('<a class="ui repo-topic large label topic gt-m-0"></a>'); 51 - link.attr('href', `${appSubUrl}/explore/repos?q=${encodeURIComponent(topic)}&topic=1`); 52 - link.text(topic); 53 - link.insertBefore(mgrBtn); // insert all new topics before manage button 50 + const $link = $('<a class="ui repo-topic large label topic gt-m-0"></a>'); 51 + $link.attr('href', `${appSubUrl}/explore/repos?q=${encodeURIComponent(topic)}&topic=1`); 52 + $link.text(topic); 53 + $link.insertBefore($mgrBtn); // insert all new topics before manage button 54 54 } 55 55 } 56 - hideElem(editDiv); 57 - showElem(viewDiv); 56 + hideElem($editDiv); 57 + showElem($viewDiv); 58 58 } 59 59 } else if (response.status === 422) { 60 60 const responseData = await response.json(); ··· 62 62 topicPrompts.formatPrompt = responseData.message; 63 63 64 64 const {invalidTopics} = responseData; 65 - const topicLabels = topicDropdown.children('a.ui.label'); 65 + const $topicLabels = $topicDropdown.children('a.ui.label'); 66 66 for (const [index, value] of topics.split(',').entries()) { 67 67 if (invalidTopics.includes(value)) { 68 - topicLabels.eq(index).removeClass('green').addClass('red'); 68 + $topicLabels.eq(index).removeClass('green').addClass('red'); 69 69 } 70 70 } 71 71 } else { ··· 74 74 } 75 75 76 76 // Always validate the form 77 - topicForm.form('validate form'); 77 + $topicForm.form('validate form'); 78 78 }); 79 79 80 - topicDropdown.dropdown({ 80 + $topicDropdown.dropdown({ 81 81 allowAdditions: true, 82 82 forceSelection: false, 83 83 fullTextSearch: 'exact', ··· 100 100 const query = stripTags(this.urlData.query.trim()); 101 101 let found_query = false; 102 102 const current_topics = []; 103 - topicDropdown.find('a.label.visible').each((_, el) => { 103 + $topicDropdown.find('a.label.visible').each((_, el) => { 104 104 current_topics.push(el.getAttribute('data-value')); 105 105 }); 106 106 ··· 150 150 }); 151 151 152 152 $.fn.form.settings.rules.validateTopic = function (_values, regExp) { 153 - const topics = topicDropdown.children('a.ui.label'); 154 - const status = topics.length === 0 || topics.last().attr('data-value').match(regExp); 153 + const $topics = $topicDropdown.children('a.ui.label'); 154 + const status = $topics.length === 0 || $topics.last().attr('data-value').match(regExp); 155 155 if (!status) { 156 - topics.last().removeClass('green').addClass('red'); 156 + $topics.last().removeClass('green').addClass('red'); 157 157 } 158 - return status && topicDropdown.children('a.ui.label.red').length === 0; 158 + return status && $topicDropdown.children('a.ui.label.red').length === 0; 159 159 }; 160 160 161 - topicForm.form({ 161 + $topicForm.form({ 162 162 on: 'change', 163 163 inline: true, 164 164 fields: {
+36 -22
web_src/js/features/repo-issue-content.js
··· 1 1 import $ from 'jquery'; 2 2 import {svg} from '../svg.js'; 3 3 import {showErrorToast} from '../modules/toast.js'; 4 + import {GET, POST} from '../modules/fetch.js'; 4 5 5 - const {appSubUrl, csrfToken} = window.config; 6 + const {appSubUrl} = window.config; 6 7 let i18nTextEdited; 7 8 let i18nTextOptions; 8 9 let i18nTextDeleteFromHistory; ··· 31 32 $dialog.find('.dialog-header-options').dropdown({ 32 33 showOnFocus: false, 33 34 allowReselection: true, 34 - onChange(_value, _text, $item) { 35 + async onChange(_value, _text, $item) { 35 36 const optionItem = $item.data('option-item'); 36 37 if (optionItem === 'delete') { 37 38 if (window.confirm(i18nTextDeleteFromHistoryConfirm)) { 38 - $.post(`${issueBaseUrl}/content-history/soft-delete?comment_id=${commentId}&history_id=${historyId}`, { 39 - _csrf: csrfToken, 40 - }).done((resp) => { 39 + try { 40 + const params = new URLSearchParams(); 41 + params.append('comment_id', commentId); 42 + params.append('history_id', historyId); 43 + 44 + const response = await POST(`${issueBaseUrl}/content-history/soft-delete?${params.toString()}`); 45 + const resp = await response.json(); 46 + 41 47 if (resp.ok) { 42 48 $dialog.modal('hide'); 43 49 } else { 44 50 showErrorToast(resp.message); 45 51 } 46 - }); 52 + } catch (error) { 53 + console.error('Error:', error); 54 + showErrorToast('An error occurred while deleting the history.'); 55 + } 47 56 } 48 57 } else { // required by eslint 49 58 showErrorToast(`unknown option item: ${optionItem}`); ··· 54 63 } 55 64 }); 56 65 $dialog.modal({ 57 - onShow() { 58 - $.ajax({ 59 - url: `${issueBaseUrl}/content-history/detail?comment_id=${commentId}&history_id=${historyId}`, 60 - data: { 61 - _csrf: csrfToken, 62 - }, 63 - }).done((resp) => { 66 + async onShow() { 67 + try { 68 + const params = new URLSearchParams(); 69 + params.append('comment_id', commentId); 70 + params.append('history_id', historyId); 71 + 72 + const url = `${issueBaseUrl}/content-history/detail?${params.toString()}`; 73 + const response = await GET(url); 74 + const resp = await response.json(); 75 + 64 76 $dialog.find('.comment-diff-data').removeClass('is-loading').html(resp.diffHtml); 65 77 // there is only one option "item[data-option-item=delete]", so the dropdown can be entirely shown/hidden. 66 78 if (resp.canSoftDelete) { 67 79 $dialog.find('.dialog-header-options').removeClass('gt-hidden'); 68 80 } 69 - }); 81 + } catch (error) { 82 + console.error('Error:', error); 83 + } 70 84 }, 71 85 onHidden() { 72 86 $dialog.remove(); ··· 103 117 }); 104 118 } 105 119 106 - export function initRepoIssueContentHistory() { 120 + export async function initRepoIssueContentHistory() { 107 121 const issueIndex = $('#issueIndex').val(); 108 122 if (!issueIndex) return; 109 123 ··· 114 128 const repoLink = $('#repolink').val(); 115 129 const issueBaseUrl = `${appSubUrl}/${repoLink}/issues/${issueIndex}`; 116 130 117 - $.ajax({ 118 - url: `${issueBaseUrl}/content-history/overview`, 119 - data: { 120 - _csrf: csrfToken, 121 - }, 122 - }).done((resp) => { 131 + try { 132 + const response = await GET(`${issueBaseUrl}/content-history/overview`); 133 + const resp = await response.json(); 134 + 123 135 i18nTextEdited = resp.i18n.textEdited; 124 136 i18nTextDeleteFromHistory = resp.i18n.textDeleteFromHistory; 125 137 i18nTextDeleteFromHistoryConfirm = resp.i18n.textDeleteFromHistoryConfirm; ··· 133 145 const $itemComment = $(`#issuecomment-${commentId}`); 134 146 showContentHistoryMenu(issueBaseUrl, $itemComment, commentId); 135 147 } 136 - }); 148 + } catch (error) { 149 + console.error('Error:', error); 150 + } 137 151 }
+26 -14
web_src/js/features/repo-issue-list.js
··· 1 1 import $ from 'jquery'; 2 2 import {updateIssuesMeta} from './repo-issue.js'; 3 - import {toggleElem, hideElem} from '../utils/dom.js'; 3 + import {toggleElem, hideElem, isElemHidden} from '../utils/dom.js'; 4 4 import {htmlEscape} from 'escape-goat'; 5 5 import {confirmModal} from './comp/ConfirmModal.js'; 6 6 import {showErrorToast} from '../modules/toast.js'; ··· 8 8 import {DELETE, POST} from '../modules/fetch.js'; 9 9 10 10 function initRepoIssueListCheckboxes() { 11 - const $issueSelectAll = $('.issue-checkbox-all'); 12 - const $issueCheckboxes = $('.issue-checkbox'); 11 + const issueSelectAll = document.querySelector('.issue-checkbox-all'); 12 + const issueCheckboxes = document.querySelectorAll('.issue-checkbox'); 13 13 14 14 const syncIssueSelectionState = () => { 15 - const $checked = $issueCheckboxes.filter(':checked'); 16 - const anyChecked = $checked.length !== 0; 17 - const allChecked = anyChecked && $checked.length === $issueCheckboxes.length; 15 + const checkedCheckboxes = Array.from(issueCheckboxes).filter((el) => el.checked); 16 + const anyChecked = Boolean(checkedCheckboxes.length); 17 + const allChecked = anyChecked && checkedCheckboxes.length === issueCheckboxes.length; 18 18 19 19 if (allChecked) { 20 - $issueSelectAll.prop({'checked': true, 'indeterminate': false}); 20 + issueSelectAll.checked = true; 21 + issueSelectAll.indeterminate = false; 21 22 } else if (anyChecked) { 22 - $issueSelectAll.prop({'checked': false, 'indeterminate': true}); 23 + issueSelectAll.checked = false; 24 + issueSelectAll.indeterminate = true; 23 25 } else { 24 - $issueSelectAll.prop({'checked': false, 'indeterminate': false}); 26 + issueSelectAll.checked = false; 27 + issueSelectAll.indeterminate = false; 25 28 } 26 29 // if any issue is selected, show the action panel, otherwise show the filter panel 27 30 toggleElem($('#issue-filters'), !anyChecked); 28 31 toggleElem($('#issue-actions'), anyChecked); 29 32 // there are two panels but only one select-all checkbox, so move the checkbox to the visible panel 30 - $('#issue-filters, #issue-actions').filter(':visible').find('.issue-list-toolbar-left').prepend($issueSelectAll); 33 + const panels = document.querySelectorAll('#issue-filters, #issue-actions'); 34 + const visiblePanel = Array.from(panels).find((el) => !isElemHidden(el)); 35 + const toolbarLeft = visiblePanel.querySelector('.issue-list-toolbar-left'); 36 + toolbarLeft.prepend(issueSelectAll); 31 37 }; 32 38 33 - $issueCheckboxes.on('change', syncIssueSelectionState); 39 + for (const el of issueCheckboxes) { 40 + el.addEventListener('change', syncIssueSelectionState); 41 + } 34 42 35 - $issueSelectAll.on('change', () => { 36 - $issueCheckboxes.prop('checked', $issueSelectAll.is(':checked')); 43 + issueSelectAll.addEventListener('change', () => { 44 + for (const el of issueCheckboxes) { 45 + el.checked = issueSelectAll.checked; 46 + } 37 47 syncIssueSelectionState(); 38 48 }); 39 49 ··· 125 135 if (newMenuHtml) { 126 136 const $newMenuItems = $(newMenuHtml); 127 137 $newMenuItems.addClass('dynamic-item'); 128 - $menu.append('<div class="divider dynamic-item"></div>', ...$newMenuItems); 138 + const div = document.createElement('div'); 139 + div.classList.add('divider', 'dynamic-item'); 140 + $menu[0].append(div, ...$newMenuItems); 129 141 } 130 142 $searchDropdown.dropdown('refresh'); 131 143 // defer our selection to the next tick, because dropdown will set the selection item after this `menu` function
+153 -123
web_src/js/features/repo-issue.js
··· 6 6 import {getComboMarkdownEditor, initComboMarkdownEditor} from './comp/ComboMarkdownEditor.js'; 7 7 import {toAbsoluteUrl} from '../utils.js'; 8 8 import {initDropzone} from './common-global.js'; 9 + import {POST, GET} from '../modules/fetch.js'; 9 10 10 - const {appSubUrl, csrfToken} = window.config; 11 + const {appSubUrl} = window.config; 11 12 12 13 export function initRepoIssueTimeTracking() { 13 14 $(document).on('click', '.issue-add-time', () => { ··· 40 41 }); 41 42 } 42 43 43 - function updateDeadline(deadlineString) { 44 + async function updateDeadline(deadlineString) { 44 45 hideElem($('#deadline-err-invalid-date')); 45 46 $('#deadline-loader').addClass('loading'); 46 47 ··· 56 57 realDeadline = new Date(newDate); 57 58 } 58 59 59 - $.ajax(`${$('#update-issue-deadline-form').attr('action')}`, { 60 - data: JSON.stringify({ 61 - due_date: realDeadline, 62 - }), 63 - headers: { 64 - 'X-Csrf-Token': csrfToken, 65 - }, 66 - contentType: 'application/json', 67 - type: 'POST', 68 - success() { 60 + try { 61 + const response = await POST($('#update-issue-deadline-form').attr('action'), { 62 + data: {due_date: realDeadline} 63 + }); 64 + 65 + if (response.ok) { 69 66 window.location.reload(); 70 - }, 71 - error() { 72 - $('#deadline-loader').removeClass('loading'); 73 - showElem($('#deadline-err-invalid-date')); 74 - }, 75 - }); 67 + } else { 68 + throw new Error('Invalid response'); 69 + } 70 + } catch (error) { 71 + console.error(error); 72 + $('#deadline-loader').removeClass('loading'); 73 + showElem($('#deadline-err-invalid-date')); 74 + } 76 75 } 77 76 78 77 export function initRepoIssueDue() { ··· 145 144 146 145 $('.menu .ui.dropdown.label-filter').on('keydown', (e) => { 147 146 if (e.altKey && e.keyCode === 13) { 148 - const selectedItems = $('.menu .ui.dropdown.label-filter .menu .item.selected'); 149 - if (selectedItems.length > 0) { 150 - excludeLabel($(selectedItems[0])); 147 + const $selectedItems = $('.menu .ui.dropdown.label-filter .menu .item.selected'); 148 + if ($selectedItems.length > 0) { 149 + excludeLabel($($selectedItems[0])); 151 150 } 152 151 } 153 152 }); ··· 156 155 157 156 export function initRepoIssueCommentDelete() { 158 157 // Delete comment 159 - $(document).on('click', '.delete-comment', function () { 158 + $(document).on('click', '.delete-comment', async function () { 160 159 const $this = $(this); 161 160 if (window.confirm($this.data('locale'))) { 162 - $.post($this.data('url'), { 163 - _csrf: csrfToken, 164 - }).done(() => { 161 + try { 162 + const response = await POST($this.data('url')); 163 + if (!response.ok) throw new Error('Failed to delete comment'); 165 164 const $conversationHolder = $this.closest('.conversation-holder'); 166 165 167 166 // Check if this was a pending comment. ··· 186 185 } 187 186 $conversationHolder.remove(); 188 187 } 189 - }); 188 + } catch (error) { 189 + console.error(error); 190 + } 190 191 } 191 192 return false; 192 193 }); ··· 213 214 export function initRepoIssueCodeCommentCancel() { 214 215 // Cancel inline code comment 215 216 $(document).on('click', '.cancel-code-comment', (e) => { 216 - const form = $(e.currentTarget).closest('form'); 217 - if (form.length > 0 && form.hasClass('comment-form')) { 218 - form.addClass('gt-hidden'); 219 - showElem(form.closest('.comment-code-cloud').find('button.comment-form-reply')); 217 + const $form = $(e.currentTarget).closest('form'); 218 + if ($form.length > 0 && $form.hasClass('comment-form')) { 219 + $form.addClass('gt-hidden'); 220 + showElem($form.closest('.comment-code-cloud').find('button.comment-form-reply')); 220 221 } else { 221 - form.closest('.comment-code-cloud').remove(); 222 + $form.closest('.comment-code-cloud').remove(); 222 223 } 223 224 }); 224 225 } ··· 226 227 export function initRepoPullRequestUpdate() { 227 228 // Pull Request update button 228 229 const $pullUpdateButton = $('.update-button > button'); 229 - $pullUpdateButton.on('click', function (e) { 230 + $pullUpdateButton.on('click', async function (e) { 230 231 e.preventDefault(); 231 232 const $this = $(this); 232 233 const redirect = $this.data('redirect'); 233 234 $this.addClass('loading'); 234 - $.post($this.data('do'), { 235 - _csrf: csrfToken 236 - }).done((data) => { 237 - if (data.redirect) { 238 - window.location.href = data.redirect; 239 - } else if (redirect) { 240 - window.location.href = redirect; 241 - } else { 242 - window.location.reload(); 243 - } 244 - }); 235 + let response; 236 + try { 237 + response = await POST($this.data('do')); 238 + } catch (error) { 239 + console.error(error); 240 + } finally { 241 + $this.removeClass('loading'); 242 + } 243 + let data; 244 + try { 245 + data = await response?.json(); // the response is probably not a JSON 246 + } catch (error) { 247 + console.error(error); 248 + } 249 + if (data?.redirect) { 250 + window.location.href = data.redirect; 251 + } else if (redirect) { 252 + window.location.href = redirect; 253 + } else { 254 + window.location.reload(); 255 + } 245 256 }); 246 257 247 258 $('.update-button > .dropdown').dropdown({ ··· 267 278 268 279 const promptError = $checkbox.attr('data-prompt-error'); 269 280 $checkbox.checkbox({ 270 - 'onChange': () => { 281 + 'onChange': async () => { 271 282 const checked = $checkbox.checkbox('is checked'); 272 283 let url = $checkbox.attr('data-url'); 273 284 url += '/set_allow_maintainer_edit'; 274 285 $checkbox.checkbox('set disabled'); 275 - $.ajax({url, type: 'POST', 276 - data: {_csrf: csrfToken, allow_maintainer_edit: checked}, 277 - error: () => { 278 - showTemporaryTooltip($checkbox[0], promptError); 279 - }, 280 - complete: () => { 281 - $checkbox.checkbox('set enabled'); 282 - }, 283 - }); 286 + try { 287 + const response = await POST(url, { 288 + data: {allow_maintainer_edit: checked}, 289 + }); 290 + if (!response.ok) { 291 + throw new Error('Failed to update maintainer edit permission'); 292 + } 293 + } catch (error) { 294 + console.error(error); 295 + showTemporaryTooltip($checkbox[0], promptError); 296 + } finally { 297 + $checkbox.checkbox('set enabled'); 298 + } 284 299 }, 285 300 }); 286 301 } ··· 329 344 }); 330 345 } 331 346 332 - export async function updateIssuesMeta(url, action, issueIds, elementId) { 333 - return $.ajax({ 334 - type: 'POST', 335 - url, 336 - data: { 337 - _csrf: csrfToken, 338 - action, 339 - issue_ids: issueIds, 340 - id: elementId, 341 - }, 342 - }); 347 + export async function updateIssuesMeta(url, action, issue_ids, id) { 348 + try { 349 + const response = await POST(url, {data: new URLSearchParams({action, issue_ids, id})}); 350 + if (!response.ok) { 351 + throw new Error('Failed to update issues meta'); 352 + } 353 + } catch (error) { 354 + console.error(error); 355 + } 343 356 } 344 357 345 358 export function initRepoIssueComments() { ··· 357 370 }); 358 371 359 372 $(document).on('click', (event) => { 360 - const urlTarget = $(':target'); 361 - if (urlTarget.length === 0) return; 373 + const $urlTarget = $(':target'); 374 + if ($urlTarget.length === 0) return; 362 375 363 - const urlTargetId = urlTarget.attr('id'); 376 + const urlTargetId = $urlTarget.attr('id'); 364 377 if (!urlTargetId) return; 365 378 if (!/^(issue|pull)(comment)?-\d+$/.test(urlTargetId)) return; 366 379 ··· 377 390 378 391 export async function handleReply($el) { 379 392 hideElem($el); 380 - const form = $el.closest('.comment-code-cloud').find('.comment-form'); 381 - form.removeClass('gt-hidden'); 393 + const $form = $el.closest('.comment-code-cloud').find('.comment-form'); 394 + $form.removeClass('gt-hidden'); 382 395 383 - const $textarea = form.find('textarea'); 396 + const $textarea = $form.find('textarea'); 384 397 let editor = getComboMarkdownEditor($textarea); 385 398 if (!editor) { 386 399 // FIXME: the initialization of the dropzone is not consistent. 387 400 // When the page is loaded, the dropzone is initialized by initGlobalDropzone, but the editor is not initialized. 388 401 // When the form is submitted and partially reload, none of them is initialized. 389 - const dropzone = form.find('.dropzone')[0]; 402 + const dropzone = $form.find('.dropzone')[0]; 390 403 if (!dropzone.dropzone) initDropzone(dropzone); 391 - editor = await initComboMarkdownEditor(form.find('.combo-markdown-editor')); 404 + editor = await initComboMarkdownEditor($form.find('.combo-markdown-editor')); 392 405 } 393 406 editor.focus(); 394 407 return editor; ··· 400 413 if (window.history.scrollRestoration !== 'manual') { 401 414 window.history.scrollRestoration = 'manual'; 402 415 } 403 - const commentDiv = $(window.location.hash); 404 - if (commentDiv) { 416 + const $commentDiv = $(window.location.hash); 417 + if ($commentDiv) { 405 418 // get the name of the parent id 406 - const groupID = commentDiv.closest('div[id^="code-comments-"]').attr('id'); 419 + const groupID = $commentDiv.closest('div[id^="code-comments-"]').attr('id'); 407 420 if (groupID && groupID.startsWith('code-comments-')) { 408 421 const id = groupID.slice(14); 409 - const ancestorDiffBox = commentDiv.closest('.diff-file-box'); 422 + const $ancestorDiffBox = $commentDiv.closest('.diff-file-box'); 410 423 // on pages like conversation, there is no diff header 411 - const diffHeader = ancestorDiffBox.find('.diff-file-header'); 424 + const $diffHeader = $ancestorDiffBox.find('.diff-file-header'); 412 425 // offset is for scrolling 413 426 let offset = 30; 414 - if (diffHeader[0]) { 415 - offset += $('.diff-detail-box').outerHeight() + diffHeader.outerHeight(); 427 + if ($diffHeader[0]) { 428 + offset += $('.diff-detail-box').outerHeight() + $diffHeader.outerHeight(); 416 429 } 417 430 $(`#show-outdated-${id}`).addClass('gt-hidden'); 418 431 $(`#code-comments-${id}`).removeClass('gt-hidden'); 419 432 $(`#code-preview-${id}`).removeClass('gt-hidden'); 420 433 $(`#hide-outdated-${id}`).removeClass('gt-hidden'); 421 434 // if the comment box is folded, expand it 422 - if (ancestorDiffBox.attr('data-folded') && ancestorDiffBox.attr('data-folded') === 'true') { 423 - setFileFolding(ancestorDiffBox[0], ancestorDiffBox.find('.fold-file')[0], false); 435 + if ($ancestorDiffBox.attr('data-folded') && $ancestorDiffBox.attr('data-folded') === 'true') { 436 + setFileFolding($ancestorDiffBox[0], $ancestorDiffBox.find('.fold-file')[0], false); 424 437 } 425 438 window.scrollTo({ 426 - top: commentDiv.offset().top - offset, 439 + top: $commentDiv.offset().top - offset, 427 440 behavior: 'instant' 428 441 }); 429 442 } ··· 491 504 const side = $(this).data('side'); 492 505 const idx = $(this).data('idx'); 493 506 const path = $(this).closest('[data-path]').data('path'); 494 - const tr = $(this).closest('tr'); 495 - const lineType = tr.data('line-type'); 507 + const $tr = $(this).closest('tr'); 508 + const lineType = $tr.data('line-type'); 496 509 497 - let ntr = tr.next(); 498 - if (!ntr.hasClass('add-comment')) { 499 - ntr = $(` 510 + let $ntr = $tr.next(); 511 + if (!$ntr.hasClass('add-comment')) { 512 + $ntr = $(` 500 513 <tr class="add-comment" data-line-type="${lineType}"> 501 514 ${isSplit ? ` 502 515 <td class="add-comment-left" colspan="4"></td> ··· 505 518 <td class="add-comment-left add-comment-right" colspan="5"></td> 506 519 `} 507 520 </tr>`); 508 - tr.after(ntr); 521 + $tr.after($ntr); 509 522 } 510 523 511 - const td = ntr.find(`.add-comment-${side}`); 512 - const commentCloud = td.find('.comment-code-cloud'); 513 - if (commentCloud.length === 0 && !ntr.find('button[name="pending_review"]').length) { 514 - const html = await $.get($(this).closest('[data-new-comment-url]').attr('data-new-comment-url')); 515 - td.html(html); 516 - td.find("input[name='line']").val(idx); 517 - td.find("input[name='side']").val(side === 'left' ? 'previous' : 'proposed'); 518 - td.find("input[name='path']").val(path); 524 + const $td = $ntr.find(`.add-comment-${side}`); 525 + const $commentCloud = $td.find('.comment-code-cloud'); 526 + if ($commentCloud.length === 0 && !$ntr.find('button[name="pending_review"]').length) { 527 + try { 528 + const response = await GET($(this).closest('[data-new-comment-url]').attr('data-new-comment-url')); 529 + const html = await response.text(); 530 + $td.html(html); 531 + $td.find("input[name='line']").val(idx); 532 + $td.find("input[name='side']").val(side === 'left' ? 'previous' : 'proposed'); 533 + $td.find("input[name='path']").val(path); 519 534 520 - initDropzone(td.find('.dropzone')[0]); 521 - const editor = await initComboMarkdownEditor(td.find('.combo-markdown-editor')); 522 - editor.focus(); 535 + initDropzone($td.find('.dropzone')[0]); 536 + const editor = await initComboMarkdownEditor($td.find('.combo-markdown-editor')); 537 + editor.focus(); 538 + } catch (error) { 539 + console.error(error); 540 + } 523 541 } 524 542 }); 525 543 } ··· 547 565 const title = toggleWip.getAttribute('data-title'); 548 566 const wipPrefix = toggleWip.getAttribute('data-wip-prefix'); 549 567 const updateUrl = toggleWip.getAttribute('data-update-url'); 550 - await $.post(updateUrl, { 551 - _csrf: csrfToken, 552 - title: title?.startsWith(wipPrefix) ? title.slice(wipPrefix.length).trim() : `${wipPrefix.trim()} ${title}`, 553 - }); 554 - window.location.reload(); 568 + 569 + try { 570 + const params = new URLSearchParams(); 571 + params.append('title', title?.startsWith(wipPrefix) ? title.slice(wipPrefix.length).trim() : `${wipPrefix.trim()} ${title}`); 572 + 573 + const response = await POST(updateUrl, {data: params}); 574 + if (!response.ok) { 575 + throw new Error('Failed to toggle WIP status'); 576 + } 577 + window.location.reload(); 578 + } catch (error) { 579 + console.error(error); 580 + } 555 581 }); 556 582 } 557 583 ··· 576 602 577 603 $('#edit-title').on('click', editTitleToggle); 578 604 $('#cancel-edit-title').on('click', editTitleToggle); 579 - $('#save-edit-title').on('click', editTitleToggle).on('click', function () { 580 - const pullrequest_targetbranch_change = function (update_url) { 605 + $('#save-edit-title').on('click', editTitleToggle).on('click', async function () { 606 + const pullrequest_targetbranch_change = async function (update_url) { 581 607 const targetBranch = $('#pull-target-branch').data('branch'); 582 608 const $branchTarget = $('#branch_target'); 583 609 if (targetBranch === $branchTarget.text()) { 584 610 window.location.reload(); 585 611 return false; 586 612 } 587 - $.post(update_url, { 588 - _csrf: csrfToken, 589 - target_branch: targetBranch 590 - }).always(() => { 613 + try { 614 + await POST(update_url, {data: new URLSearchParams({target_branch: targetBranch})}); 615 + } catch (error) { 616 + console.error(error); 617 + } finally { 591 618 window.location.reload(); 592 - }); 619 + } 593 620 }; 594 621 595 622 const pullrequest_target_update_url = $(this).attr('data-target-update-url'); 596 623 if ($editInput.val().length === 0 || $editInput.val() === $issueTitle.text()) { 597 624 $editInput.val($issueTitle.text()); 598 - pullrequest_targetbranch_change(pullrequest_target_update_url); 625 + await pullrequest_targetbranch_change(pullrequest_target_update_url); 599 626 } else { 600 - $.post($(this).attr('data-update-url'), { 601 - _csrf: csrfToken, 602 - title: $editInput.val() 603 - }, (data) => { 627 + try { 628 + const params = new URLSearchParams(); 629 + params.append('title', $editInput.val()); 630 + const response = await POST($(this).attr('data-update-url'), {data: params}); 631 + const data = await response.json(); 604 632 $editInput.val(data.title); 605 633 $issueTitle.text(data.title); 606 634 if (pullrequest_target_update_url) { 607 - pullrequest_targetbranch_change(pullrequest_target_update_url); // it will reload the window 635 + await pullrequest_targetbranch_change(pullrequest_target_update_url); // it will reload the window 608 636 } else { 609 637 window.location.reload(); 610 638 } 611 - }); 639 + } catch (error) { 640 + console.error(error); 641 + } 612 642 } 613 643 return false; 614 644 }); ··· 616 646 617 647 export function initRepoIssueBranchSelect() { 618 648 const changeBranchSelect = function () { 619 - const selectionTextField = $('#pull-target-branch'); 649 + const $selectionTextField = $('#pull-target-branch'); 620 650 621 - const baseName = selectionTextField.data('basename'); 651 + const baseName = $selectionTextField.data('basename'); 622 652 const branchNameNew = $(this).data('branch'); 623 - const branchNameOld = selectionTextField.data('branch'); 653 + const branchNameOld = $selectionTextField.data('branch'); 624 654 625 655 // Replace branch name to keep translation from HTML template 626 - selectionTextField.html(selectionTextField.html().replace( 656 + $selectionTextField.html($selectionTextField.html().replace( 627 657 `${baseName}:${branchNameOld}`, 628 658 `${baseName}:${branchNameNew}` 629 659 )); 630 - selectionTextField.data('branch', branchNameNew); // update branch name in setting 660 + $selectionTextField.data('branch', branchNameNew); // update branch name in setting 631 661 }; 632 662 $('#branch-select > .item').on('click', changeBranchSelect); 633 663 }
+50 -33
web_src/js/features/repo-legacy.js
··· 24 24 import {hideElem, showElem} from '../utils/dom.js'; 25 25 import {getComboMarkdownEditor, initComboMarkdownEditor} from './comp/ComboMarkdownEditor.js'; 26 26 import {attachRefIssueContextPopup} from './contextpopup.js'; 27 + import {POST, GET} from '../modules/fetch.js'; 27 28 28 29 const {csrfToken} = window.config; 29 30 ··· 65 66 const $selectBranch = $('.ui.select-branch'); 66 67 const $branchMenu = $selectBranch.find('.reference-list-menu'); 67 68 const $isNewIssue = $branchMenu.hasClass('new-issue'); 68 - $branchMenu.find('.item:not(.no-select)').on('click', function () { 69 + $branchMenu.find('.item:not(.no-select)').on('click', async function () { 69 70 const selectedValue = $(this).data('id'); 70 71 const editMode = $('#editing_mode').val(); 71 72 $($(this).data('id-selector')).val(selectedValue); ··· 75 76 } 76 77 77 78 if (editMode === 'true') { 78 - const form = $('#update_issueref_form'); 79 - $.post(form.attr('action'), {_csrf: csrfToken, ref: selectedValue}, () => window.location.reload()); 79 + const $form = $('#update_issueref_form'); 80 + const params = new URLSearchParams(); 81 + params.append('ref', selectedValue); 82 + try { 83 + await POST($form.attr('action'), {data: params}); 84 + window.location.reload(); 85 + } catch (error) { 86 + console.error(error); 87 + } 80 88 } else if (editMode === '') { 81 89 $selectBranch.find('.ui .branch-name').text(selectedValue); 82 90 } ··· 131 139 132 140 hasUpdateAction = $listMenu.data('action') === 'update'; // Update the var 133 141 134 - const clickedItem = $(this); 142 + const $clickedItem = $(this); 135 143 const scope = $(this).attr('data-scope'); 136 144 137 145 $(this).parent().find('.item').each(function () { ··· 140 148 if ($(this).attr('data-scope') !== scope) { 141 149 return true; 142 150 } 143 - if (!$(this).is(clickedItem) && !$(this).hasClass('checked')) { 151 + if (!$(this).is($clickedItem) && !$(this).hasClass('checked')) { 144 152 return true; 145 153 } 146 - } else if (!$(this).is(clickedItem)) { 154 + } else if (!$(this).is($clickedItem)) { 147 155 // Toggle for other labels 148 156 return true; 149 157 } ··· 344 352 this.on('success', (file, data) => { 345 353 file.uuid = data.uuid; 346 354 fileUuidDict[file.uuid] = {submitted: false}; 347 - const input = $(`<input id="${data.uuid}" name="files" type="hidden">`).val(data.uuid); 348 - $dropzone.find('.files').append(input); 355 + const $input = $(`<input id="${data.uuid}" name="files" type="hidden">`).val(data.uuid); 356 + $dropzone.find('.files').append($input); 349 357 }); 350 - this.on('removedfile', (file) => { 358 + this.on('removedfile', async (file) => { 351 359 if (disableRemovedfileEvent) return; 352 360 $(`#${file.uuid}`).remove(); 353 361 if ($dropzone.attr('data-remove-url') && !fileUuidDict[file.uuid].submitted) { 354 - $.post($dropzone.attr('data-remove-url'), { 355 - file: file.uuid, 356 - _csrf: csrfToken, 357 - }); 362 + try { 363 + await POST($dropzone.attr('data-remove-url'), {data: new URLSearchParams({file: file.uuid})}); 364 + } catch (error) { 365 + console.error(error); 366 + } 358 367 } 359 368 }); 360 369 this.on('submit', () => { ··· 362 371 fileUuidDict[fileUuid].submitted = true; 363 372 }); 364 373 }); 365 - this.on('reload', () => { 366 - $.getJSON($editContentZone.attr('data-attachment-url'), (data) => { 374 + this.on('reload', async () => { 375 + try { 376 + const response = await GET($editContentZone.attr('data-attachment-url')); 377 + const data = await response.json(); 367 378 // do not trigger the "removedfile" event, otherwise the attachments would be deleted from server 368 379 disableRemovedfileEvent = true; 369 380 dz.removeAllFiles(true); ··· 379 390 dz.files.push(attachment); 380 391 fileUuidDict[attachment.uuid] = {submitted: true}; 381 392 $dropzone.find(`img[src='${imgSrc}']`).css('max-width', '100%'); 382 - const input = $(`<input id="${attachment.uuid}" name="files" type="hidden">`).val(attachment.uuid); 383 - $dropzone.find('.files').append(input); 393 + const $input = $(`<input id="${attachment.uuid}" name="files" type="hidden">`).val(attachment.uuid); 394 + $dropzone.find('.files').append($input); 384 395 } 385 - }); 396 + } catch (error) { 397 + console.error(error); 398 + } 386 399 }); 387 400 }, 388 401 }); ··· 398 411 } 399 412 }; 400 413 401 - const saveAndRefresh = (dz) => { 414 + const saveAndRefresh = async (dz) => { 402 415 showElem($renderContent); 403 416 hideElem($editContentZone); 404 - $.post($editContentZone.attr('data-update-url'), { 405 - _csrf: csrfToken, 406 - content: comboMarkdownEditor.value(), 407 - context: $editContentZone.attr('data-context'), 408 - files: dz.files.map((file) => file.uuid), 409 - }, (data) => { 417 + 418 + try { 419 + const params = new URLSearchParams({ 420 + content: comboMarkdownEditor.value(), 421 + context: $editContentZone.attr('data-context'), 422 + }); 423 + for (const file of dz.files) params.append('files[]', file.uuid); 424 + 425 + const response = await POST($editContentZone.attr('data-update-url'), {data: params}); 426 + const data = await response.json(); 410 427 if (!data.content) { 411 428 $renderContent.html($('#no-content').html()); 412 429 $rawContent.text(''); 413 430 } else { 414 431 $renderContent.html(data.content); 415 432 $rawContent.text(comboMarkdownEditor.value()); 416 - 417 - const refIssues = $renderContent.find('p .ref-issue'); 418 - attachRefIssueContextPopup(refIssues); 433 + const $refIssues = $renderContent.find('p .ref-issue'); 434 + attachRefIssueContextPopup($refIssues); 419 435 } 420 436 const $content = $segment; 421 437 if (!$content.find('.dropzone-attachments').length) { 422 438 if (data.attachments !== '') { 423 - $content.append(`<div class="dropzone-attachments"></div>`); 424 - $content.find('.dropzone-attachments').replaceWith(data.attachments); 439 + $content[0].append(data.attachments); 425 440 } 426 441 } else if (data.attachments === '') { 427 442 $content.find('.dropzone-attachments').remove(); 428 443 } else { 429 - $content.find('.dropzone-attachments').replaceWith(data.attachments); 444 + $content.find('.dropzone-attachments')[0].outerHTML = data.attachments; 430 445 } 431 446 if (dz) { 432 447 dz.emit('submit'); ··· 434 449 } 435 450 initMarkupContent(); 436 451 initCommentContent(); 437 - }); 452 + } catch (error) { 453 + console.error(error); 454 + } 438 455 }; 439 456 440 457 if (!$editContentZone.html()) { ··· 516 533 const gitignores = $('input[name="gitignores"]').val(); 517 534 const license = $('input[name="license"]').val(); 518 535 if (gitignores || license) { 519 - $('input[name="auto_init"]').prop('checked', true); 536 + document.querySelector('input[name="auto_init"]').checked = true; 520 537 } 521 538 }); 522 539 }
+81 -89
web_src/js/features/repo-projects.js
··· 2 2 import {useLightTextOnBackground} from '../utils/color.js'; 3 3 import tinycolor from 'tinycolor2'; 4 4 import {createSortable} from '../modules/sortable.js'; 5 - 6 - const {csrfToken} = window.config; 5 + import {POST, DELETE, PUT} from '../modules/fetch.js'; 7 6 8 7 function updateIssueCount(cards) { 9 8 const parent = cards.parentElement; ··· 11 10 parent.getElementsByClassName('project-column-issue-count')[0].textContent = cnt; 12 11 } 13 12 14 - function createNewColumn(url, columnTitle, projectColorInput) { 15 - $.ajax({ 16 - url, 17 - data: JSON.stringify({title: columnTitle.val(), color: projectColorInput.val()}), 18 - headers: { 19 - 'X-Csrf-Token': csrfToken, 20 - }, 21 - contentType: 'application/json', 22 - method: 'POST', 23 - }).done(() => { 13 + async function createNewColumn(url, columnTitle, projectColorInput) { 14 + try { 15 + await POST(url, { 16 + data: { 17 + title: columnTitle.val(), 18 + color: projectColorInput.val(), 19 + }, 20 + }); 21 + } catch (error) { 22 + console.error(error); 23 + } finally { 24 24 columnTitle.closest('form').removeClass('dirty'); 25 25 window.location.reload(); 26 - }); 26 + } 27 27 } 28 28 29 - function moveIssue({item, from, to, oldIndex}) { 29 + async function moveIssue({item, from, to, oldIndex}) { 30 30 const columnCards = to.getElementsByClassName('issue-card'); 31 31 updateIssueCount(from); 32 32 updateIssueCount(to); ··· 38 38 })), 39 39 }; 40 40 41 - $.ajax({ 42 - url: `${to.getAttribute('data-url')}/move`, 43 - data: JSON.stringify(columnSorting), 44 - headers: { 45 - 'X-Csrf-Token': csrfToken, 46 - }, 47 - contentType: 'application/json', 48 - type: 'POST', 49 - error: () => { 50 - from.insertBefore(item, from.children[oldIndex]); 51 - }, 52 - }); 41 + try { 42 + await POST(`${to.getAttribute('data-url')}/move`, { 43 + data: columnSorting, 44 + }); 45 + } catch (error) { 46 + console.error(error); 47 + from.insertBefore(item, from.children[oldIndex]); 48 + } 53 49 } 54 50 55 51 async function initRepoProjectSortable() { ··· 67 63 ghostClass: 'card-ghost', 68 64 delayOnTouchOnly: true, 69 65 delay: 500, 70 - onSort: () => { 66 + onSort: async () => { 71 67 boardColumns = mainBoard.getElementsByClassName('project-column'); 72 68 for (let i = 0; i < boardColumns.length; i++) { 73 69 const column = boardColumns[i]; 74 70 if (parseInt($(column).data('sorting')) !== i) { 75 - $.ajax({ 76 - url: $(column).data('url'), 77 - data: JSON.stringify({sorting: i, color: rgbToHex($(column).css('backgroundColor'))}), 78 - headers: { 79 - 'X-Csrf-Token': csrfToken, 80 - }, 81 - contentType: 'application/json', 82 - method: 'PUT', 83 - }); 71 + try { 72 + await PUT($(column).data('url'), { 73 + data: { 74 + sorting: i, 75 + color: rgbToHex($(column).css('backgroundColor')), 76 + }, 77 + }); 78 + } catch (error) { 79 + console.error(error); 80 + } 84 81 } 85 82 } 86 83 }, ··· 108 105 const _promise = initRepoProjectSortable(); 109 106 110 107 $('.edit-project-column-modal').each(function () { 111 - const projectHeader = $(this).closest('.project-column-header'); 112 - const projectTitleLabel = projectHeader.find('.project-column-title'); 113 - const projectTitleInput = $(this).find('.project-column-title-input'); 114 - const projectColorInput = $(this).find('#new_project_column_color'); 115 - const boardColumn = $(this).closest('.project-column'); 108 + const $projectHeader = $(this).closest('.project-column-header'); 109 + const $projectTitleLabel = $projectHeader.find('.project-column-title'); 110 + const $projectTitleInput = $(this).find('.project-column-title-input'); 111 + const $projectColorInput = $(this).find('#new_project_column_color'); 112 + const $boardColumn = $(this).closest('.project-column'); 116 113 117 - if (boardColumn.css('backgroundColor')) { 118 - setLabelColor(projectHeader, rgbToHex(boardColumn.css('backgroundColor'))); 114 + if ($boardColumn.css('backgroundColor')) { 115 + setLabelColor($projectHeader, rgbToHex($boardColumn.css('backgroundColor'))); 119 116 } 120 117 121 - $(this).find('.edit-project-column-button').on('click', function (e) { 118 + $(this).find('.edit-project-column-button').on('click', async function (e) { 122 119 e.preventDefault(); 123 120 124 - $.ajax({ 125 - url: $(this).data('url'), 126 - data: JSON.stringify({title: projectTitleInput.val(), color: projectColorInput.val()}), 127 - headers: { 128 - 'X-Csrf-Token': csrfToken, 129 - }, 130 - contentType: 'application/json', 131 - method: 'PUT', 132 - }).done(() => { 133 - projectTitleLabel.text(projectTitleInput.val()); 134 - projectTitleInput.closest('form').removeClass('dirty'); 135 - if (projectColorInput.val()) { 136 - setLabelColor(projectHeader, projectColorInput.val()); 121 + try { 122 + await PUT($(this).data('url'), { 123 + data: { 124 + title: $projectTitleInput.val(), 125 + color: $projectColorInput.val(), 126 + }, 127 + }); 128 + } catch (error) { 129 + console.error(error); 130 + } finally { 131 + $projectTitleLabel.text($projectTitleInput.val()); 132 + $projectTitleInput.closest('form').removeClass('dirty'); 133 + if ($projectColorInput.val()) { 134 + setLabelColor($projectHeader, $projectColorInput.val()); 137 135 } 138 - boardColumn.attr('style', `background: ${projectColorInput.val()}!important`); 136 + $boardColumn.attr('style', `background: ${$projectColorInput.val()}!important`); 139 137 $('.ui.modal').modal('hide'); 140 - }); 138 + } 141 139 }); 142 140 }); 143 141 144 142 $('.default-project-column-modal').each(function () { 145 - const boardColumn = $(this).closest('.project-column'); 146 - const showButton = $(boardColumn).find('.default-project-column-show'); 147 - const commitButton = $(this).find('.actions > .ok.button'); 143 + const $boardColumn = $(this).closest('.project-column'); 144 + const $showButton = $($boardColumn).find('.default-project-column-show'); 145 + const $commitButton = $(this).find('.actions > .ok.button'); 148 146 149 - $(commitButton).on('click', (e) => { 147 + $($commitButton).on('click', async (e) => { 150 148 e.preventDefault(); 151 149 152 - $.ajax({ 153 - method: 'POST', 154 - url: $(showButton).data('url'), 155 - headers: { 156 - 'X-Csrf-Token': csrfToken, 157 - }, 158 - contentType: 'application/json', 159 - }).done(() => { 150 + try { 151 + await POST($($showButton).data('url')); 152 + } catch (error) { 153 + console.error(error); 154 + } finally { 160 155 window.location.reload(); 161 - }); 156 + } 162 157 }); 163 158 }); 164 159 165 160 $('.show-delete-project-column-modal').each(function () { 166 - const deleteColumnModal = $(`${$(this).attr('data-modal')}`); 167 - const deleteColumnButton = deleteColumnModal.find('.actions > .ok.button'); 161 + const $deleteColumnModal = $(`${$(this).attr('data-modal')}`); 162 + const $deleteColumnButton = $deleteColumnModal.find('.actions > .ok.button'); 168 163 const deleteUrl = $(this).attr('data-url'); 169 164 170 - deleteColumnButton.on('click', (e) => { 165 + $deleteColumnButton.on('click', async (e) => { 171 166 e.preventDefault(); 172 167 173 - $.ajax({ 174 - url: deleteUrl, 175 - headers: { 176 - 'X-Csrf-Token': csrfToken, 177 - }, 178 - contentType: 'application/json', 179 - method: 'DELETE', 180 - }).done(() => { 168 + try { 169 + await DELETE(deleteUrl); 170 + } catch (error) { 171 + console.error(error); 172 + } finally { 181 173 window.location.reload(); 182 - }); 174 + } 183 175 }); 184 176 }); 185 177 186 178 $('#new_project_column_submit').on('click', (e) => { 187 179 e.preventDefault(); 188 - const columnTitle = $('#new_project_column'); 189 - const projectColorInput = $('#new_project_column_color_picker'); 190 - if (!columnTitle.val()) { 180 + const $columnTitle = $('#new_project_column'); 181 + const $projectColorInput = $('#new_project_column_color_picker'); 182 + if (!$columnTitle.val()) { 191 183 return; 192 184 } 193 - const url = $(this).data('url'); 194 - createNewColumn(url, columnTitle, projectColorInput); 185 + const url = e.target.getAttribute('data-url'); 186 + createNewColumn(url, $columnTitle, $projectColorInput); 195 187 }); 196 188 } 197 189
+1 -1
web_src/js/jquery.js
··· 1 1 import $ from 'jquery'; 2 2 3 - window.$ = window.jQuery = $; 3 + window.$ = window.jQuery = $; // eslint-disable-line no-jquery/variable-pattern
+3 -1
web_src/js/modules/fomantic/dropdown.js
··· 73 73 dropdownTemplates.menu = function(response, fields, preserveHTML, className) { 74 74 // when the dropdown menu items are loaded from AJAX requests, the items are created dynamically 75 75 const menuItems = dropdownTemplatesMenuOld(response, fields, preserveHTML, className); 76 - const $wrapper = $('<div>').append(menuItems); 76 + const div = document.createElement('div'); 77 + div.innerHTML = menuItems; 78 + const $wrapper = $(div); 77 79 const $items = $wrapper.find('> .item'); 78 80 $items.each((_, item) => updateMenuItem($dropdown[0], item)); 79 81 $dropdown[0][ariaPatchKey].deferredRefreshAriaActiveItem();
+6 -8
web_src/js/modules/tippy.js
··· 7 7 export function createTippy(target, opts = {}) { 8 8 // the callback functions should be destructured from opts, 9 9 // because we should use our own wrapper functions to handle them, do not let the user override them 10 - const {onHide, onShow, onDestroy, ...other} = opts; 10 + const {onHide, onShow, onDestroy, role, theme, ...other} = opts; 11 + 11 12 const instance = tippy(target, { 12 13 appendTo: document.body, 13 14 animation: false, ··· 35 36 return onShow?.(instance); 36 37 }, 37 38 arrow: `<svg width="16" height="7"><path d="m0 7 8-7 8 7Z" class="tippy-svg-arrow-outer"/><path d="m0 8 8-7 8 7Z" class="tippy-svg-arrow-inner"/></svg>`, 38 - role: 'menu', // HTML role attribute, only tooltips should use "tooltip" 39 - theme: other.role || 'menu', // CSS theme, either "tooltip", "menu" or "box-with-header" 39 + role: role || 'menu', // HTML role attribute 40 + theme: theme || role || 'menu', // CSS theme, either "tooltip", "menu" or "box-with-header" 40 41 plugins: [followCursor], 41 42 ...other, 42 43 }); 43 44 44 - // for popups where content refers to a DOM element, we use the 'tippy-target' class 45 - // to initially hide the content, now we can remove it as the content has been removed 46 - // from the DOM by tippy 47 - if (other.content instanceof Element) { 48 - other.content.classList.remove('tippy-target'); 45 + if (role === 'menu') { 46 + target.setAttribute('aria-haspopup', 'true'); 49 47 } 50 48 51 49 return instance;
+1 -1
web_src/js/webcomponents/GiteaOriginUrl.js web_src/js/webcomponents/origin-url.js
··· 15 15 return urlStr; 16 16 } 17 17 18 - window.customElements.define('gitea-origin-url', class extends HTMLElement { 18 + window.customElements.define('origin-url', class extends HTMLElement { 19 19 connectedCallback() { 20 20 this.textContent = toOriginUrl(this.getAttribute('data-url')); 21 21 }
+1 -1
web_src/js/webcomponents/GiteaOriginUrl.test.js web_src/js/webcomponents/origin-url.test.js
··· 1 - import {toOriginUrl} from './GiteaOriginUrl.js'; 1 + import {toOriginUrl} from './origin-url.js'; 2 2 3 3 test('toOriginUrl', () => { 4 4 const oldLocation = window.location;
+3 -4
web_src/js/webcomponents/README.md
··· 6 6 7 7 # Guidelines 8 8 9 - * These components are loaded in `<head>` (before DOM body), 10 - so they should have their own dependencies and should be very light, 11 - then they won't affect the page loading time too much. 12 - * If the component is not a public one, it's suggested to have its own `Gitea` or `gitea-` prefix to avoid conflicts. 9 + * These components are loaded in `<head>` (before DOM body) in a separate entry point, they need to be lightweight to not affect the page loading time too much. 10 + * Do not import `svg.js` into a web component because that file is currently not tree-shakeable, import svg files individually insteat. 11 + * All our components must be added to `webpack.config.js` so they work correctly in Vue.
+40
web_src/js/webcomponents/absolute-date.js
··· 1 + import {Temporal} from 'temporal-polyfill'; 2 + 3 + export function toAbsoluteLocaleDate(dateStr, lang, opts) { 4 + return Temporal.PlainDate.from(dateStr).toLocaleString(lang ?? [], opts); 5 + } 6 + 7 + window.customElements.define('absolute-date', class extends HTMLElement { 8 + static observedAttributes = ['date', 'year', 'month', 'weekday', 'day']; 9 + 10 + update = () => { 11 + const year = this.getAttribute('year') ?? ''; 12 + const month = this.getAttribute('month') ?? ''; 13 + const weekday = this.getAttribute('weekday') ?? ''; 14 + const day = this.getAttribute('day') ?? ''; 15 + const lang = this.closest('[lang]')?.getAttribute('lang') || 16 + this.ownerDocument.documentElement.getAttribute('lang') || ''; 17 + 18 + // only use the first 10 characters, e.g. the `yyyy-mm-dd` part 19 + const dateStr = this.getAttribute('date').substring(0, 10); 20 + 21 + if (!this.shadowRoot) this.attachShadow({mode: 'open'}); 22 + this.shadowRoot.textContent = toAbsoluteLocaleDate(dateStr, lang, { 23 + ...(year && {year}), 24 + ...(month && {month}), 25 + ...(weekday && {weekday}), 26 + ...(day && {day}), 27 + }); 28 + }; 29 + 30 + attributeChangedCallback(_name, oldValue, newValue) { 31 + if (!this.initialized || oldValue === newValue) return; 32 + this.update(); 33 + } 34 + 35 + connectedCallback() { 36 + this.initialized = false; 37 + this.update(); 38 + this.initialized = true; 39 + } 40 + });
+15
web_src/js/webcomponents/absolute-date.test.js
··· 1 + import {toAbsoluteLocaleDate} from './absolute-date.js'; 2 + 3 + test('toAbsoluteLocaleDate', () => { 4 + expect(toAbsoluteLocaleDate('2024-03-15', 'en-US', { 5 + year: 'numeric', 6 + month: 'long', 7 + day: 'numeric', 8 + })).toEqual('March 15, 2024'); 9 + 10 + expect(toAbsoluteLocaleDate('2024-03-15', 'de-DE', { 11 + year: 'numeric', 12 + month: 'long', 13 + day: 'numeric', 14 + })).toEqual('15. März 2024'); 15 + });
+5
web_src/js/webcomponents/index.js
··· 1 + import './polyfills.js'; 2 + import '@github/relative-time-element'; 3 + import './origin-url.js'; 4 + import './overflow-menu.js'; 5 + import './absolute-date.js';
+179
web_src/js/webcomponents/overflow-menu.js
··· 1 + import {throttle} from 'throttle-debounce'; 2 + import {createTippy} from '../modules/tippy.js'; 3 + import {isDocumentFragmentOrElementNode} from '../utils/dom.js'; 4 + import octiconKebabHorizontal from '../../../public/assets/img/svg/octicon-kebab-horizontal.svg'; 5 + 6 + window.customElements.define('overflow-menu', class extends HTMLElement { 7 + updateItems = throttle(100, () => { 8 + if (!this.tippyContent) { 9 + const div = document.createElement('div'); 10 + div.classList.add('tippy-target'); 11 + div.tabIndex = '-1'; // for initial focus, programmatic focus only 12 + div.addEventListener('keydown', (e) => { 13 + if (e.key === 'Tab') { 14 + const items = this.tippyContent.querySelectorAll('[role="menuitem"]'); 15 + if (e.shiftKey) { 16 + if (document.activeElement === items[0]) { 17 + e.preventDefault(); 18 + items[items.length - 1].focus(); 19 + } 20 + } else { 21 + if (document.activeElement === items[items.length - 1]) { 22 + e.preventDefault(); 23 + items[0].focus(); 24 + } 25 + } 26 + } else if (e.key === 'Escape') { 27 + e.preventDefault(); 28 + e.stopPropagation(); 29 + this.button._tippy.hide(); 30 + this.button.focus(); 31 + } else if (e.key === ' ' || e.code === 'Enter') { 32 + if (document.activeElement?.matches('[role="menuitem"]')) { 33 + e.preventDefault(); 34 + e.stopPropagation(); 35 + document.activeElement.click(); 36 + } 37 + } else if (e.key === 'ArrowDown') { 38 + if (document.activeElement?.matches('.tippy-target')) { 39 + e.preventDefault(); 40 + e.stopPropagation(); 41 + document.activeElement.querySelector('[role="menuitem"]:first-of-type').focus(); 42 + } else if (document.activeElement?.matches('[role="menuitem"]')) { 43 + e.preventDefault(); 44 + e.stopPropagation(); 45 + document.activeElement.nextElementSibling?.focus(); 46 + } 47 + } else if (e.key === 'ArrowUp') { 48 + if (document.activeElement?.matches('.tippy-target')) { 49 + e.preventDefault(); 50 + e.stopPropagation(); 51 + document.activeElement.querySelector('[role="menuitem"]:last-of-type').focus(); 52 + } else if (document.activeElement?.matches('[role="menuitem"]')) { 53 + e.preventDefault(); 54 + e.stopPropagation(); 55 + document.activeElement.previousElementSibling?.focus(); 56 + } 57 + } 58 + }); 59 + this.append(div); 60 + this.tippyContent = div; 61 + } 62 + 63 + // move items in tippy back into the menu items for subsequent measurement 64 + for (const item of this.tippyItems || []) { 65 + this.menuItemsEl.append(item); 66 + } 67 + 68 + // measure which items are partially outside the element and move them into the button menu 69 + this.tippyItems = []; 70 + const menuRight = this.offsetLeft + this.offsetWidth; 71 + const menuItems = this.menuItemsEl.querySelectorAll('.item'); 72 + for (const item of menuItems) { 73 + const itemRight = item.offsetLeft + item.offsetWidth; 74 + if (menuRight - itemRight < 38) { // roughly the width of .overflow-menu-button 75 + this.tippyItems.push(item); 76 + } 77 + } 78 + 79 + // if there are no overflown items, remove any previously created button 80 + if (!this.tippyItems?.length) { 81 + const btn = this.querySelector('.overflow-menu-button'); 82 + btn?._tippy?.destroy(); 83 + btn?.remove(); 84 + return; 85 + } 86 + 87 + // remove aria role from items that moved from tippy to menu 88 + for (const item of menuItems) { 89 + if (!this.tippyItems.includes(item)) { 90 + item.removeAttribute('role'); 91 + } 92 + } 93 + 94 + // move all items that overflow into tippy 95 + for (const item of this.tippyItems) { 96 + item.setAttribute('role', 'menuitem'); 97 + this.tippyContent.append(item); 98 + } 99 + 100 + // update existing tippy 101 + if (this.button?._tippy) { 102 + this.button._tippy.setContent(this.tippyContent); 103 + return; 104 + } 105 + 106 + // create button initially 107 + const btn = document.createElement('button'); 108 + btn.classList.add('overflow-menu-button', 'btn', 'tw-px-2', 'hover:tw-text-text-dark'); 109 + btn.setAttribute('aria-label', window.config.i18n.more_items); 110 + btn.innerHTML = octiconKebabHorizontal; 111 + this.append(btn); 112 + this.button = btn; 113 + 114 + createTippy(btn, { 115 + trigger: 'click', 116 + hideOnClick: true, 117 + interactive: true, 118 + placement: 'bottom-end', 119 + role: 'menu', 120 + content: this.tippyContent, 121 + onShow: () => { // FIXME: onShown doesn't work (never be called) 122 + setTimeout(() => { 123 + this.tippyContent.focus(); 124 + }, 0); 125 + }, 126 + }); 127 + }); 128 + 129 + init() { 130 + // ResizeObserver triggers on initial render, so we don't manually call `updateItems` here which 131 + // also avoids a full-page FOUC in Firefox that happens when `updateItems` is called too soon. 132 + this.resizeObserver = new ResizeObserver((entries) => { 133 + for (const entry of entries) { 134 + const newWidth = entry.contentBoxSize[0].inlineSize; 135 + if (newWidth !== this.lastWidth) { 136 + requestAnimationFrame(() => { 137 + this.updateItems(); 138 + }); 139 + this.lastWidth = newWidth; 140 + } 141 + } 142 + }); 143 + this.resizeObserver.observe(this); 144 + } 145 + 146 + connectedCallback() { 147 + this.setAttribute('role', 'navigation'); 148 + 149 + // check whether the mandatory `.overflow-menu-items` element is present initially which happens 150 + // with Vue which renders differently than browsers. If it's not there, like in the case of browser 151 + // template rendering, wait for its addition. 152 + // The eslint rule is not sophisticated enough or aware of this problem, see 153 + // https://github.com/43081j/eslint-plugin-wc/pull/130 154 + const menuItemsEl = this.querySelector('.overflow-menu-items'); // eslint-disable-line wc/no-child-traversal-in-connectedcallback 155 + if (menuItemsEl) { 156 + this.menuItemsEl = menuItemsEl; 157 + this.init(); 158 + } else { 159 + this.mutationObserver = new MutationObserver((mutations) => { 160 + for (const mutation of mutations) { 161 + for (const node of mutation.addedNodes) { 162 + if (!isDocumentFragmentOrElementNode(node)) continue; 163 + if (node.classList.contains('overflow-menu-items')) { 164 + this.menuItemsEl = node; 165 + this.mutationObserver?.disconnect(); 166 + this.init(); 167 + } 168 + } 169 + } 170 + }); 171 + this.mutationObserver.observe(this, {childList: true}); 172 + } 173 + } 174 + 175 + disconnectedCallback() { 176 + this.mutationObserver?.disconnect(); 177 + this.resizeObserver?.disconnect(); 178 + } 179 + });
web_src/js/webcomponents/polyfill.js web_src/js/webcomponents/polyfills.js
-5
web_src/js/webcomponents/webcomponents.js
··· 1 - import '@webcomponents/custom-elements'; // polyfill for some browsers like PaleMoon 2 - import './polyfill.js'; 3 - 4 - import '@github/relative-time-element'; 5 - import './GiteaOriginUrl.js';
+27 -5
webpack.config.js
··· 13 13 import {env} from 'node:process'; 14 14 import tailwindcss from 'tailwindcss'; 15 15 import tailwindConfig from './tailwind.config.js'; 16 + import tailwindcssNesting from 'tailwindcss/nesting/index.js'; 17 + import postcssNesting from 'postcss-nesting'; 16 18 17 19 const {EsbuildPlugin} = EsBuildLoader; 18 20 const {SourceMapDevToolPlugin, DefinePlugin} = webpack; ··· 41 43 sourceMaps = isProduction ? 'reduced' : 'true'; 42 44 } 43 45 46 + // define which web components we use for Vue to not interpret them as Vue components 47 + const webComponents = new Set([ 48 + // our own, in web_src/js/webcomponents 49 + 'overflow-menu', 50 + 'origin-url', 51 + 'absolute-date', 52 + // from dependencies 53 + 'markdown-toolbar', 54 + 'relative-time', 55 + 'text-expander', 56 + ]); 57 + 44 58 const filterCssImport = (url, ...args) => { 45 59 const cssFile = args[1] || args[0]; // resourcePath is 2nd argument for url and 3rd for import 46 60 const importedFile = url.replace(/[?#].+/, '').toLowerCase(); ··· 70 84 fileURLToPath(new URL('web_src/css/index.css', import.meta.url)), 71 85 ], 72 86 webcomponents: [ 73 - fileURLToPath(new URL('web_src/js/webcomponents/webcomponents.js', import.meta.url)), 87 + fileURLToPath(new URL('web_src/js/webcomponents/index.js', import.meta.url)), 74 88 ], 75 89 forgejoswagger: [ // Forgejo swagger is OpenAPI 3.0.0 and has specific parameters 76 90 fileURLToPath(new URL('web_src/js/standalone/forgejo-swagger.js', import.meta.url)), ··· 123 137 test: /\.vue$/i, 124 138 exclude: /node_modules/, 125 139 loader: 'vue-loader', 140 + options: { 141 + compilerOptions: { 142 + isCustomElement: (tag) => webComponents.has(tag), 143 + }, 144 + }, 126 145 }, 127 146 { 128 147 test: /\.js$/i, ··· 149 168 sourceMap: sourceMaps === 'true', 150 169 url: {filter: filterCssImport}, 151 170 import: {filter: filterCssImport}, 171 + importLoaders: 1, 152 172 }, 153 173 }, 154 174 { ··· 156 176 options: { 157 177 postcssOptions: { 158 178 map: false, // https://github.com/postcss/postcss/issues/1914 159 - plugins: [tailwindcss(tailwindConfig)], 179 + plugins: [ 180 + tailwindcssNesting(postcssNesting({edition: '2024-02'})), 181 + tailwindcss(tailwindConfig), 182 + ], 160 183 }, 161 184 }, 162 185 } ··· 224 247 }, 225 248 override: { 226 249 'khroma@*': {licenseName: 'MIT'}, // https://github.com/fabiospampinato/khroma/pull/33 227 - 'htmx.org@1.9.10': {licenseName: 'BSD-2-Clause'}, // "BSD 2-Clause" -> "BSD-2-Clause" 228 - 'idiomorph@0.3.0': {licenseName: 'BSD-2-Clause'}, // "BSD 2-Clause" -> "BSD-2-Clause" 250 + 'idiomorph@0.3.0': {licenseName: 'BSD-2-Clause'}, // https://github.com/bigskysoftware/idiomorph/pull/37 229 251 }, 230 252 emitError: true, 231 - allow: '(Apache-2.0 OR BSD-2-Clause OR BSD-3-Clause OR MIT OR ISC OR CPAL-1.0 OR Unlicense OR EPL-1.0 OR EPL-2.0)', 253 + allow: '(Apache-2.0 OR 0BSD OR BSD-2-Clause OR BSD-3-Clause OR MIT OR ISC OR CPAL-1.0 OR Unlicense OR EPL-1.0 OR EPL-2.0)', 232 254 }) : new AddAssetPlugin('licenses.txt', `Licenses are disabled during development`), 233 255 ], 234 256 performance: {