From 17c7ebb327faf6f8b6d659a0adb451b553405116 Mon Sep 17 00:00:00 2001 From: GiteaBot Date: Thu, 11 Apr 2024 00:24:56 +0000 Subject: [PATCH 01/35] [skip ci] Updated translations via Crowdin --- options/locale/locale_ja-JP.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/options/locale/locale_ja-JP.ini b/options/locale/locale_ja-JP.ini index 57b2aff2548f..0edd6c5dd706 100644 --- a/options/locale/locale_ja-JP.ini +++ b/options/locale/locale_ja-JP.ini @@ -2109,7 +2109,7 @@ settings.pulls.default_delete_branch_after_merge=デフォルトでプルリク settings.pulls.default_allow_edits_from_maintainers=デフォルトでメンテナからの編集を許可する settings.releases_desc=リリースを有効にする settings.packages_desc=リポジトリパッケージレジストリを有効にする -settings.projects_desc=リポジトリプロジェクトを有効にする +settings.projects_desc=プロジェクトを有効にする settings.projects_mode_all=すべてのプロジェクト settings.actions_desc=Actionsを有効にする settings.admin_settings=管理者用設定 From f0bfad29ea00eea7fd421d51352825aaa931aba8 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Thu, 11 Apr 2024 09:12:40 +0800 Subject: [PATCH 02/35] Replace MSSQL driver with a better maintained version (#30390) As the latest tag of `github.com/denisenkom/go-mssqldb` is in 2022, but as a fork of it, `github.com/microsoft/go-mssqldb` has more activities than the original repository. We can convert the driver to the fork. Since the interface of Go database driver are the same, it should have no any affect for the end users. --- assets/go-licenses.json | 15 ++++++++++----- go.mod | 2 +- go.sum | 28 ++++++++++++++++++---------- models/db/engine.go | 6 +++--- 4 files changed, 32 insertions(+), 19 deletions(-) diff --git a/assets/go-licenses.json b/assets/go-licenses.json index be9022b69465..ea73182a838d 100644 --- a/assets/go-licenses.json +++ b/assets/go-licenses.json @@ -304,11 +304,6 @@ "path": "github.com/davecgh/go-spew/spew/LICENSE", "licenseText": "ISC License\n\nCopyright (c) 2012-2016 Dave Collins \u003cdave@davec.name\u003e\n\nPermission to use, copy, modify, and/or distribute this software for any\npurpose with or without fee is hereby granted, provided that the above\ncopyright notice and this permission notice appear in all copies.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\nWITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\nMERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\nANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\nWHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\nACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\nOR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n" }, - { - "name": "github.com/denisenkom/go-mssqldb", - "path": "github.com/denisenkom/go-mssqldb/LICENSE.txt", - "licenseText": "Copyright (c) 2012 The Go Authors. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n * Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n * Redistributions in binary form must reproduce the above\ncopyright notice, this list of conditions and the following disclaimer\nin the documentation and/or other materials provided with the\ndistribution.\n * Neither the name of Google Inc. nor the names of its\ncontributors may be used to endorse or promote products derived from\nthis software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nOWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n" - }, { "name": "github.com/dgryski/go-rendezvous", "path": "github.com/dgryski/go-rendezvous/LICENSE", @@ -759,6 +754,16 @@ "path": "github.com/microcosm-cc/bluemonday/LICENSE.md", "licenseText": "SPDX short identifier: BSD-3-Clause\nhttps://opensource.org/licenses/BSD-3-Clause\n\nCopyright (c) 2014, David Kitchen \u003cdavid@buro9.com\u003e\n\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n* Redistributions of source code must retain the above copyright notice, this\n list of conditions and the following disclaimer.\n\n* Redistributions in binary form must reproduce the above copyright notice,\n this list of conditions and the following disclaimer in the documentation\n and/or other materials provided with the distribution.\n\n* Neither the name of the organisation (Microcosm) nor the names of its\n contributors may be used to endorse or promote products derived from\n this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\nFOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\nDAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\nSERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\nCAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\nOR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n" }, + { + "name": "github.com/microsoft/go-mssqldb", + "path": "github.com/microsoft/go-mssqldb/LICENSE.txt", + "licenseText": "Copyright (c) 2012 The Go Authors. All rights reserved.\nCopyright (c) Microsoft Corporation.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n * Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n * Redistributions in binary form must reproduce the above\ncopyright notice, this list of conditions and the following disclaimer\nin the documentation and/or other materials provided with the\ndistribution.\n * Neither the name of Google Inc. nor the names of its\ncontributors may be used to endorse or promote products derived from\nthis software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nOWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n" + }, + { + "name": "github.com/microsoft/go-mssqldb/internal/github.com/swisscom/mssql-always-encrypted/pkg", + "path": "github.com/microsoft/go-mssqldb/internal/github.com/swisscom/mssql-always-encrypted/pkg/LICENSE.txt", + "licenseText": "Copyright (c) 2021 Swisscom (Switzerland) Ltd\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software is furnished to do so,\nsubject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\nFOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\nCOPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\nIN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\nCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n\n" + }, { "name": "github.com/miekg/dns", "path": "github.com/miekg/dns/LICENSE", diff --git a/go.mod b/go.mod index 27e1924806d3..1e0f1ea8f87d 100644 --- a/go.mod +++ b/go.mod @@ -24,7 +24,6 @@ require ( github.com/buildkite/terminal-to-html/v3 v3.11.0 github.com/caddyserver/certmagic v0.20.0 github.com/chi-middleware/proxy v1.1.1 - github.com/denisenkom/go-mssqldb v0.12.3 github.com/dimiro1/reply v0.0.0-20200315094148-d0136a4c9e21 github.com/djherbis/buffer v1.2.0 github.com/djherbis/nio/v3 v3.0.1 @@ -77,6 +76,7 @@ require ( github.com/meilisearch/meilisearch-go v0.26.2 github.com/mholt/archiver/v3 v3.5.1 github.com/microcosm-cc/bluemonday v1.0.26 + github.com/microsoft/go-mssqldb v1.7.0 github.com/minio/minio-go/v7 v7.0.69 github.com/msteinert/pam v1.2.0 github.com/nektos/act v0.2.52 diff --git a/go.sum b/go.sum index 55f24bf2e7fd..864bed66770e 100644 --- a/go.sum +++ b/go.sum @@ -38,11 +38,20 @@ github.com/42wim/sshsig v0.0.0-20211121163825-841cf5bbc121 h1:r3qt8PCHnfjOv9PN3H github.com/42wim/sshsig v0.0.0-20211121163825-841cf5bbc121/go.mod h1:Ock8XgA7pvULhIaHGAk/cDnRfNrF9Jey81nPcc403iU= github.com/6543/go-version v1.3.1 h1:HvOp+Telns7HWJ2Xo/05YXQSB2bE0WmVgbHqwMPZT4U= github.com/6543/go-version v1.3.1/go.mod h1:oqFAHCwtLVUTLdhQmVZWYvaHXTdsbB4SY85at64SQEo= -github.com/Azure/azure-sdk-for-go/sdk/azcore v0.19.0/go.mod h1:h6H6c8enJmmocHUbLiiGY6sx7f9i+X3m1CHdd5c6Rdw= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v0.11.0/go.mod h1:HcM1YX14R7CJcghJGOYCgdezslRSVzqwLf/q+4Y2r/0= -github.com/Azure/azure-sdk-for-go/sdk/internal v0.7.0/go.mod h1:yqy467j36fJxcRV2TzfVZ1pCb5vxm4BtZPUdYWe/Xo8= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.1 h1:lGlwhPtrX6EVml1hO0ivjkUxsSyl4dsiw9qcA1k/3IQ= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.1/go.mod h1:RKUqNu35KJYcVG/fqTRqmuXJZYNhYkBrnC/hX7yGbTA= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.1 h1:sO0/P7g68FrryJzljemN+6GTssUXdANk6aJ7T1ZxnsQ= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.1/go.mod h1:h8hyGFDsU5HMivxiS2iYFZsgDbU9OnnJ163x5UGVKYo= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.1 h1:6oNBlSdi1QqM1PNW7FPA6xOGA5UNsXnkaYZz9vdPGhA= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.1/go.mod h1:s4kgfzA0covAXNicZHDMN58jExvcng2mC/DepXiF1EI= +github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.0.1 h1:MyVTgWR8qd/Jw1Le0NZebGBUCLbtak3bJ3z1OlqZBpw= +github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.0.1/go.mod h1:GpPjLhVR9dnUoJMyHWSPy71xY9/lcmpzIPZXmF0FCVY= +github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.0.0 h1:D3occbWoio4EBLkbkevetNMAVX197GkzbUMtqjGWn80= +github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.0.0/go.mod h1:bTSOgj05NGRuHHhQwAdPnYr9TOdNmKlZTgGLL6nyAdI= github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8= github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= +github.com/AzureAD/microsoft-authentication-library-for-go v1.2.1 h1:DzHpqpoJVaCgOUdVHxE8QB52S6NiVdDQvGlny1qvPqA= +github.com/AzureAD/microsoft-authentication-library-for-go v1.2.1/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/ClickHouse/ch-go v0.61.5 h1:zwR8QbYI0tsMiEcze/uIMK+Tz1D3XZXLdNrlaOpeEI4= github.com/ClickHouse/ch-go v0.61.5/go.mod h1:s1LJW/F/LcFs5HJnuogFMta50kKDO0lf9zzfrbl0RQg= @@ -220,7 +229,6 @@ github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55k github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI= github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= -github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 h1:iFaUwBSo5Svw6L7HYpRu/0lE3e0BaElwnNO1qkNQxBY= github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s= github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY= @@ -355,7 +363,6 @@ github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOW github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= -github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA= github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A= @@ -513,6 +520,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= @@ -551,6 +560,8 @@ github.com/mholt/archiver/v3 v3.5.1 h1:rDjOBX9JSF5BvoJGvjqK479aL70qh9DIpZCl+k7Cl github.com/mholt/archiver/v3 v3.5.1/go.mod h1:e3dqJ7H78uzsRSEACH1joayhuSyhnonssnDhppzS1L4= github.com/microcosm-cc/bluemonday v1.0.26 h1:xbqSvqzQMeEHCqMi64VAs4d8uy6Mequs3rQ0k/Khz58= github.com/microcosm-cc/bluemonday v1.0.26/go.mod h1:JyzOCs9gkyQyjs+6h10UEVSe02CGwkhd72Xdqh78TWs= +github.com/microsoft/go-mssqldb v1.7.0 h1:sgMPW0HA6Ihd37Yx0MzHyKD726C2kY/8KJsQtXHNaAs= +github.com/microsoft/go-mssqldb v1.7.0/go.mod h1:kOvZKUdrhhFQmxLZqbwUV0rHkNkZpthMITIb2Ko1IoA= github.com/miekg/dns v1.1.58 h1:ca2Hdkz+cDg/7eNF6V56jjzuZ4aCAE+DbVkILdQWG/4= github.com/miekg/dns v1.1.58/go.mod h1:Ypv+3b/KadlvW9vJfXOTf300O4UqaHFzFCuHz+rPkBY= github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34= @@ -574,7 +585,6 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8= github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= github.com/mrjones/oauth v0.0.0-20190623134757-126b35219450 h1:j2kD3MT1z4PXCiUllUJF9mWUESr9TWKS7iEKsQ/IipM= github.com/mrjones/oauth v0.0.0-20190623134757-126b35219450/go.mod h1:skjdDftzkFALcuGzYSklqYd8gvat6F1gZJ4YPVbkZpM= @@ -627,7 +637,8 @@ github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4= github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI= -github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA= +github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= +github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -836,7 +847,6 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= @@ -871,7 +881,6 @@ golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= @@ -1022,7 +1031,6 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI= diff --git a/models/db/engine.go b/models/db/engine.go index 2a2743e92798..8684c4e2f162 100755 --- a/models/db/engine.go +++ b/models/db/engine.go @@ -21,9 +21,9 @@ import ( "xorm.io/xorm/names" "xorm.io/xorm/schemas" - _ "github.com/denisenkom/go-mssqldb" // Needed for the MSSQL driver - _ "github.com/go-sql-driver/mysql" // Needed for the MySQL driver - _ "github.com/lib/pq" // Needed for the Postgresql driver + _ "github.com/go-sql-driver/mysql" // Needed for the MySQL driver + _ "github.com/lib/pq" // Needed for the Postgresql driver + _ "github.com/microsoft/go-mssqldb" // Needed for the MSSQL driver ) var ( From e6d3f9fc07d193ce95cf0964f0d12da87156fac9 Mon Sep 17 00:00:00 2001 From: silverwind Date: Thu, 11 Apr 2024 04:40:03 +0200 Subject: [PATCH 03/35] Upgrade golangci-lint to v1.57.2 (#30401) Update and adapt to one setting [deprecation](https://github.com/golangci/golangci-lint/pull/4509). --- .golangci.yml | 5 +---- Makefile | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index d6ce37f49afa..5be2cefe44b7 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -30,10 +30,6 @@ linters: run: timeout: 10m - skip-dirs: - - node_modules - - public - - web_src linters-settings: stylecheck: @@ -94,6 +90,7 @@ linters-settings: issues: max-issues-per-linter: 0 max-same-issues: 0 + exclude-dirs: [node_modules, public, web_src] exclude-rules: # Exclude some linters from running on tests files. - path: _test\.go diff --git a/Makefile b/Makefile index 8489520920c3..ee9c90e8d972 100644 --- a/Makefile +++ b/Makefile @@ -28,7 +28,7 @@ XGO_VERSION := go-1.22.x AIR_PACKAGE ?= github.com/cosmtrek/air@v1.49.0 EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-checker/cmd/editorconfig-checker@2.7.0 GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@v0.6.0 -GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/cmd/golangci-lint@v1.56.1 +GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/cmd/golangci-lint@v1.57.2 GXZ_PACKAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.11 MISSPELL_PACKAGE ?= github.com/golangci/misspell/cmd/misspell@v0.4.1 SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@db51e79a0e37c572d8b59ae0c58bf2bbbbe53285 From 50dbed652738182eb42af51967ec7bd10e84ede9 Mon Sep 17 00:00:00 2001 From: silverwind Date: Thu, 11 Apr 2024 05:16:44 +0200 Subject: [PATCH 04/35] Fix author name alignment in commits table (#30396) Fixes https://github.com/go-gitea/gitea/issues/30129 by introducing a wrapper div with flexbox that collapses any inter-tag whitespace within. View diff with whitespace hidden. Author names aligned: Screenshot 2024-04-10 at 19 41 27 Vertically centered on expand: Screenshot 2024-04-10 at 19 43 02 Ellipsis works: image --- templates/repo/commits_list.tmpl | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/templates/repo/commits_list.tmpl b/templates/repo/commits_list.tmpl index be73c4ca18e8..bb5d2a039477 100644 --- a/templates/repo/commits_list.tmpl +++ b/templates/repo/commits_list.tmpl @@ -13,17 +13,19 @@ {{$commitRepoLink := $.RepoLink}}{{if $.CommitRepoLink}}{{$commitRepoLink = $.CommitRepoLink}}{{end}} {{range .Commits}} - - {{$userName := .Author.Name}} - {{if .User}} - {{if and .User.FullName DefaultShowFullName}} - {{$userName = .User.FullName}} + +
+ {{$userName := .Author.Name}} + {{if .User}} + {{if and .User.FullName DefaultShowFullName}} + {{$userName = .User.FullName}} + {{end}} + {{ctx.AvatarUtils.Avatar .User 28 "tw-mr-2"}}{{$userName}} + {{else}} + {{ctx.AvatarUtils.AvatarByEmail .Author.Email .Author.Name 28 "tw-mr-2"}} + {{$userName}} {{end}} - {{ctx.AvatarUtils.Avatar .User 28 "tw-mr-2"}}{{$userName}} - {{else}} - {{ctx.AvatarUtils.AvatarByEmail .Author.Email .Author.Name 28 "tw-mr-2"}} - {{$userName}} - {{end}} +
{{$class := "ui sha label"}} From f3cc00626b5a170e193961b885d4e60088ef7d9b Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Thu, 11 Apr 2024 11:57:03 +0800 Subject: [PATCH 05/35] Update actions variables documents (#30394) Fix #30393 --------- Co-authored-by: silverwind Co-authored-by: Zettat123 --- .../content/usage/actions/act-runner.en-us.md | 31 -------------- .../content/usage/actions/act-runner.zh-cn.md | 29 ------------- docs/content/usage/actions/variables.en-us.md | 41 +++++++++++++++++++ docs/content/usage/actions/variables.zh-cn.md | 39 ++++++++++++++++++ 4 files changed, 80 insertions(+), 60 deletions(-) create mode 100644 docs/content/usage/actions/variables.en-us.md create mode 100644 docs/content/usage/actions/variables.zh-cn.md diff --git a/docs/content/usage/actions/act-runner.en-us.md b/docs/content/usage/actions/act-runner.en-us.md index b2806bf5dd32..942d12691928 100644 --- a/docs/content/usage/actions/act-runner.en-us.md +++ b/docs/content/usage/actions/act-runner.en-us.md @@ -303,34 +303,3 @@ sudo systemctl enable act_runner --now ``` If using Docker, the `act_runner` user should also be added to the `docker` group before starting the service. Keep in mind that this effectively gives `act_runner` root access to the system [[1]](https://docs.docker.com/engine/security/#docker-daemon-attack-surface). - -## Configuration variable - -You can create configuration variables on the user, organization and repository level. -The level of the variable depends on where you created it. - -### Naming conventions - -The following rules apply to variable names: - -- Variable names can only contain alphanumeric characters (`[a-z]`, `[A-Z]`, `[0-9]`) or underscores (`_`). Spaces are not allowed. - -- Variable names must not start with the `GITHUB_` and `GITEA_` prefix. - -- Variable names must not start with a number. - -- Variable names are case-insensitive. - -- Variable names must be unique at the level they are created at. - -- Variable names must not be `CI`. - -### Using variable - -After creating configuration variables, they will be automatically filled in the `vars` context. -They can be accessed through expressions like `{{ vars.VARIABLE_NAME }}` in the workflow. - -### Precedence - -If a variable with the same name exists at multiple levels, the variable at the lowest level takes precedence: -A repository variable will always be chosen over an organization/user variable. diff --git a/docs/content/usage/actions/act-runner.zh-cn.md b/docs/content/usage/actions/act-runner.zh-cn.md index 274b0f06922e..e5ebff976d9c 100644 --- a/docs/content/usage/actions/act-runner.zh-cn.md +++ b/docs/content/usage/actions/act-runner.zh-cn.md @@ -258,32 +258,3 @@ Runner的标签用于确定Runner可以运行哪些Job以及如何运行它们 Runner将从Gitea实例获取Job并自动运行它们。 由于Act Runner仍处于开发中,建议定期检查最新版本并进行升级。 - -## 变量 - -您可以创建用户、组织和仓库级别的变量。变量的级别取决于创建它的位置。 - -### 命名规则 - -以下规则适用于变量名: - -- 变量名称只能包含字母数字字符 (`[a-z]`, `[A-Z]`, `[0-9]`) 或下划线 (`_`)。不允许使用空格。 - -- 变量名称不能以 `GITHUB_` 和 `GITEA_` 前缀开头。 - -- 变量名称不能以数字开头。 - -- 变量名称不区分大小写。 - -- 变量名称在创建它们的级别上必须是唯一的。 - -- 变量名称不能为 “CI”。 - -### 使用 - -创建配置变量后,它们将自动填充到 `vars` 上下文中。您可以在工作流中使用类似 `{{ vars.VARIABLE_NAME }}` 这样的表达式来使用它们。 - -### 优先级 - -如果同名变量存在于多个级别,则级别最低的变量优先。 -仓库级别的变量总是比组织或者用户级别的变量优先被选中。 diff --git a/docs/content/usage/actions/variables.en-us.md b/docs/content/usage/actions/variables.en-us.md new file mode 100644 index 000000000000..dee2e74234d5 --- /dev/null +++ b/docs/content/usage/actions/variables.en-us.md @@ -0,0 +1,41 @@ +--- +date: "2024-04-10T22:21:00+08:00" +title: "Variables" +slug: "actions-variables" +sidebar_position: 25 +draft: false +toc: false +menu: + sidebar: + parent: "actions" + name: "Variables" + sidebar_position: 25 + identifier: "actions-variables" +--- + +## Variables + +You can create configuration variables on the user, organization and repository level. +The level of the variable depends on where you created it. When creating a variable, the +key will be converted to uppercase. You need use uppercase on the yaml file. + +### Naming conventions + +The following rules apply to variable names: + +- Variable names can only contain alphanumeric characters (`[a-z]`, `[A-Z]`, `[0-9]`) or underscores (`_`). Spaces are not allowed. +- Variable names must not start with the `GITHUB_` and `GITEA_` prefix. +- Variable names must not start with a number. +- Variable names are case-insensitive. +- Variable names must be unique at the level they are created at. +- Variable names must not be `CI`. + +### Using variable + +After creating configuration variables, they will be automatically filled in the `vars` context. +They can be accessed through expressions like `${{ vars.VARIABLE_NAME }}` in the workflow. + +### Precedence + +If a variable with the same name exists at multiple levels, the variable at the lowest level takes precedence: +A repository variable will always be chosen over an organization/user variable. diff --git a/docs/content/usage/actions/variables.zh-cn.md b/docs/content/usage/actions/variables.zh-cn.md new file mode 100644 index 000000000000..77643408a182 --- /dev/null +++ b/docs/content/usage/actions/variables.zh-cn.md @@ -0,0 +1,39 @@ +--- +date: "2024-04-10T22:21:00+08:00" +title: "变量" +slug: "actions-variables" +sidebar_position: 25 +draft: false +toc: false +menu: + sidebar: + parent: "actions" + name: "变量" + sidebar_position: 25 + identifier: "actions-variables" +--- + +## 变量 + +您可以创建用户、组织和仓库级别的变量。变量的级别取决于创建它的位置。当创建变量时,变量的名称会被 +转换为大写,在yaml文件中引用时需要使用大写。 + +### 命名规则 + +以下规则适用于变量名: + +- 变量名称只能包含字母数字字符 (`[a-z]`, `[A-Z]`, `[0-9]`) 或下划线 (`_`)。不允许使用空格。 +- 变量名称不能以 `GITHUB_` 和 `GITEA_` 前缀开头。 +- 变量名称不能以数字开头。 +- 变量名称不区分大小写。 +- 变量名称在创建它们的级别上必须是唯一的。 +- 变量名称不能为 `CI`。 + +### 使用 + +创建配置变量后,它们将自动填充到 `vars` 上下文中。您可以在工作流中使用类似 `${{ vars.VARIABLE_NAME }}` 这样的表达式来使用它们。 + +### 优先级 + +如果同名变量存在于多个级别,则级别最低的变量优先。 +仓库级别的变量总是比组织或者用户级别的变量优先被选中。 From 96d31fe0a8b88c09488989cd5459d4124dcb7983 Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Thu, 11 Apr 2024 16:11:32 +0900 Subject: [PATCH 06/35] Avoid user does not exist error when detecting schedule actions when the commit author is an external user (#30357) ![image](https://github.com/go-gitea/gitea/assets/18380374/ddf6ee84-2242-49b9-b066-bd8429ba4d76) When repo is a mirror, and commit author is an external user, then `GetUserByEmail` will return error. reproduce/test: - mirror Gitea to your instance - disable action and enable it again, this will trigger `DetectAndHandleSchedules` ps: also follow #24706, it only fixed normal runs, not scheduled runs. --- models/actions/schedule_list.go | 3 +++ services/actions/notifier_helper.go | 9 +++------ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/models/actions/schedule_list.go b/models/actions/schedule_list.go index 1d35adc42082..5361b94801aa 100644 --- a/models/actions/schedule_list.go +++ b/models/actions/schedule_list.go @@ -40,6 +40,9 @@ func (schedules ScheduleList) LoadTriggerUser(ctx context.Context) error { schedule.TriggerUser = user_model.NewActionsUser() } else { schedule.TriggerUser = users[schedule.TriggerUserID] + if schedule.TriggerUser == nil { + schedule.TriggerUser = user_model.NewGhostUser() + } } } return nil diff --git a/services/actions/notifier_helper.go b/services/actions/notifier_helper.go index 8c98f56af53e..c48886a8246e 100644 --- a/services/actions/notifier_helper.go +++ b/services/actions/notifier_helper.go @@ -525,12 +525,9 @@ func DetectAndHandleSchedules(ctx context.Context, repo *repo_model.Repository) } // We need a notifyInput to call handleSchedules - // Here we use the commit author as the Doer of the notifyInput - commitUser, err := user_model.GetUserByEmail(ctx, commit.Author.Email) - if err != nil { - return fmt.Errorf("get user by email: %w", err) - } - notifyInput := newNotifyInput(repo, commitUser, webhook_module.HookEventSchedule) + // if repo is a mirror, commit author maybe an external user, + // so we use action user as the Doer of the notifyInput + notifyInput := newNotifyInput(repo, user_model.NewActionsUser(), webhook_module.HookEventSchedule) return handleSchedules(ctx, scheduleWorkflows, commit, notifyInput, repo.DefaultBranch) } From 0fe9f93eb4c94d55e43b18b9c3cc6d513a34c0b5 Mon Sep 17 00:00:00 2001 From: Zettat123 Date: Thu, 11 Apr 2024 16:01:44 +0800 Subject: [PATCH 07/35] Check the token's owner and repository when registering a runner (#30406) Fix #30378 --- models/organization/org.go | 3 +++ routers/api/actions/runner/runner.go | 14 ++++++++++++++ services/repository/delete.go | 1 + services/user/delete.go | 1 + 4 files changed, 19 insertions(+) diff --git a/models/organization/org.go b/models/organization/org.go index ba0fd756e38f..b33d15d29c12 100644 --- a/models/organization/org.go +++ b/models/organization/org.go @@ -9,6 +9,7 @@ import ( "fmt" "strings" + actions_model "code.gitea.io/gitea/models/actions" "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/perm" repo_model "code.gitea.io/gitea/models/repo" @@ -402,6 +403,8 @@ func DeleteOrganization(ctx context.Context, org *Organization) error { &TeamInvite{OrgID: org.ID}, &secret_model.Secret{OwnerID: org.ID}, &user_model.Blocking{BlockerID: org.ID}, + &actions_model.ActionRunner{OwnerID: org.ID}, + &actions_model.ActionRunnerToken{OwnerID: org.ID}, ); err != nil { return fmt.Errorf("DeleteBeans: %w", err) } diff --git a/routers/api/actions/runner/runner.go b/routers/api/actions/runner/runner.go index 1d07be3aecd1..b2f3e7af781e 100644 --- a/routers/api/actions/runner/runner.go +++ b/routers/api/actions/runner/runner.go @@ -9,6 +9,8 @@ import ( "net/http" actions_model "code.gitea.io/gitea/models/actions" + repo_model "code.gitea.io/gitea/models/repo" + user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/actions" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/util" @@ -52,6 +54,18 @@ func (s *Service) Register( return nil, errors.New("runner registration token has been invalidated, please use the latest one") } + if runnerToken.OwnerID > 0 { + if _, err := user_model.GetUserByID(ctx, runnerToken.OwnerID); err != nil { + return nil, errors.New("owner of the token not found") + } + } + + if runnerToken.RepoID > 0 { + if _, err := repo_model.GetRepositoryByID(ctx, runnerToken.RepoID); err != nil { + return nil, errors.New("repository of the token not found") + } + } + labels := req.Msg.Labels // TODO: agent_labels should be removed from pb after Gitea 1.20 released. // Old version runner's agent_labels slice is not empty and labels slice is empty. diff --git a/services/repository/delete.go b/services/repository/delete.go index 8d6729f31bc8..7c7dfe2dddb2 100644 --- a/services/repository/delete.go +++ b/services/repository/delete.go @@ -163,6 +163,7 @@ func DeleteRepositoryDirectly(ctx context.Context, doer *user_model.User, repoID &actions_model.ActionScheduleSpec{RepoID: repoID}, &actions_model.ActionSchedule{RepoID: repoID}, &actions_model.ActionArtifact{RepoID: repoID}, + &actions_model.ActionRunnerToken{RepoID: repoID}, ); err != nil { return fmt.Errorf("deleteBeans: %w", err) } diff --git a/services/user/delete.go b/services/user/delete.go index 212cb83e0311..889da3eb6749 100644 --- a/services/user/delete.go +++ b/services/user/delete.go @@ -94,6 +94,7 @@ func deleteUser(ctx context.Context, u *user_model.User, purge bool) (err error) &actions_model.ActionRunner{OwnerID: u.ID}, &user_model.Blocking{BlockerID: u.ID}, &user_model.Blocking{BlockeeID: u.ID}, + &actions_model.ActionRunnerToken{OwnerID: u.ID}, ); err != nil { return fmt.Errorf("deleteBeans: %w", err) } From 26ee66327fecf2f1755a47f9193bc6305132def1 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Fri, 12 Apr 2024 02:22:59 +0800 Subject: [PATCH 08/35] Split `issue edit` code from `repo-legacy.js` into its own file (#30419) Follow Split `index.js` to separate files (#17315) It's time to move some code away from the messy "legacy" file. --- web_src/js/features/repo-issue-edit.js | 206 ++++++++++++++++++++++++ web_src/js/features/repo-legacy.js | 207 +------------------------ 2 files changed, 209 insertions(+), 204 deletions(-) create mode 100644 web_src/js/features/repo-issue-edit.js diff --git a/web_src/js/features/repo-issue-edit.js b/web_src/js/features/repo-issue-edit.js new file mode 100644 index 000000000000..4c03325c7a5f --- /dev/null +++ b/web_src/js/features/repo-issue-edit.js @@ -0,0 +1,206 @@ +import $ from 'jquery'; +import {handleReply} from './repo-issue.js'; +import {getComboMarkdownEditor, initComboMarkdownEditor} from './comp/ComboMarkdownEditor.js'; +import {createDropzone} from './dropzone.js'; +import {GET, POST} from '../modules/fetch.js'; +import {hideElem, showElem} from '../utils/dom.js'; +import {attachRefIssueContextPopup} from './contextpopup.js'; +import {initCommentContent, initMarkupContent} from '../markup/content.js'; + +const {csrfToken} = window.config; + +async function onEditContent(event) { + event.preventDefault(); + + const segment = this.closest('.header').nextElementSibling; + const editContentZone = segment.querySelector('.edit-content-zone'); + const renderContent = segment.querySelector('.render-content'); + const rawContent = segment.querySelector('.raw-content'); + + let comboMarkdownEditor; + + /** + * @param {HTMLElement} dropzone + */ + const setupDropzone = async (dropzone) => { + if (!dropzone) return null; + + let disableRemovedfileEvent = false; // when resetting the dropzone (removeAllFiles), disable the "removedfile" event + let fileUuidDict = {}; // to record: if a comment has been saved, then the uploaded files won't be deleted from server when clicking the Remove in the dropzone + const dz = await createDropzone(dropzone, { + url: dropzone.getAttribute('data-upload-url'), + headers: {'X-Csrf-Token': csrfToken}, + maxFiles: dropzone.getAttribute('data-max-file'), + maxFilesize: dropzone.getAttribute('data-max-size'), + acceptedFiles: ['*/*', ''].includes(dropzone.getAttribute('data-accepts')) ? null : dropzone.getAttribute('data-accepts'), + addRemoveLinks: true, + dictDefaultMessage: dropzone.getAttribute('data-default-message'), + dictInvalidFileType: dropzone.getAttribute('data-invalid-input-type'), + dictFileTooBig: dropzone.getAttribute('data-file-too-big'), + dictRemoveFile: dropzone.getAttribute('data-remove-file'), + timeout: 0, + thumbnailMethod: 'contain', + thumbnailWidth: 480, + thumbnailHeight: 480, + init() { + this.on('success', (file, data) => { + file.uuid = data.uuid; + fileUuidDict[file.uuid] = {submitted: false}; + const input = document.createElement('input'); + input.id = data.uuid; + input.name = 'files'; + input.type = 'hidden'; + input.value = data.uuid; + dropzone.querySelector('.files').append(input); + }); + this.on('removedfile', async (file) => { + document.getElementById(file.uuid)?.remove(); + if (disableRemovedfileEvent) return; + if (dropzone.getAttribute('data-remove-url') && !fileUuidDict[file.uuid].submitted) { + try { + await POST(dropzone.getAttribute('data-remove-url'), {data: new URLSearchParams({file: file.uuid})}); + } catch (error) { + console.error(error); + } + } + }); + this.on('submit', () => { + for (const fileUuid of Object.keys(fileUuidDict)) { + fileUuidDict[fileUuid].submitted = true; + } + }); + this.on('reload', async () => { + try { + const response = await GET(editContentZone.getAttribute('data-attachment-url')); + const data = await response.json(); + // do not trigger the "removedfile" event, otherwise the attachments would be deleted from server + disableRemovedfileEvent = true; + dz.removeAllFiles(true); + dropzone.querySelector('.files').innerHTML = ''; + for (const el of dropzone.querySelectorAll('.dz-preview')) el.remove(); + fileUuidDict = {}; + disableRemovedfileEvent = false; + + for (const attachment of data) { + const imgSrc = `${dropzone.getAttribute('data-link-url')}/${attachment.uuid}`; + dz.emit('addedfile', attachment); + dz.emit('thumbnail', attachment, imgSrc); + dz.emit('complete', attachment); + fileUuidDict[attachment.uuid] = {submitted: true}; + dropzone.querySelector(`img[src='${imgSrc}']`).style.maxWidth = '100%'; + const input = document.createElement('input'); + input.id = attachment.uuid; + input.name = 'files'; + input.type = 'hidden'; + input.value = attachment.uuid; + dropzone.querySelector('.files').append(input); + } + if (!dropzone.querySelector('.dz-preview')) { + dropzone.classList.remove('dz-started'); + } + } catch (error) { + console.error(error); + } + }); + }, + }); + dz.emit('reload'); + return dz; + }; + + const cancelAndReset = (e) => { + e.preventDefault(); + showElem(renderContent); + hideElem(editContentZone); + comboMarkdownEditor.attachedDropzoneInst?.emit('reload'); + }; + + const saveAndRefresh = async (e) => { + e.preventDefault(); + showElem(renderContent); + hideElem(editContentZone); + const dropzoneInst = comboMarkdownEditor.attachedDropzoneInst; + try { + const params = new URLSearchParams({ + content: comboMarkdownEditor.value(), + context: editContentZone.getAttribute('data-context'), + }); + for (const fileInput of dropzoneInst?.element.querySelectorAll('.files [name=files]')) params.append('files[]', fileInput.value); + + const response = await POST(editContentZone.getAttribute('data-update-url'), {data: params}); + const data = await response.json(); + if (!data.content) { + renderContent.innerHTML = document.getElementById('no-content').innerHTML; + rawContent.textContent = ''; + } else { + renderContent.innerHTML = data.content; + rawContent.textContent = comboMarkdownEditor.value(); + const refIssues = renderContent.querySelectorAll('p .ref-issue'); + attachRefIssueContextPopup(refIssues); + } + const content = segment; + if (!content.querySelector('.dropzone-attachments')) { + if (data.attachments !== '') { + content.insertAdjacentHTML('beforeend', data.attachments); + } + } else if (data.attachments === '') { + content.querySelector('.dropzone-attachments').remove(); + } else { + content.querySelector('.dropzone-attachments').outerHTML = data.attachments; + } + dropzoneInst?.emit('submit'); + dropzoneInst?.emit('reload'); + initMarkupContent(); + initCommentContent(); + } catch (error) { + console.error(error); + } + }; + + comboMarkdownEditor = getComboMarkdownEditor(editContentZone.querySelector('.combo-markdown-editor')); + if (!comboMarkdownEditor) { + editContentZone.innerHTML = document.getElementById('issue-comment-editor-template').innerHTML; + comboMarkdownEditor = await initComboMarkdownEditor(editContentZone.querySelector('.combo-markdown-editor')); + comboMarkdownEditor.attachedDropzoneInst = await setupDropzone(editContentZone.querySelector('.dropzone')); + editContentZone.querySelector('.cancel.button').addEventListener('click', cancelAndReset); + editContentZone.querySelector('.save.button').addEventListener('click', saveAndRefresh); + } + + // Show write/preview tab and copy raw content as needed + showElem(editContentZone); + hideElem(renderContent); + if (!comboMarkdownEditor.value()) { + comboMarkdownEditor.value(rawContent.textContent); + } + comboMarkdownEditor.focus(); +} + +export function initRepoIssueCommentEdit() { + // Edit issue or comment content + $(document).on('click', '.edit-content', onEditContent); + + // Quote reply + $(document).on('click', '.quote-reply', async function (event) { + event.preventDefault(); + const target = $(this).data('target'); + const quote = $(`#${target}`).text().replace(/\n/g, '\n> '); + const content = `> ${quote}\n\n`; + let editor; + if ($(this).hasClass('quote-reply-diff')) { + const $replyBtn = $(this).closest('.comment-code-cloud').find('button.comment-form-reply'); + editor = await handleReply($replyBtn); + } else { + // for normal issue/comment page + editor = getComboMarkdownEditor($('#comment-form .combo-markdown-editor')); + } + if (editor) { + if (editor.value()) { + editor.value(`${editor.value()}\n\n${content}`); + } else { + editor.value(content); + } + editor.focus(); + editor.moveCursorToEnd(); + } + }); +} diff --git a/web_src/js/features/repo-legacy.js b/web_src/js/features/repo-legacy.js index 4c7dd36920d0..e83de27e4cf4 100644 --- a/web_src/js/features/repo-legacy.js +++ b/web_src/js/features/repo-legacy.js @@ -3,7 +3,7 @@ import { initRepoIssueBranchSelect, initRepoIssueCodeCommentCancel, initRepoIssueCommentDelete, initRepoIssueComments, initRepoIssueDependencyDelete, initRepoIssueReferenceIssue, initRepoIssueTitleEdit, initRepoIssueWipToggle, - initRepoPullRequestUpdate, updateIssuesMeta, handleReply, initIssueTemplateCommentEditors, initSingleCommentEditor, + initRepoPullRequestUpdate, updateIssuesMeta, initIssueTemplateCommentEditors, initSingleCommentEditor, } from './repo-issue.js'; import {initUnicodeEscapeButton} from './repo-unicode-escape.js'; import {svg} from '../svg.js'; @@ -15,18 +15,13 @@ import { import {initCitationFileCopyContent} from './citation.js'; import {initCompLabelEdit} from './comp/LabelEdit.js'; import {initRepoDiffConversationNav} from './repo-diff.js'; -import {createDropzone} from './dropzone.js'; -import {initCommentContent, initMarkupContent} from '../markup/content.js'; import {initCompReactionSelector} from './comp/ReactionSelector.js'; import {initRepoSettingBranches} from './repo-settings.js'; import {initRepoPullRequestMergeForm} from './repo-issue-pr-form.js'; import {initRepoPullRequestCommitStatus} from './repo-issue-pr-status.js'; import {hideElem, showElem} from '../utils/dom.js'; -import {getComboMarkdownEditor, initComboMarkdownEditor} from './comp/ComboMarkdownEditor.js'; -import {attachRefIssueContextPopup} from './contextpopup.js'; -import {POST, GET} from '../modules/fetch.js'; - -const {csrfToken} = window.config; +import {POST} from '../modules/fetch.js'; +import {initRepoIssueCommentEdit} from './repo-issue-edit.js'; // if there are draft comments, confirm before reloading, to avoid losing comments function reloadConfirmDraftComment() { @@ -316,172 +311,6 @@ export function initRepoCommentForm() { selectItem('.select-assignee', '#assignee_id'); } -async function onEditContent(event) { - event.preventDefault(); - - const segment = this.closest('.header').nextElementSibling; - const editContentZone = segment.querySelector('.edit-content-zone'); - const renderContent = segment.querySelector('.render-content'); - const rawContent = segment.querySelector('.raw-content'); - - let comboMarkdownEditor; - - /** - * @param {HTMLElement} dropzone - */ - const setupDropzone = async (dropzone) => { - if (!dropzone) return null; - - let disableRemovedfileEvent = false; // when resetting the dropzone (removeAllFiles), disable the "removedfile" event - let fileUuidDict = {}; // to record: if a comment has been saved, then the uploaded files won't be deleted from server when clicking the Remove in the dropzone - const dz = await createDropzone(dropzone, { - url: dropzone.getAttribute('data-upload-url'), - headers: {'X-Csrf-Token': csrfToken}, - maxFiles: dropzone.getAttribute('data-max-file'), - maxFilesize: dropzone.getAttribute('data-max-size'), - acceptedFiles: ['*/*', ''].includes(dropzone.getAttribute('data-accepts')) ? null : dropzone.getAttribute('data-accepts'), - addRemoveLinks: true, - dictDefaultMessage: dropzone.getAttribute('data-default-message'), - dictInvalidFileType: dropzone.getAttribute('data-invalid-input-type'), - dictFileTooBig: dropzone.getAttribute('data-file-too-big'), - dictRemoveFile: dropzone.getAttribute('data-remove-file'), - timeout: 0, - thumbnailMethod: 'contain', - thumbnailWidth: 480, - thumbnailHeight: 480, - init() { - this.on('success', (file, data) => { - file.uuid = data.uuid; - fileUuidDict[file.uuid] = {submitted: false}; - const input = document.createElement('input'); - input.id = data.uuid; - input.name = 'files'; - input.type = 'hidden'; - input.value = data.uuid; - dropzone.querySelector('.files').append(input); - }); - this.on('removedfile', async (file) => { - document.getElementById(file.uuid)?.remove(); - if (disableRemovedfileEvent) return; - if (dropzone.getAttribute('data-remove-url') && !fileUuidDict[file.uuid].submitted) { - try { - await POST(dropzone.getAttribute('data-remove-url'), {data: new URLSearchParams({file: file.uuid})}); - } catch (error) { - console.error(error); - } - } - }); - this.on('submit', () => { - for (const fileUuid of Object.keys(fileUuidDict)) { - fileUuidDict[fileUuid].submitted = true; - } - }); - this.on('reload', async () => { - try { - const response = await GET(editContentZone.getAttribute('data-attachment-url')); - const data = await response.json(); - // do not trigger the "removedfile" event, otherwise the attachments would be deleted from server - disableRemovedfileEvent = true; - dz.removeAllFiles(true); - dropzone.querySelector('.files').innerHTML = ''; - for (const el of dropzone.querySelectorAll('.dz-preview')) el.remove(); - fileUuidDict = {}; - disableRemovedfileEvent = false; - - for (const attachment of data) { - const imgSrc = `${dropzone.getAttribute('data-link-url')}/${attachment.uuid}`; - dz.emit('addedfile', attachment); - dz.emit('thumbnail', attachment, imgSrc); - dz.emit('complete', attachment); - fileUuidDict[attachment.uuid] = {submitted: true}; - dropzone.querySelector(`img[src='${imgSrc}']`).style.maxWidth = '100%'; - const input = document.createElement('input'); - input.id = attachment.uuid; - input.name = 'files'; - input.type = 'hidden'; - input.value = attachment.uuid; - dropzone.querySelector('.files').append(input); - } - if (!dropzone.querySelector('.dz-preview')) { - dropzone.classList.remove('dz-started'); - } - } catch (error) { - console.error(error); - } - }); - }, - }); - dz.emit('reload'); - return dz; - }; - - const cancelAndReset = (e) => { - e.preventDefault(); - showElem(renderContent); - hideElem(editContentZone); - comboMarkdownEditor.attachedDropzoneInst?.emit('reload'); - }; - - const saveAndRefresh = async (e) => { - e.preventDefault(); - showElem(renderContent); - hideElem(editContentZone); - const dropzoneInst = comboMarkdownEditor.attachedDropzoneInst; - try { - const params = new URLSearchParams({ - content: comboMarkdownEditor.value(), - context: editContentZone.getAttribute('data-context'), - }); - for (const fileInput of dropzoneInst?.element.querySelectorAll('.files [name=files]')) params.append('files[]', fileInput.value); - - const response = await POST(editContentZone.getAttribute('data-update-url'), {data: params}); - const data = await response.json(); - if (!data.content) { - renderContent.innerHTML = document.getElementById('no-content').innerHTML; - rawContent.textContent = ''; - } else { - renderContent.innerHTML = data.content; - rawContent.textContent = comboMarkdownEditor.value(); - const refIssues = renderContent.querySelectorAll('p .ref-issue'); - attachRefIssueContextPopup(refIssues); - } - const content = segment; - if (!content.querySelector('.dropzone-attachments')) { - if (data.attachments !== '') { - content.insertAdjacentHTML('beforeend', data.attachments); - } - } else if (data.attachments === '') { - content.querySelector('.dropzone-attachments').remove(); - } else { - content.querySelector('.dropzone-attachments').outerHTML = data.attachments; - } - dropzoneInst?.emit('submit'); - dropzoneInst?.emit('reload'); - initMarkupContent(); - initCommentContent(); - } catch (error) { - console.error(error); - } - }; - - comboMarkdownEditor = getComboMarkdownEditor(editContentZone.querySelector('.combo-markdown-editor')); - if (!comboMarkdownEditor) { - editContentZone.innerHTML = document.getElementById('issue-comment-editor-template').innerHTML; - comboMarkdownEditor = await initComboMarkdownEditor(editContentZone.querySelector('.combo-markdown-editor')); - comboMarkdownEditor.attachedDropzoneInst = await setupDropzone(editContentZone.querySelector('.dropzone')); - editContentZone.querySelector('.cancel.button').addEventListener('click', cancelAndReset); - editContentZone.querySelector('.save.button').addEventListener('click', saveAndRefresh); - } - - // Show write/preview tab and copy raw content as needed - showElem(editContentZone); - hideElem(renderContent); - if (!comboMarkdownEditor.value()) { - comboMarkdownEditor.value(rawContent.textContent); - } - comboMarkdownEditor.focus(); -} - export function initRepository() { if (!$('.page-content.repository').length) return; @@ -585,33 +414,3 @@ export function initRepository() { initUnicodeEscapeButton(); } - -function initRepoIssueCommentEdit() { - // Edit issue or comment content - $(document).on('click', '.edit-content', onEditContent); - - // Quote reply - $(document).on('click', '.quote-reply', async function (event) { - event.preventDefault(); - const target = $(this).data('target'); - const quote = $(`#${target}`).text().replace(/\n/g, '\n> '); - const content = `> ${quote}\n\n`; - let editor; - if ($(this).hasClass('quote-reply-diff')) { - const $replyBtn = $(this).closest('.comment-code-cloud').find('button.comment-form-reply'); - editor = await handleReply($replyBtn); - } else { - // for normal issue/comment page - editor = getComboMarkdownEditor($('#comment-form .combo-markdown-editor')); - } - if (editor) { - if (editor.value()) { - editor.value(`${editor.value()}\n\n${content}`); - } else { - editor.value(content); - } - editor.focus(); - editor.moveCursorToEnd(); - } - }); -} From fc34481d054a9324ea4654dc721e54e2f608ac17 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Fri, 12 Apr 2024 09:41:50 +0800 Subject: [PATCH 09/35] Add commit status summary table to reduce query from commit status table (#30223) This PR adds a new table named commit status summary to reduce queries from the commit status table. After this change, commit status summary table will be used for the final result, commit status table will be for details. --------- Co-authored-by: Jason Song --- models/git/commit_status.go | 21 ++--- models/git/commit_status_summary.go | 84 +++++++++++++++++++ models/migrations/migrations.go | 3 + models/migrations/v1_23/v295.go | 18 ++++ services/actions/commit_status.go | 20 ++--- .../repository/commitstatus/commitstatus.go | 48 +++++++++-- tests/integration/pull_status_test.go | 7 ++ 7 files changed, 170 insertions(+), 31 deletions(-) create mode 100644 models/git/commit_status_summary.go create mode 100644 models/migrations/v1_23/v295.go diff --git a/models/git/commit_status.go b/models/git/commit_status.go index bb75dcca263b..c3cda7b73d0e 100644 --- a/models/git/commit_status.go +++ b/models/git/commit_status.go @@ -292,30 +292,27 @@ func GetLatestCommitStatus(ctx context.Context, repoID int64, sha string, listOp } // GetLatestCommitStatusForPairs returns all statuses with a unique context for a given list of repo-sha pairs -func GetLatestCommitStatusForPairs(ctx context.Context, repoIDsToLatestCommitSHAs map[int64]string, listOptions db.ListOptions) (map[int64][]*CommitStatus, error) { +func GetLatestCommitStatusForPairs(ctx context.Context, repoSHAs []RepoSHA) (map[int64][]*CommitStatus, error) { type result struct { Index int64 RepoID int64 + SHA string } - results := make([]result, 0, len(repoIDsToLatestCommitSHAs)) + results := make([]result, 0, len(repoSHAs)) getBase := func() *xorm.Session { return db.GetEngine(ctx).Table(&CommitStatus{}) } // Create a disjunction of conditions for each repoID and SHA pair - conds := make([]builder.Cond, 0, len(repoIDsToLatestCommitSHAs)) - for repoID, sha := range repoIDsToLatestCommitSHAs { - conds = append(conds, builder.Eq{"repo_id": repoID, "sha": sha}) + conds := make([]builder.Cond, 0, len(repoSHAs)) + for _, repoSHA := range repoSHAs { + conds = append(conds, builder.Eq{"repo_id": repoSHA.RepoID, "sha": repoSHA.SHA}) } sess := getBase().Where(builder.Or(conds...)). - Select("max( `index` ) as `index`, repo_id"). - GroupBy("context_hash, repo_id").OrderBy("max( `index` ) desc") - - if !listOptions.IsListAll() { - sess = db.SetSessionPagination(sess, &listOptions) - } + Select("max( `index` ) as `index`, repo_id, sha"). + GroupBy("context_hash, repo_id, sha").OrderBy("max( `index` ) desc") err := sess.Find(&results) if err != nil { @@ -332,7 +329,7 @@ func GetLatestCommitStatusForPairs(ctx context.Context, repoIDsToLatestCommitSHA cond := builder.Eq{ "`index`": result.Index, "repo_id": result.RepoID, - "sha": repoIDsToLatestCommitSHAs[result.RepoID], + "sha": result.SHA, } conds = append(conds, cond) } diff --git a/models/git/commit_status_summary.go b/models/git/commit_status_summary.go new file mode 100644 index 000000000000..01674e943d04 --- /dev/null +++ b/models/git/commit_status_summary.go @@ -0,0 +1,84 @@ +// Copyright 2024 Gitea. All rights reserved. +// SPDX-License-Identifier: MIT + +package git + +import ( + "context" + + "code.gitea.io/gitea/models/db" + "code.gitea.io/gitea/modules/setting" + api "code.gitea.io/gitea/modules/structs" + + "xorm.io/builder" +) + +// CommitStatusSummary holds the latest commit Status of a single Commit +type CommitStatusSummary struct { + ID int64 `xorm:"pk autoincr"` + RepoID int64 `xorm:"INDEX UNIQUE(repo_id_sha)"` + SHA string `xorm:"VARCHAR(64) NOT NULL INDEX UNIQUE(repo_id_sha)"` + State api.CommitStatusState `xorm:"VARCHAR(7) NOT NULL"` +} + +func init() { + db.RegisterModel(new(CommitStatusSummary)) +} + +type RepoSHA struct { + RepoID int64 + SHA string +} + +func GetLatestCommitStatusForRepoAndSHAs(ctx context.Context, repoSHAs []RepoSHA) ([]*CommitStatus, error) { + cond := builder.NewCond() + for _, rs := range repoSHAs { + cond = cond.Or(builder.Eq{"repo_id": rs.RepoID, "sha": rs.SHA}) + } + + var summaries []CommitStatusSummary + if err := db.GetEngine(ctx).Where(cond).Find(&summaries); err != nil { + return nil, err + } + + commitStatuses := make([]*CommitStatus, 0, len(repoSHAs)) + for _, summary := range summaries { + commitStatuses = append(commitStatuses, &CommitStatus{ + RepoID: summary.RepoID, + SHA: summary.SHA, + State: summary.State, + }) + } + return commitStatuses, nil +} + +func UpdateCommitStatusSummary(ctx context.Context, repoID int64, sha string) error { + commitStatuses, _, err := GetLatestCommitStatus(ctx, repoID, sha, db.ListOptionsAll) + if err != nil { + return err + } + state := CalcCommitStatus(commitStatuses) + // mysql will return 0 when update a record which state hasn't been changed which behaviour is different from other database, + // so we need to use insert in on duplicate + if setting.Database.Type.IsMySQL() { + _, err := db.GetEngine(ctx).Exec("INSERT INTO commit_status_summary (repo_id,sha,state) VALUES (?,?,?) ON DUPLICATE KEY UPDATE state=?", + repoID, sha, state.State, state.State) + return err + } + + if cnt, err := db.GetEngine(ctx).Where("repo_id=? AND sha=?", repoID, sha). + Cols("state"). + Update(&CommitStatusSummary{ + State: state.State, + }); err != nil { + return err + } else if cnt == 0 { + _, err = db.GetEngine(ctx).Insert(&CommitStatusSummary{ + RepoID: repoID, + SHA: sha, + State: state.State, + }) + return err + } + return nil +} diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go index 387cd96a5347..3ea8f2acbf26 100644 --- a/models/migrations/migrations.go +++ b/models/migrations/migrations.go @@ -576,7 +576,10 @@ var migrations = []Migration{ // Gitea 1.22.0 ends at 294 + // v294 -> v295 NewMigration("Add unique index for project issue table", v1_23.AddUniqueIndexForProjectIssue), + // v295 -> v296 + NewMigration("Add commit status summary table", v1_23.AddCommitStatusSummary), } // GetCurrentDBVersion returns the current db version diff --git a/models/migrations/v1_23/v295.go b/models/migrations/v1_23/v295.go new file mode 100644 index 000000000000..9a2003cfc116 --- /dev/null +++ b/models/migrations/v1_23/v295.go @@ -0,0 +1,18 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package v1_23 //nolint + +import "xorm.io/xorm" + +func AddCommitStatusSummary(x *xorm.Engine) error { + type CommitStatusSummary struct { + ID int64 `xorm:"pk autoincr"` + RepoID int64 `xorm:"INDEX UNIQUE(repo_id_sha)"` + SHA string `xorm:"VARCHAR(64) NOT NULL INDEX UNIQUE(repo_id_sha)"` + State string `xorm:"VARCHAR(7) NOT NULL"` + } + // there is no migrations because if there is no data on this table, it will fall back to get data + // from commit status + return x.Sync2(new(CommitStatusSummary)) +} diff --git a/services/actions/commit_status.go b/services/actions/commit_status.go index 42365539271b..eb031511f6b9 100644 --- a/services/actions/commit_status.go +++ b/services/actions/commit_status.go @@ -16,6 +16,7 @@ import ( "code.gitea.io/gitea/modules/log" api "code.gitea.io/gitea/modules/structs" webhook_module "code.gitea.io/gitea/modules/webhook" + commitstatus_service "code.gitea.io/gitea/services/repository/commitstatus" "github.com/nektos/act/pkg/jobparser" ) @@ -122,18 +123,13 @@ func createCommitStatus(ctx context.Context, job *actions_model.ActionRunJob) er if err != nil { return fmt.Errorf("HashTypeInterfaceFromHashString: %w", err) } - if err := git_model.NewCommitStatus(ctx, git_model.NewCommitStatusOptions{ - Repo: repo, - SHA: commitID, - Creator: creator, - CommitStatus: &git_model.CommitStatus{ - SHA: sha, - TargetURL: fmt.Sprintf("%s/jobs/%d", run.Link(), index), - Description: description, - Context: ctxname, - CreatorID: creator.ID, - State: state, - }, + if err := commitstatus_service.CreateCommitStatus(ctx, repo, creator, commitID.String(), &git_model.CommitStatus{ + SHA: sha, + TargetURL: fmt.Sprintf("%s/jobs/%d", run.Link(), index), + Description: description, + Context: ctxname, + CreatorID: creator.ID, + State: state, }); err != nil { return fmt.Errorf("NewCommitStatus: %w", err) } diff --git a/services/repository/commitstatus/commitstatus.go b/services/repository/commitstatus/commitstatus.go index 145fc7d53c4e..167a5330ddc4 100644 --- a/services/repository/commitstatus/commitstatus.go +++ b/services/repository/commitstatus/commitstatus.go @@ -7,6 +7,7 @@ import ( "context" "crypto/sha256" "fmt" + "slices" "code.gitea.io/gitea/models/db" git_model "code.gitea.io/gitea/models/git" @@ -59,13 +60,19 @@ func CreateCommitStatus(ctx context.Context, repo *repo_model.Repository, creato sha = commit.ID.String() } - if err := git_model.NewCommitStatus(ctx, git_model.NewCommitStatusOptions{ - Repo: repo, - Creator: creator, - SHA: commit.ID, - CommitStatus: status, + if err := db.WithTx(ctx, func(ctx context.Context) error { + if err := git_model.NewCommitStatus(ctx, git_model.NewCommitStatusOptions{ + Repo: repo, + Creator: creator, + SHA: commit.ID, + CommitStatus: status, + }); err != nil { + return fmt.Errorf("NewCommitStatus[repo_id: %d, user_id: %d, sha: %s]: %w", repo.ID, creator.ID, sha, err) + } + + return git_model.UpdateCommitStatusSummary(ctx, repo.ID, commit.ID.String()) }); err != nil { - return fmt.Errorf("NewCommitStatus[repo_id: %d, user_id: %d, sha: %s]: %w", repo.ID, creator.ID, sha, err) + return err } defaultBranchCommit, err := gitRepo.GetBranchCommit(repo.DefaultBranch) @@ -114,8 +121,35 @@ func FindReposLastestCommitStatuses(ctx context.Context, repos []*repo_model.Rep return nil, fmt.Errorf("FindBranchesByRepoAndBranchName: %v", err) } + var repoSHAs []git_model.RepoSHA + for id, sha := range repoIDsToLatestCommitSHAs { + repoSHAs = append(repoSHAs, git_model.RepoSHA{RepoID: id, SHA: sha}) + } + + summaryResults, err := git_model.GetLatestCommitStatusForRepoAndSHAs(ctx, repoSHAs) + if err != nil { + return nil, fmt.Errorf("GetLatestCommitStatusForRepoAndSHAs: %v", err) + } + + for _, summary := range summaryResults { + for i, repo := range repos { + if repo.ID == summary.RepoID { + results[i] = summary + _ = slices.DeleteFunc(repoSHAs, func(repoSHA git_model.RepoSHA) bool { + return repoSHA.RepoID == repo.ID + }) + if results[i].State != "" { + if err := updateCommitStatusCache(ctx, repo.ID, repo.DefaultBranch, results[i].State); err != nil { + log.Error("updateCommitStatusCache[%d:%s] failed: %v", repo.ID, repo.DefaultBranch, err) + } + } + break + } + } + } + // call the database O(1) times to get the commit statuses for all repos - repoToItsLatestCommitStatuses, err := git_model.GetLatestCommitStatusForPairs(ctx, repoIDsToLatestCommitSHAs, db.ListOptionsAll) + repoToItsLatestCommitStatuses, err := git_model.GetLatestCommitStatusForPairs(ctx, repoSHAs) if err != nil { return nil, fmt.Errorf("GetLatestCommitStatusForPairs: %v", err) } diff --git a/tests/integration/pull_status_test.go b/tests/integration/pull_status_test.go index 26c99e644597..bb7098e42425 100644 --- a/tests/integration/pull_status_test.go +++ b/tests/integration/pull_status_test.go @@ -12,6 +12,9 @@ import ( "testing" auth_model "code.gitea.io/gitea/models/auth" + git_model "code.gitea.io/gitea/models/git" + repo_model "code.gitea.io/gitea/models/repo" + "code.gitea.io/gitea/models/unittest" api "code.gitea.io/gitea/modules/structs" "github.com/stretchr/testify/assert" @@ -90,6 +93,10 @@ func TestPullCreate_CommitStatus(t *testing.T) { assert.True(t, ok) assert.Contains(t, cls, statesIcons[status]) } + + repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerName: "user1", Name: "repo1"}) + css := unittest.AssertExistsAndLoadBean(t, &git_model.CommitStatusSummary{RepoID: repo1.ID, SHA: commitID}) + assert.EqualValues(t, api.CommitStatusWarning, css.State) }) } From e8a99c8f92c5b36382abb38a1471c94245457560 Mon Sep 17 00:00:00 2001 From: HEREYUA <37935145+HEREYUA@users.noreply.github.com> Date: Fri, 12 Apr 2024 10:08:58 +0800 Subject: [PATCH 10/35] Fix the spacing issue in the Project view (#30415) **fix**: [#30388](https://github.com/go-gitea/gitea/issues/30388) **before** ![image](https://github.com/go-gitea/gitea/assets/37935145/52ca7311-dca4-4430-9a37-3c45b08fe3dd) **after** ![image](https://github.com/go-gitea/gitea/assets/37935145/6b75ce69-4423-4ea4-99a1-d7234287c5c0) --- templates/org/projects/list.tmpl | 6 ++---- templates/org/projects/view.tmpl | 2 +- templates/user/overview/package_versions.tmpl | 6 ++---- templates/user/overview/packages.tmpl | 6 ++---- templates/user/profile.tmpl | 7 ++----- 5 files changed, 9 insertions(+), 18 deletions(-) diff --git a/templates/org/projects/list.tmpl b/templates/org/projects/list.tmpl index ec9cfece9a8e..80dde1c4d2d8 100644 --- a/templates/org/projects/list.tmpl +++ b/templates/org/projects/list.tmpl @@ -13,11 +13,9 @@
{{template "shared/user/profile_big_avatar" .}}
-
-
+
{{template "user/overview/header" .}} -
- {{template "projects/list" .}} + {{template "projects/list" .}}
diff --git a/templates/org/projects/view.tmpl b/templates/org/projects/view.tmpl index 495204b06d65..e1ab81c4cde5 100644 --- a/templates/org/projects/view.tmpl +++ b/templates/org/projects/view.tmpl @@ -1,7 +1,7 @@ {{template "base/head" .}}
{{template "shared/user/org_profile_avatar" .}} -
+
{{template "user/overview/header" .}}
diff --git a/templates/user/overview/package_versions.tmpl b/templates/user/overview/package_versions.tmpl index b2cc814e13f8..0ac2db0d86c4 100644 --- a/templates/user/overview/package_versions.tmpl +++ b/templates/user/overview/package_versions.tmpl @@ -13,11 +13,9 @@
{{template "shared/user/profile_big_avatar" .}}
-
-
+
{{template "user/overview/header" .}} -
- {{template "package/shared/versionlist" .}} + {{template "package/shared/versionlist" .}}
diff --git a/templates/user/overview/packages.tmpl b/templates/user/overview/packages.tmpl index 95cb506e578f..bb2238b919d5 100644 --- a/templates/user/overview/packages.tmpl +++ b/templates/user/overview/packages.tmpl @@ -13,11 +13,9 @@
{{template "shared/user/profile_big_avatar" .}}
-
-
+
{{template "user/overview/header" .}} -
- {{template "package/shared/list" .}} + {{template "package/shared/list" .}}
diff --git a/templates/user/profile.tmpl b/templates/user/profile.tmpl index e68f79fae686..cf61bb906a17 100644 --- a/templates/user/profile.tmpl +++ b/templates/user/profile.tmpl @@ -5,11 +5,8 @@
{{template "shared/user/profile_big_avatar" .}}
-
-
- {{template "user/overview/header" .}} -
- +
+ {{template "user/overview/header" .}} {{if eq .TabName "activity"}} {{if .ContextUser.KeepActivityPrivate}}
From 7af074dbeebc3c863618992b43f84ec9e5ab9657 Mon Sep 17 00:00:00 2001 From: "Kazushi (Jam) Marukawa" Date: Fri, 12 Apr 2024 11:51:40 +0900 Subject: [PATCH 11/35] Change the default maxPerPage for gitbucket (#30392) This patch improves the migration from gitbucket to gitea. The gitbucket uses it's own internal perPage value (= 25) for paging and ignore per_page arguments in the requested URL. This cause gitea to migrate only 25 issues and 25 PRs from gitbucket repository. This may not happens on old gitbucket. But recent gitbucket 4.40 or 4.38.4 has this problem. This patch change to use this internally hardcoded perPage of gitbucket as gitea's maxPerPage numer when migrating from gitbucket. There are several perPage values in gitbucket like 25 for Isseus/PRs and 10 for Releases. Some of those API doesn't support paging yet. It sounds difficult to implement, but using the minimum number among them worked out very well. So, I use 10 in this patch. Brief descriptions of problems and this patch are also available in https://github.com/go-gitea/gitea/issues/30316. In addition, I'm not sure what kind of test cases are possible to write here. It's a test for migration, so it requires testing gitbucket server and gitea server, I guess. Please let me know if it is possible to write such test cases here. Thanks! --- services/migrations/gitbucket.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/services/migrations/gitbucket.go b/services/migrations/gitbucket.go index 5f11555839b5..4fe9e30a39c9 100644 --- a/services/migrations/gitbucket.go +++ b/services/migrations/gitbucket.go @@ -72,6 +72,11 @@ func (g *GitBucketDownloader) LogString() string { // NewGitBucketDownloader creates a GitBucket downloader func NewGitBucketDownloader(ctx context.Context, baseURL, userName, password, token, repoOwner, repoName string) *GitBucketDownloader { githubDownloader := NewGithubDownloaderV3(ctx, baseURL, userName, password, token, repoOwner, repoName) + // Gitbucket 4.40 uses different internal hard-coded perPage values. + // Issues, PRs, and other major parts use 25. Release page uses 10. + // Some API doesn't support paging yet. Sounds difficult, but using + // minimum number among them worked out very well. + githubDownloader.maxPerPage = 10 githubDownloader.SkipReactions = true githubDownloader.SkipReviews = true return &GitBucketDownloader{ From f9fdac9809335729b2ac3227b2a5f71a62fc64ad Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Fri, 12 Apr 2024 11:36:34 +0800 Subject: [PATCH 12/35] Limit the max line length when parsing git grep output (#30418) --- modules/git/grep.go | 20 ++++++++++++++++---- modules/git/grep_test.go | 10 ++++++++++ 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/modules/git/grep.go b/modules/git/grep.go index a6c486112a5f..e7d238e586aa 100644 --- a/modules/git/grep.go +++ b/modules/git/grep.go @@ -10,6 +10,7 @@ import ( "errors" "fmt" "os" + "slices" "strconv" "strings" @@ -27,6 +28,7 @@ type GrepOptions struct { MaxResultLimit int ContextLineNumber int IsFuzzy bool + MaxLineLength int // the maximum length of a line to parse, exceeding chars will be truncated } func GrepSearch(ctx context.Context, repo *Repository, search string, opts GrepOptions) ([]*GrepResult, error) { @@ -71,10 +73,20 @@ func GrepSearch(ctx context.Context, repo *Repository, search string, opts GrepO defer stdoutReader.Close() isInBlock := false - scanner := bufio.NewScanner(stdoutReader) + rd := bufio.NewReaderSize(stdoutReader, util.IfZero(opts.MaxLineLength, 16*1024)) var res *GrepResult - for scanner.Scan() { - line := scanner.Text() + for { + lineBytes, isPrefix, err := rd.ReadLine() + if isPrefix { + lineBytes = slices.Clone(lineBytes) + for isPrefix && err == nil { + _, isPrefix, err = rd.ReadLine() + } + } + if len(lineBytes) == 0 && err != nil { + break + } + line := string(lineBytes) // the memory of lineBytes is mutable if !isInBlock { if _ /* ref */, filename, ok := strings.Cut(line, ":"); ok { isInBlock = true @@ -100,7 +112,7 @@ func GrepSearch(ctx context.Context, repo *Repository, search string, opts GrepO res.LineCodes = append(res.LineCodes, lineCode) } } - return scanner.Err() + return nil }, }) // git grep exits by cancel (killed), usually it is caused by the limit of results diff --git a/modules/git/grep_test.go b/modules/git/grep_test.go index b5fa437c53f8..7f4ded478f57 100644 --- a/modules/git/grep_test.go +++ b/modules/git/grep_test.go @@ -41,6 +41,16 @@ func TestGrepSearch(t *testing.T) { }, }, res) + res, err = GrepSearch(context.Background(), repo, "void", GrepOptions{MaxResultLimit: 1, MaxLineLength: 39}) + assert.NoError(t, err) + assert.Equal(t, []*GrepResult{ + { + Filename: "java-hello/main.java", + LineNumbers: []int{3}, + LineCodes: []string{" public static void main(String[] arg"}, + }, + }, res) + res, err = GrepSearch(context.Background(), repo, "no-such-content", GrepOptions{}) assert.NoError(t, err) assert.Len(t, res, 0) From 9466fec879f4f2c88c7c1e7a5cffba319282ab66 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Fri, 12 Apr 2024 18:11:16 +0800 Subject: [PATCH 13/35] Fix rename branch 500 when the target branch is deleted but exist in database (#30430) Fix #30428 --- models/git/branch.go | 31 ++++++-- routers/web/repo/setting/protected_branch.go | 8 +- tests/integration/rename_branch_test.go | 80 ++++++++++++++++++-- 3 files changed, 107 insertions(+), 12 deletions(-) diff --git a/models/git/branch.go b/models/git/branch.go index fa0781fed1d5..2979dff3d211 100644 --- a/models/git/branch.go +++ b/models/git/branch.go @@ -297,6 +297,7 @@ func RenameBranch(ctx context.Context, repo *repo_model.Repository, from, to str sess := db.GetEngine(ctx) + // check whether from branch exist var branch Branch exist, err := db.GetEngine(ctx).Where("repo_id=? AND name=?", repo.ID, from).Get(&branch) if err != nil { @@ -308,6 +309,24 @@ func RenameBranch(ctx context.Context, repo *repo_model.Repository, from, to str } } + // check whether to branch exist or is_deleted + var dstBranch Branch + exist, err = db.GetEngine(ctx).Where("repo_id=? AND name=?", repo.ID, to).Get(&dstBranch) + if err != nil { + return err + } + if exist { + if !dstBranch.IsDeleted { + return ErrBranchAlreadyExists{ + BranchName: to, + } + } + + if _, err := db.GetEngine(ctx).ID(dstBranch.ID).NoAutoCondition().Delete(&dstBranch); err != nil { + return err + } + } + // 1. update branch in database if n, err := sess.Where("repo_id=? AND name=?", repo.ID, from).Update(&Branch{ Name: to, @@ -362,12 +381,7 @@ func RenameBranch(ctx context.Context, repo *repo_model.Repository, from, to str return err } - // 5. do git action - if err = gitAction(ctx, isDefault); err != nil { - return err - } - - // 6. insert renamed branch record + // 5. insert renamed branch record renamedBranch := &RenamedBranch{ RepoID: repo.ID, From: from, @@ -378,6 +392,11 @@ func RenameBranch(ctx context.Context, repo *repo_model.Repository, from, to str return err } + // 6. do git action + if err = gitAction(ctx, isDefault); err != nil { + return err + } + return committer.Commit() } diff --git a/routers/web/repo/setting/protected_branch.go b/routers/web/repo/setting/protected_branch.go index b30dc3b0614f..4bab3f897a20 100644 --- a/routers/web/repo/setting/protected_branch.go +++ b/routers/web/repo/setting/protected_branch.go @@ -313,7 +313,13 @@ func RenameBranchPost(ctx *context.Context) { msg, err := repository.RenameBranch(ctx, ctx.Repo.Repository, ctx.Doer, ctx.Repo.GitRepo, form.From, form.To) if err != nil { - ctx.ServerError("RenameBranch", err) + switch { + case git_model.IsErrBranchAlreadyExists(err): + ctx.Flash.Error(ctx.Tr("repo.branch.branch_already_exists", form.To)) + ctx.Redirect(fmt.Sprintf("%s/branches", ctx.Repo.RepoLink)) + default: + ctx.ServerError("RenameBranch", err) + } return } diff --git a/tests/integration/rename_branch_test.go b/tests/integration/rename_branch_test.go index 703fc243a4d3..13f6cf204b53 100644 --- a/tests/integration/rename_branch_test.go +++ b/tests/integration/rename_branch_test.go @@ -5,17 +5,23 @@ package integration import ( "net/http" + "net/url" "testing" git_model "code.gitea.io/gitea/models/git" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" + gitea_context "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/tests" "github.com/stretchr/testify/assert" ) func TestRenameBranch(t *testing.T) { + onGiteaRun(t, testRenameBranch) +} + +func testRenameBranch(t *testing.T, u *url.URL) { defer tests.PrepareTestEnv(t)() unittest.AssertExistsAndLoadBean(t, &git_model.Branch{RepoID: 1, Name: "master"}) @@ -26,20 +32,19 @@ func TestRenameBranch(t *testing.T) { resp := session.MakeRequest(t, req, http.StatusOK) htmlDoc := NewHTMLParser(t, resp.Body) - postData := map[string]string{ + req = NewRequestWithValues(t, "POST", "/user2/repo1/settings/rename_branch", map[string]string{ "_csrf": htmlDoc.GetCSRF(), "from": "master", "to": "main", - } - req = NewRequestWithValues(t, "POST", "/user2/repo1/settings/rename_branch", postData) + }) session.MakeRequest(t, req, http.StatusSeeOther) // check new branch link - req = NewRequestWithValues(t, "GET", "/user2/repo1/src/branch/main/README.md", postData) + req = NewRequestWithValues(t, "GET", "/user2/repo1/src/branch/main/README.md", nil) session.MakeRequest(t, req, http.StatusOK) // check old branch link - req = NewRequestWithValues(t, "GET", "/user2/repo1/src/branch/master/README.md", postData) + req = NewRequestWithValues(t, "GET", "/user2/repo1/src/branch/master/README.md", nil) resp = session.MakeRequest(t, req, http.StatusSeeOther) location := resp.Header().Get("Location") assert.Equal(t, "/user2/repo1/src/branch/main/README.md", location) @@ -47,4 +52,69 @@ func TestRenameBranch(t *testing.T) { // check db repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) assert.Equal(t, "main", repo1.DefaultBranch) + + // create branch1 + csrf := GetCSRF(t, session, "/user2/repo1/src/branch/main") + + req = NewRequestWithValues(t, "POST", "/user2/repo1/branches/_new/branch/main", map[string]string{ + "_csrf": csrf, + "new_branch_name": "branch1", + }) + session.MakeRequest(t, req, http.StatusSeeOther) + + branch1 := unittest.AssertExistsAndLoadBean(t, &git_model.Branch{RepoID: repo1.ID, Name: "branch1"}) + assert.Equal(t, "branch1", branch1.Name) + + // create branch2 + req = NewRequestWithValues(t, "POST", "/user2/repo1/branches/_new/branch/main", map[string]string{ + "_csrf": csrf, + "new_branch_name": "branch2", + }) + session.MakeRequest(t, req, http.StatusSeeOther) + + branch2 := unittest.AssertExistsAndLoadBean(t, &git_model.Branch{RepoID: repo1.ID, Name: "branch2"}) + assert.Equal(t, "branch2", branch2.Name) + + // rename branch2 to branch1 + req = NewRequestWithValues(t, "POST", "/user2/repo1/settings/rename_branch", map[string]string{ + "_csrf": htmlDoc.GetCSRF(), + "from": "branch2", + "to": "branch1", + }) + session.MakeRequest(t, req, http.StatusSeeOther) + flashCookie := session.GetCookie(gitea_context.CookieNameFlash) + assert.NotNil(t, flashCookie) + assert.Contains(t, flashCookie.Value, "error") + + branch2 = unittest.AssertExistsAndLoadBean(t, &git_model.Branch{RepoID: repo1.ID, Name: "branch2"}) + assert.Equal(t, "branch2", branch2.Name) + branch1 = unittest.AssertExistsAndLoadBean(t, &git_model.Branch{RepoID: repo1.ID, Name: "branch1"}) + assert.Equal(t, "branch1", branch1.Name) + + // delete branch1 + req = NewRequestWithValues(t, "POST", "/user2/repo1/branches/delete", map[string]string{ + "_csrf": htmlDoc.GetCSRF(), + "name": "branch1", + }) + session.MakeRequest(t, req, http.StatusOK) + branch2 = unittest.AssertExistsAndLoadBean(t, &git_model.Branch{RepoID: repo1.ID, Name: "branch2"}) + assert.Equal(t, "branch2", branch2.Name) + branch1 = unittest.AssertExistsAndLoadBean(t, &git_model.Branch{RepoID: repo1.ID, Name: "branch1"}) + assert.True(t, branch1.IsDeleted) // virtual deletion + + // rename branch2 to branch1 again + req = NewRequestWithValues(t, "POST", "/user2/repo1/settings/rename_branch", map[string]string{ + "_csrf": htmlDoc.GetCSRF(), + "from": "branch2", + "to": "branch1", + }) + session.MakeRequest(t, req, http.StatusSeeOther) + + flashCookie = session.GetCookie(gitea_context.CookieNameFlash) + assert.NotNil(t, flashCookie) + assert.Contains(t, flashCookie.Value, "success") + + unittest.AssertNotExistsBean(t, &git_model.Branch{RepoID: repo1.ID, Name: "branch2"}) + branch1 = unittest.AssertExistsAndLoadBean(t, &git_model.Branch{RepoID: repo1.ID, Name: "branch1"}) + assert.Equal(t, "branch1", branch1.Name) } From 25427e0aee435cdedb9f8aae58767174d877767f Mon Sep 17 00:00:00 2001 From: Yarden Shoham Date: Fri, 12 Apr 2024 13:34:12 +0300 Subject: [PATCH 14/35] Remove jQuery from the commit graph (except Fomantic) (#30395) - Switched to plain JavaScript - Tested the commit graph and it works as before # Demo using JavaScript without jQuery ![demo](https://github.com/go-gitea/gitea/assets/20454870/d0755ed6-bb5c-4601-a2b7-ebccaf4abce4) --------- Signed-off-by: Yarden Shoham Co-authored-by: silverwind Co-authored-by: delvh Co-authored-by: Giteabot --- web_src/js/features/repo-graph.js | 138 +++++++++++++++++------------- 1 file changed, 77 insertions(+), 61 deletions(-) diff --git a/web_src/js/features/repo-graph.js b/web_src/js/features/repo-graph.js index a5b61bff54d9..0086b92021bd 100644 --- a/web_src/js/features/repo-graph.js +++ b/web_src/js/features/repo-graph.js @@ -1,14 +1,16 @@ import $ from 'jquery'; +import {hideElem, showElem} from '../utils/dom.js'; import {GET} from '../modules/fetch.js'; export function initRepoGraphGit() { const graphContainer = document.getElementById('git-graph-container'); if (!graphContainer) return; - $('#flow-color-monochrome').on('click', () => { - $('#flow-color-monochrome').addClass('active'); - $('#flow-color-colored').removeClass('active'); - $('#git-graph-container').removeClass('colored').addClass('monochrome'); + document.getElementById('flow-color-monochrome')?.addEventListener('click', () => { + document.getElementById('flow-color-monochrome').classList.add('active'); + document.getElementById('flow-color-colored')?.classList.remove('active'); + graphContainer.classList.remove('colored'); + graphContainer.classList.add('monochrome'); const params = new URLSearchParams(window.location.search); params.set('mode', 'monochrome'); const queryString = params.toString(); @@ -17,29 +19,31 @@ export function initRepoGraphGit() { } else { window.history.replaceState({}, '', window.location.pathname); } - $('.pagination a').each((_, that) => { - const href = that.getAttribute('href'); - if (!href) return; + for (const link of document.querySelectorAll('.pagination a')) { + const href = link.getAttribute('href'); + if (!href) continue; const url = new URL(href, window.location); const params = url.searchParams; params.set('mode', 'monochrome'); url.search = `?${params.toString()}`; - that.setAttribute('href', url.href); - }); + link.setAttribute('href', url.href); + } }); - $('#flow-color-colored').on('click', () => { - $('#flow-color-colored').addClass('active'); - $('#flow-color-monochrome').removeClass('active'); - $('#git-graph-container').addClass('colored').removeClass('monochrome'); - $('.pagination a').each((_, that) => { - const href = that.getAttribute('href'); - if (!href) return; + + document.getElementById('flow-color-colored')?.addEventListener('click', () => { + document.getElementById('flow-color-colored').classList.add('active'); + document.getElementById('flow-color-monochrome')?.classList.remove('active'); + graphContainer.classList.add('colored'); + graphContainer.classList.remove('monochrome'); + for (const link of document.querySelectorAll('.pagination a')) { + const href = link.getAttribute('href'); + if (!href) continue; const url = new URL(href, window.location); const params = url.searchParams; params.delete('mode'); url.search = `?${params.toString()}`; - that.setAttribute('href', url.href); - }); + link.setAttribute('href', url.href); + } const params = new URLSearchParams(window.location.search); params.delete('mode'); const queryString = params.toString(); @@ -56,20 +60,21 @@ export function initRepoGraphGit() { const ajaxUrl = new URL(url); ajaxUrl.searchParams.set('div-only', 'true'); window.history.replaceState({}, '', queryString ? `?${queryString}` : window.location.pathname); - $('#pagination').empty(); - $('#rel-container').addClass('tw-hidden'); - $('#rev-container').addClass('tw-hidden'); - $('#loading-indicator').removeClass('tw-hidden'); + document.getElementById('pagination').innerHTML = ''; + hideElem('#rel-container'); + hideElem('#rev-container'); + showElem('#loading-indicator'); (async () => { const response = await GET(String(ajaxUrl)); const html = await response.text(); - const $div = $(html); - $('#pagination').html($div.find('#pagination').html()); - $('#rel-container').html($div.find('#rel-container').html()); - $('#rev-container').html($div.find('#rev-container').html()); - $('#loading-indicator').addClass('tw-hidden'); - $('#rel-container').removeClass('tw-hidden'); - $('#rev-container').removeClass('tw-hidden'); + const div = document.createElement('div'); + div.innerHTML = html; + document.getElementById('pagination').innerHTML = div.getElementById('pagination').innerHTML; + document.getElementById('rel-container').innerHTML = div.getElementById('rel-container').innerHTML; + document.getElementById('rev-container').innerHTML = div.getElementById('rev-container').innerHTML; + hideElem('#loading-indicator'); + showElem('#rel-container'); + showElem('#rev-container'); })(); }; const dropdownSelected = params.getAll('branch'); @@ -77,8 +82,9 @@ export function initRepoGraphGit() { dropdownSelected.splice(0, 0, '...flow-hide-pr-refs'); } - $('#flow-select-refs-dropdown').dropdown('set selected', dropdownSelected); - $('#flow-select-refs-dropdown').dropdown({ + const flowSelectRefsDropdown = document.getElementById('flow-select-refs-dropdown'); + $(flowSelectRefsDropdown).dropdown('set selected', dropdownSelected); + $(flowSelectRefsDropdown).dropdown({ clearable: true, fullTextSeach: 'exact', onRemove(toRemove) { @@ -104,36 +110,46 @@ export function initRepoGraphGit() { updateGraph(); }, }); - $('#git-graph-container').on('mouseenter', '#rev-list li', (e) => { - const flow = $(e.currentTarget).data('flow'); - if (flow === 0) return; - $(`#flow-${flow}`).addClass('highlight'); - $(e.currentTarget).addClass('hover'); - $(`#rev-list li[data-flow='${flow}']`).addClass('highlight'); - }); - $('#git-graph-container').on('mouseleave', '#rev-list li', (e) => { - const flow = $(e.currentTarget).data('flow'); - if (flow === 0) return; - $(`#flow-${flow}`).removeClass('highlight'); - $(e.currentTarget).removeClass('hover'); - $(`#rev-list li[data-flow='${flow}']`).removeClass('highlight'); - }); - $('#git-graph-container').on('mouseenter', '#rel-container .flow-group', (e) => { - $(e.currentTarget).addClass('highlight'); - const flow = $(e.currentTarget).data('flow'); - $(`#rev-list li[data-flow='${flow}']`).addClass('highlight'); - }); - $('#git-graph-container').on('mouseleave', '#rel-container .flow-group', (e) => { - $(e.currentTarget).removeClass('highlight'); - const flow = $(e.currentTarget).data('flow'); - $(`#rev-list li[data-flow='${flow}']`).removeClass('highlight'); - }); - $('#git-graph-container').on('mouseenter', '#rel-container .flow-commit', (e) => { - const rev = $(e.currentTarget).data('rev'); - $(`#rev-list li#commit-${rev}`).addClass('hover'); + + graphContainer.addEventListener('mouseenter', (e) => { + if (e.target.matches('#rev-list li')) { + const flow = e.target.getAttribute('data-flow'); + if (flow === '0') return; + document.getElementById(`flow-${flow}`)?.classList.add('highlight'); + e.target.classList.add('hover'); + for (const item of document.querySelectorAll(`#rev-list li[data-flow='${flow}']`)) { + item.classList.add('highlight'); + } + } else if (e.target.matches('#rel-container .flow-group')) { + e.target.classList.add('highlight'); + const flow = e.target.getAttribute('data-flow'); + for (const item of document.querySelectorAll(`#rev-list li[data-flow='${flow}']`)) { + item.classList.add('highlight'); + } + } else if (e.target.matches('#rel-container .flow-commit')) { + const rev = e.target.getAttribute('data-rev'); + document.querySelector(`#rev-list li#commit-${rev}`)?.classList.add('hover'); + } }); - $('#git-graph-container').on('mouseleave', '#rel-container .flow-commit', (e) => { - const rev = $(e.currentTarget).data('rev'); - $(`#rev-list li#commit-${rev}`).removeClass('hover'); + + graphContainer.addEventListener('mouseleave', (e) => { + if (e.target.matches('#rev-list li')) { + const flow = e.target.getAttribute('data-flow'); + if (flow === '0') return; + document.getElementById(`flow-${flow}`)?.classList.remove('highlight'); + e.target.classList.remove('hover'); + for (const item of document.querySelectorAll(`#rev-list li[data-flow='${flow}']`)) { + item.classList.remove('highlight'); + } + } else if (e.target.matches('#rel-container .flow-group')) { + e.target.classList.remove('highlight'); + const flow = e.target.getAttribute('data-flow'); + for (const item of document.querySelectorAll(`#rev-list li[data-flow='${flow}']`)) { + item.classList.remove('highlight'); + } + } else if (e.target.matches('#rel-container .flow-commit')) { + const rev = e.target.getAttribute('data-rev'); + document.querySelector(`#rev-list li#commit-${rev}`)?.classList.remove('hover'); + } }); } From f9c3e79abac9dc417cdbbddf24a9fb8dc49363c4 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Fri, 12 Apr 2024 19:02:42 +0800 Subject: [PATCH 15/35] Fix commit status cache which missed target_url (#30426) Fix #30421 --------- Co-authored-by: Jason Song --- .../repository/commitstatus/commitstatus.go | 52 +++++++++++++++---- 1 file changed, 41 insertions(+), 11 deletions(-) diff --git a/services/repository/commitstatus/commitstatus.go b/services/repository/commitstatus/commitstatus.go index 167a5330ddc4..7c1c6c260938 100644 --- a/services/repository/commitstatus/commitstatus.go +++ b/services/repository/commitstatus/commitstatus.go @@ -16,6 +16,7 @@ import ( "code.gitea.io/gitea/modules/cache" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/gitrepo" + "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/log" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/services/automerge" @@ -26,12 +27,41 @@ func getCacheKey(repoID int64, brancheName string) string { return fmt.Sprintf("commit_status:%x", hashBytes) } -func updateCommitStatusCache(ctx context.Context, repoID int64, branchName string, status api.CommitStatusState) error { +type commitStatusCacheValue struct { + State string `json:"state"` + TargetURL string `json:"target_url"` +} + +func getCommitStatusCache(repoID int64, branchName string) *commitStatusCacheValue { + c := cache.GetCache() + statusStr, ok := c.Get(getCacheKey(repoID, branchName)).(string) + if ok && statusStr != "" { + var cv commitStatusCacheValue + err := json.Unmarshal([]byte(statusStr), &cv) + if err == nil && cv.State != "" { + return &cv + } + if err != nil { + log.Warn("getCommitStatusCache: json.Unmarshal failed: %v", err) + } + } + return nil +} + +func updateCommitStatusCache(repoID int64, branchName string, state api.CommitStatusState, targetURL string) error { c := cache.GetCache() - return c.Put(getCacheKey(repoID, branchName), string(status), 3*24*60) + bs, err := json.Marshal(commitStatusCacheValue{ + State: state.String(), + TargetURL: targetURL, + }) + if err != nil { + log.Warn("updateCommitStatusCache: json.Marshal failed: %v", err) + return nil + } + return c.Put(getCacheKey(repoID, branchName), string(bs), 3*24*60) } -func deleteCommitStatusCache(ctx context.Context, repoID int64, branchName string) error { +func deleteCommitStatusCache(repoID int64, branchName string) error { c := cache.GetCache() return c.Delete(getCacheKey(repoID, branchName)) } @@ -81,7 +111,7 @@ func CreateCommitStatus(ctx context.Context, repo *repo_model.Repository, creato } if commit.ID.String() == defaultBranchCommit.ID.String() { // since one commit status updated, the combined commit status should be invalid - if err := deleteCommitStatusCache(ctx, repo.ID, repo.DefaultBranch); err != nil { + if err := deleteCommitStatusCache(repo.ID, repo.DefaultBranch); err != nil { log.Error("deleteCommitStatusCache[%d:%s] failed: %v", repo.ID, repo.DefaultBranch, err) } } @@ -98,12 +128,12 @@ func CreateCommitStatus(ctx context.Context, repo *repo_model.Repository, creato // FindReposLastestCommitStatuses loading repository default branch latest combinded commit status with cache func FindReposLastestCommitStatuses(ctx context.Context, repos []*repo_model.Repository) ([]*git_model.CommitStatus, error) { results := make([]*git_model.CommitStatus, len(repos)) - c := cache.GetCache() - for i, repo := range repos { - status, ok := c.Get(getCacheKey(repo.ID, repo.DefaultBranch)).(string) - if ok && status != "" { - results[i] = &git_model.CommitStatus{State: api.CommitStatusState(status)} + if cv := getCommitStatusCache(repo.ID, repo.DefaultBranch); cv != nil { + results[i] = &git_model.CommitStatus{ + State: api.CommitStatusState(cv.State), + TargetURL: cv.TargetURL, + } } } @@ -139,7 +169,7 @@ func FindReposLastestCommitStatuses(ctx context.Context, repos []*repo_model.Rep return repoSHA.RepoID == repo.ID }) if results[i].State != "" { - if err := updateCommitStatusCache(ctx, repo.ID, repo.DefaultBranch, results[i].State); err != nil { + if err := updateCommitStatusCache(repo.ID, repo.DefaultBranch, results[i].State, results[i].TargetURL); err != nil { log.Error("updateCommitStatusCache[%d:%s] failed: %v", repo.ID, repo.DefaultBranch, err) } } @@ -158,7 +188,7 @@ func FindReposLastestCommitStatuses(ctx context.Context, repos []*repo_model.Rep if results[i] == nil { results[i] = git_model.CalcCommitStatus(repoToItsLatestCommitStatuses[repo.ID]) if results[i].State != "" { - if err := updateCommitStatusCache(ctx, repo.ID, repo.DefaultBranch, results[i].State); err != nil { + if err := updateCommitStatusCache(repo.ID, repo.DefaultBranch, results[i].State, results[i].TargetURL); err != nil { log.Error("updateCommitStatusCache[%d:%s] failed: %v", repo.ID, repo.DefaultBranch, err) } } From 487b12783fb2dba7459a8aa739162cfe6bab3904 Mon Sep 17 00:00:00 2001 From: silverwind Date: Fri, 12 Apr 2024 14:52:39 +0200 Subject: [PATCH 16/35] Lock a few tool dependencies to major versions (#30439) It's better having to update these less often, so unlock a few dependencies that I trust enough to not break to their latest major versions. This excludes any tool still at major version 0 and golangci-lint can't really be unlocked either because new versions almost always break there. For the v0 packages, I've opened https://github.com/golangci/misspell/issues/14 and https://github.com/mvdan/gofumpt/issues/303. --- Makefile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index ee9c90e8d972..f1acfbc81e3c 100644 --- a/Makefile +++ b/Makefile @@ -25,7 +25,7 @@ COMMA := , XGO_VERSION := go-1.22.x -AIR_PACKAGE ?= github.com/cosmtrek/air@v1.49.0 +AIR_PACKAGE ?= github.com/cosmtrek/air@v1 EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-checker/cmd/editorconfig-checker@2.7.0 GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@v0.6.0 GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/cmd/golangci-lint@v1.57.2 @@ -33,9 +33,9 @@ GXZ_PACKAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.11 MISSPELL_PACKAGE ?= github.com/golangci/misspell/cmd/misspell@v0.4.1 SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@db51e79a0e37c572d8b59ae0c58bf2bbbbe53285 XGO_PACKAGE ?= src.techknowlogick.com/xgo@latest -GO_LICENSES_PACKAGE ?= github.com/google/go-licenses@v1.6.0 -GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@v1.0.3 -ACTIONLINT_PACKAGE ?= github.com/rhysd/actionlint/cmd/actionlint@v1.6.26 +GO_LICENSES_PACKAGE ?= github.com/google/go-licenses@v1 +GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@v1 +ACTIONLINT_PACKAGE ?= github.com/rhysd/actionlint/cmd/actionlint@v1 DOCKER_IMAGE ?= gitea/gitea DOCKER_TAG ?= latest From 68271834d6ae6d397b5a2048f9e515ff53735994 Mon Sep 17 00:00:00 2001 From: silverwind Date: Sat, 13 Apr 2024 04:28:20 +0200 Subject: [PATCH 17/35] Add `/public/assets/img/webpack` to ignore files again (#30451) Fixes https://github.com/go-gitea/gitea/issues/30442 It's inconvenient to have new untracked files show up in git when switching to older branches that had generated them. Introduce a list of such files and folders to gitignore and dockerignore. --- .dockerignore | 3 +++ .gitignore | 3 +++ 2 files changed, 6 insertions(+) diff --git a/.dockerignore b/.dockerignore index b299c7313d05..b696e1603cca 100644 --- a/.dockerignore +++ b/.dockerignore @@ -95,6 +95,9 @@ cpu.out /.air /.go-licenses +# Files and folders that were previously generated +/public/assets/img/webpack + # Snapcraft snap/.snapcraft/ parts/ diff --git a/.gitignore b/.gitignore index 501fef7dcf88..46c8b9b49c9a 100644 --- a/.gitignore +++ b/.gitignore @@ -94,6 +94,9 @@ cpu.out /.air /.go-licenses +# Files and folders that were previously generated +/public/assets/img/webpack + # Snapcraft /gitea_a*.txt snap/.snapcraft/ From b4d86912ef18c58e973ee10c4a3bc621e9bd6c52 Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Sat, 13 Apr 2024 12:01:02 +0900 Subject: [PATCH 18/35] Fix mirror error when mirror repo is empty (#30432) Fix #30424 Co-authored-by: Giteabot --- services/mirror/mirror_pull.go | 40 +++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/services/mirror/mirror_pull.go b/services/mirror/mirror_pull.go index 2a38d4ba55d3..21d5f08205de 100644 --- a/services/mirror/mirror_pull.go +++ b/services/mirror/mirror_pull.go @@ -449,19 +449,17 @@ func SyncPullMirror(ctx context.Context, repoID int64) bool { return false } - var gitRepo *git.Repository - if len(results) == 0 { - log.Trace("SyncMirrors [repo: %-v]: no branches updated", m.Repo) - } else { - log.Trace("SyncMirrors [repo: %-v]: %d branches updated", m.Repo, len(results)) - gitRepo, err = gitrepo.OpenRepository(ctx, m.Repo) - if err != nil { - log.Error("SyncMirrors [repo: %-v]: unable to OpenRepository: %v", m.Repo, err) - return false - } - defer gitRepo.Close() + gitRepo, err := gitrepo.OpenRepository(ctx, m.Repo) + if err != nil { + log.Error("SyncMirrors [repo: %-v]: unable to OpenRepository: %v", m.Repo, err) + return false + } + defer gitRepo.Close() + log.Trace("SyncMirrors [repo: %-v]: %d branches updated", m.Repo, len(results)) + if len(results) > 0 { if ok := checkAndUpdateEmptyRepository(ctx, m, gitRepo, results); !ok { + log.Error("SyncMirrors [repo: %-v]: checkAndUpdateEmptyRepository: %v", m.Repo, err) return false } } @@ -534,16 +532,24 @@ func SyncPullMirror(ctx context.Context, repoID int64) bool { } log.Trace("SyncMirrors [repo: %-v]: done notifying updated branches/tags - now updating last commit time", m.Repo) - // Get latest commit date and update to current repository updated time - commitDate, err := git.GetLatestCommitTime(ctx, m.Repo.RepoPath()) + isEmpty, err := gitRepo.IsEmpty() if err != nil { - log.Error("SyncMirrors [repo: %-v]: unable to GetLatestCommitDate: %v", m.Repo, err) + log.Error("SyncMirrors [repo: %-v]: unable to check empty git repo: %v", m.Repo, err) return false } + if !isEmpty { + // Get latest commit date and update to current repository updated time + commitDate, err := git.GetLatestCommitTime(ctx, m.Repo.RepoPath()) + if err != nil { + log.Error("SyncMirrors [repo: %-v]: unable to GetLatestCommitDate: %v", m.Repo, err) + return false + } + + if err = repo_model.UpdateRepositoryUpdatedTime(ctx, m.RepoID, commitDate); err != nil { + log.Error("SyncMirrors [repo: %-v]: unable to update repository 'updated_unix': %v", m.Repo, err) + return false + } - if err = repo_model.UpdateRepositoryUpdatedTime(ctx, m.RepoID, commitDate); err != nil { - log.Error("SyncMirrors [repo: %-v]: unable to update repository 'updated_unix': %v", m.Repo, err) - return false } log.Trace("SyncMirrors [repo: %-v]: Successfully updated", m.Repo) From 8fd8978b4934865c2b041216e84e923ad574a4c7 Mon Sep 17 00:00:00 2001 From: silverwind Date: Sat, 13 Apr 2024 09:46:02 +0200 Subject: [PATCH 19/35] Fix admin notice view-detail (#30450) Fix https://github.com/go-gitea/gitea/issues/30434, regression from https://github.com/go-gitea/gitea/pull/30115. I also removed the date insertion into the modal which was also broken since that date was switched to `absolute-date` because I see no real purpose to putting that date into the modal. Result: image --- templates/admin/notice.tmpl | 5 +---- web_src/js/features/admin/common.js | 8 ++++---- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/templates/admin/notice.tmpl b/templates/admin/notice.tmpl index 5ea003e5ec12..33d8a2f9632d 100644 --- a/templates/admin/notice.tmpl +++ b/templates/admin/notice.tmpl @@ -62,10 +62,7 @@ {{template "admin/layout_footer" .}} diff --git a/web_src/js/features/admin/common.js b/web_src/js/features/admin/common.js index f388b1122ec5..b35502d52f9f 100644 --- a/web_src/js/features/admin/common.js +++ b/web_src/js/features/admin/common.js @@ -207,13 +207,13 @@ export function initAdminCommon() { // Notice if (document.querySelector('.admin.notice')) { - const $detailModal = document.getElementById('detail-modal'); + const detailModal = document.getElementById('detail-modal'); // Attach view detail modals $('.view-detail').on('click', function () { - $detailModal.find('.content pre').text($(this).parents('tr').find('.notice-description').text()); - $detailModal.find('.sub.header').text(this.closest('tr')?.querySelector('relative-time')?.getAttribute('title')); - $detailModal.modal('show'); + const description = this.closest('tr').querySelector('.notice-description').textContent; + detailModal.querySelector('.content pre').textContent = description; + $(detailModal).modal('show'); return false; }); From c248f010ad08a7017ba1d418e9b6a5b72aff0c88 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Sat, 13 Apr 2024 16:38:44 +0800 Subject: [PATCH 20/35] Refactor cache and disable go-chi cache (#30417) use built-in cache package to wrap external go-chi cache package --- .golangci.yml | 2 + modules/cache/cache.go | 138 ++++-------------- modules/cache/cache_redis.go | 2 +- modules/cache/cache_test.go | 40 +---- modules/cache/cache_twoqueue.go | 2 +- modules/cache/string_cache.go | 120 +++++++++++++++ modules/git/last_commit_cache.go | 15 +- routers/api/v1/misc/nodeinfo.go | 6 +- services/context/api.go | 8 +- services/context/captcha.go | 2 +- services/context/context.go | 7 +- services/repository/branch.go | 11 +- .../repository/commitstatus/commitstatus.go | 2 +- services/repository/contributors_graph.go | 32 ++-- .../repository/contributors_graph_test.go | 20 ++- 15 files changed, 198 insertions(+), 209 deletions(-) create mode 100644 modules/cache/string_cache.go diff --git a/.golangci.yml b/.golangci.yml index 5be2cefe44b7..27fee20f75ae 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -86,6 +86,8 @@ linters-settings: desc: do not use the internal package, use AddXxx function instead - pkg: gopkg.in/ini.v1 desc: do not use the ini package, use gitea's config system instead + - pkg: gitea.com/go-chi/cache + desc: do not use the go-chi cache package, use gitea's cache system issues: max-issues-per-linter: 0 diff --git a/modules/cache/cache.go b/modules/cache/cache.go index 09afc8b7f736..2ca77bdb29f3 100644 --- a/modules/cache/cache.go +++ b/modules/cache/cache.go @@ -4,149 +4,75 @@ package cache import ( - "fmt" "strconv" + "time" "code.gitea.io/gitea/modules/setting" - - mc "gitea.com/go-chi/cache" - - _ "gitea.com/go-chi/cache/memcache" // memcache plugin for cache ) -var conn mc.Cache - -func newCache(cacheConfig setting.Cache) (mc.Cache, error) { - return mc.NewCacher(mc.Options{ - Adapter: cacheConfig.Adapter, - AdapterConfig: cacheConfig.Conn, - Interval: cacheConfig.Interval, - }) -} +var defaultCache StringCache // Init start cache service func Init() error { - var err error - - if conn == nil { - if conn, err = newCache(setting.CacheService.Cache); err != nil { + if defaultCache == nil { + c, err := NewStringCache(setting.CacheService.Cache) + if err != nil { return err } - if err = conn.Ping(); err != nil { + for i := 0; i < 10; i++ { + if err = c.Ping(); err == nil { + break + } + time.Sleep(time.Second) + } + if err != nil { return err } + defaultCache = c } - - return err + return nil } // GetCache returns the currently configured cache -func GetCache() mc.Cache { - return conn +func GetCache() StringCache { + return defaultCache } // GetString returns the key value from cache with callback when no key exists in cache func GetString(key string, getFunc func() (string, error)) (string, error) { - if conn == nil || setting.CacheService.TTL == 0 { + if defaultCache == nil || setting.CacheService.TTL == 0 { return getFunc() } - - cached := conn.Get(key) - - if cached == nil { + cached, exist := defaultCache.Get(key) + if !exist { value, err := getFunc() if err != nil { return value, err } - return value, conn.Put(key, value, setting.CacheService.TTLSeconds()) - } - - if value, ok := cached.(string); ok { - return value, nil - } - - if stringer, ok := cached.(fmt.Stringer); ok { - return stringer.String(), nil - } - - return fmt.Sprintf("%s", cached), nil -} - -// GetInt returns key value from cache with callback when no key exists in cache -func GetInt(key string, getFunc func() (int, error)) (int, error) { - if conn == nil || setting.CacheService.TTL == 0 { - return getFunc() - } - - cached := conn.Get(key) - - if cached == nil { - value, err := getFunc() - if err != nil { - return value, err - } - - return value, conn.Put(key, value, setting.CacheService.TTLSeconds()) - } - - switch v := cached.(type) { - case int: - return v, nil - case string: - value, err := strconv.Atoi(v) - if err != nil { - return 0, err - } - return value, nil - default: - value, err := getFunc() - if err != nil { - return value, err - } - return value, conn.Put(key, value, setting.CacheService.TTLSeconds()) + return value, defaultCache.Put(key, value, setting.CacheService.TTLSeconds()) } + return cached, nil } // GetInt64 returns key value from cache with callback when no key exists in cache func GetInt64(key string, getFunc func() (int64, error)) (int64, error) { - if conn == nil || setting.CacheService.TTL == 0 { - return getFunc() - } - - cached := conn.Get(key) - - if cached == nil { - value, err := getFunc() - if err != nil { - return value, err - } - - return value, conn.Put(key, value, setting.CacheService.TTLSeconds()) + s, err := GetString(key, func() (string, error) { + v, err := getFunc() + return strconv.FormatInt(v, 10), err + }) + if err != nil { + return 0, err } - - switch v := conn.Get(key).(type) { - case int64: - return v, nil - case string: - value, err := strconv.ParseInt(v, 10, 64) - if err != nil { - return 0, err - } - return value, nil - default: - value, err := getFunc() - if err != nil { - return value, err - } - - return value, conn.Put(key, value, setting.CacheService.TTLSeconds()) + if s == "" { + return 0, nil } + return strconv.ParseInt(s, 10, 64) } // Remove key from cache func Remove(key string) { - if conn == nil { + if defaultCache == nil { return } - _ = conn.Delete(key) + _ = defaultCache.Delete(key) } diff --git a/modules/cache/cache_redis.go b/modules/cache/cache_redis.go index 6c358b0a78a4..c5b52a2086ce 100644 --- a/modules/cache/cache_redis.go +++ b/modules/cache/cache_redis.go @@ -11,7 +11,7 @@ import ( "code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/nosql" - "gitea.com/go-chi/cache" + "gitea.com/go-chi/cache" //nolint:depguard "github.com/redis/go-redis/v9" ) diff --git a/modules/cache/cache_test.go b/modules/cache/cache_test.go index 3f6504092462..0c68cc26ee54 100644 --- a/modules/cache/cache_test.go +++ b/modules/cache/cache_test.go @@ -14,7 +14,7 @@ import ( ) func createTestCache() { - conn, _ = newCache(setting.Cache{ + defaultCache, _ = NewStringCache(setting.Cache{ Adapter: "memory", TTL: time.Minute, }) @@ -25,7 +25,7 @@ func TestNewContext(t *testing.T) { assert.NoError(t, Init()) setting.CacheService.Cache = setting.Cache{Adapter: "redis", Conn: "some random string"} - con, err := newCache(setting.Cache{ + con, err := NewStringCache(setting.Cache{ Adapter: "rand", Conn: "false conf", Interval: 100, @@ -76,42 +76,6 @@ func TestGetString(t *testing.T) { Remove("key") } -func TestGetInt(t *testing.T) { - createTestCache() - - data, err := GetInt("key", func() (int, error) { - return 0, fmt.Errorf("some error") - }) - assert.Error(t, err) - assert.Equal(t, 0, data) - - data, err = GetInt("key", func() (int, error) { - return 0, nil - }) - assert.NoError(t, err) - assert.Equal(t, 0, data) - - data, err = GetInt("key", func() (int, error) { - return 100, nil - }) - assert.NoError(t, err) - assert.Equal(t, 0, data) - Remove("key") - - data, err = GetInt("key", func() (int, error) { - return 100, nil - }) - assert.NoError(t, err) - assert.Equal(t, 100, data) - - data, err = GetInt("key", func() (int, error) { - return 0, fmt.Errorf("some error") - }) - assert.NoError(t, err) - assert.Equal(t, 100, data) - Remove("key") -} - func TestGetInt64(t *testing.T) { createTestCache() diff --git a/modules/cache/cache_twoqueue.go b/modules/cache/cache_twoqueue.go index f9de2563ec72..1eda2debc43a 100644 --- a/modules/cache/cache_twoqueue.go +++ b/modules/cache/cache_twoqueue.go @@ -10,7 +10,7 @@ import ( "code.gitea.io/gitea/modules/json" - mc "gitea.com/go-chi/cache" + mc "gitea.com/go-chi/cache" //nolint:depguard lru "github.com/hashicorp/golang-lru/v2" ) diff --git a/modules/cache/string_cache.go b/modules/cache/string_cache.go new file mode 100644 index 000000000000..4f659616f501 --- /dev/null +++ b/modules/cache/string_cache.go @@ -0,0 +1,120 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package cache + +import ( + "errors" + "strings" + + "code.gitea.io/gitea/modules/json" + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/util" + + chi_cache "gitea.com/go-chi/cache" //nolint:depguard +) + +type GetJSONError struct { + err error + cachedError string // Golang error can't be stored in cache, only the string message could be stored +} + +func (e *GetJSONError) ToError() error { + if e.err != nil { + return e.err + } + return errors.New("cached error: " + e.cachedError) +} + +type StringCache interface { + Ping() error + + Get(key string) (string, bool) + Put(key, value string, ttl int64) error + Delete(key string) error + IsExist(key string) bool + + PutJSON(key string, v any, ttl int64) error + GetJSON(key string, ptr any) (exist bool, err *GetJSONError) + + ChiCache() chi_cache.Cache +} + +type stringCache struct { + chiCache chi_cache.Cache +} + +func NewStringCache(cacheConfig setting.Cache) (StringCache, error) { + adapter := util.IfZero(cacheConfig.Adapter, "memory") + interval := util.IfZero(cacheConfig.Interval, 60) + cc, err := chi_cache.NewCacher(chi_cache.Options{ + Adapter: adapter, + AdapterConfig: cacheConfig.Conn, + Interval: interval, + }) + if err != nil { + return nil, err + } + return &stringCache{chiCache: cc}, nil +} + +func (sc *stringCache) Ping() error { + return sc.chiCache.Ping() +} + +func (sc *stringCache) Get(key string) (string, bool) { + v := sc.chiCache.Get(key) + if v == nil { + return "", false + } + s, ok := v.(string) + return s, ok +} + +func (sc *stringCache) Put(key, value string, ttl int64) error { + return sc.chiCache.Put(key, value, ttl) +} + +func (sc *stringCache) Delete(key string) error { + return sc.chiCache.Delete(key) +} + +func (sc *stringCache) IsExist(key string) bool { + return sc.chiCache.IsExist(key) +} + +const cachedErrorPrefix = ":" + +func (sc *stringCache) PutJSON(key string, v any, ttl int64) error { + var s string + switch v := v.(type) { + case error: + s = cachedErrorPrefix + v.Error() + default: + b, err := json.Marshal(v) + if err != nil { + return err + } + s = util.UnsafeBytesToString(b) + } + return sc.chiCache.Put(key, s, ttl) +} + +func (sc *stringCache) GetJSON(key string, ptr any) (exist bool, getErr *GetJSONError) { + s, ok := sc.Get(key) + if !ok || s == "" { + return false, nil + } + s, isCachedError := strings.CutPrefix(s, cachedErrorPrefix) + if isCachedError { + return true, &GetJSONError{cachedError: s} + } + if err := json.Unmarshal(util.UnsafeStringToBytes(s), ptr); err != nil { + return false, &GetJSONError{err: err} + } + return true, nil +} + +func (sc *stringCache) ChiCache() chi_cache.Cache { + return sc.chiCache +} diff --git a/modules/git/last_commit_cache.go b/modules/git/last_commit_cache.go index 5b62b90b2795..cf9c10d7b468 100644 --- a/modules/git/last_commit_cache.go +++ b/modules/git/last_commit_cache.go @@ -7,18 +7,11 @@ import ( "crypto/sha256" "fmt" + "code.gitea.io/gitea/modules/cache" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" ) -// Cache represents a caching interface -type Cache interface { - // Put puts value into cache with key and expire time. - Put(key string, val any, timeout int64) error - // Get gets cached value by given key. - Get(key string) any -} - func getCacheKey(repoPath, commitID, entryPath string) string { hashBytes := sha256.Sum256([]byte(fmt.Sprintf("%s:%s:%s", repoPath, commitID, entryPath))) return fmt.Sprintf("last_commit:%x", hashBytes) @@ -30,11 +23,11 @@ type LastCommitCache struct { ttl func() int64 repo *Repository commitCache map[string]*Commit - cache Cache + cache cache.StringCache } // NewLastCommitCache creates a new last commit cache for repo -func NewLastCommitCache(count int64, repoPath string, gitRepo *Repository, cache Cache) *LastCommitCache { +func NewLastCommitCache(count int64, repoPath string, gitRepo *Repository, cache cache.StringCache) *LastCommitCache { if cache == nil { return nil } @@ -65,7 +58,7 @@ func (c *LastCommitCache) Get(ref, entryPath string) (*Commit, error) { return nil, nil } - commitID, ok := c.cache.Get(getCacheKey(c.repoPath, ref, entryPath)).(string) + commitID, ok := c.cache.Get(getCacheKey(c.repoPath, ref, entryPath)) if !ok || commitID == "" { return nil, nil } diff --git a/routers/api/v1/misc/nodeinfo.go b/routers/api/v1/misc/nodeinfo.go index 3bd80de5c18e..597372478260 100644 --- a/routers/api/v1/misc/nodeinfo.go +++ b/routers/api/v1/misc/nodeinfo.go @@ -29,9 +29,7 @@ func NodeInfo(ctx *context.APIContext) { nodeInfoUsage := structs.NodeInfoUsage{} if setting.Federation.ShareUserStatistics { - var cached bool - nodeInfoUsage, cached = ctx.Cache.Get(cacheKeyNodeInfoUsage).(structs.NodeInfoUsage) - + cached, _ := ctx.Cache.GetJSON(cacheKeyNodeInfoUsage, &nodeInfoUsage) if !cached { usersTotal := int(user_model.CountUsers(ctx, nil)) now := time.Now() @@ -53,7 +51,7 @@ func NodeInfo(ctx *context.APIContext) { LocalComments: int(allComments), } - if err := ctx.Cache.Put(cacheKeyNodeInfoUsage, nodeInfoUsage, 180); err != nil { + if err := ctx.Cache.PutJSON(cacheKeyNodeInfoUsage, nodeInfoUsage, 180); err != nil { ctx.InternalServerError(err) return } diff --git a/services/context/api.go b/services/context/api.go index b18a206b5e28..c684add297d1 100644 --- a/services/context/api.go +++ b/services/context/api.go @@ -13,7 +13,7 @@ import ( "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user" - mc "code.gitea.io/gitea/modules/cache" + "code.gitea.io/gitea/modules/cache" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/httpcache" @@ -21,15 +21,13 @@ import ( "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/web" web_types "code.gitea.io/gitea/modules/web/types" - - "gitea.com/go-chi/cache" ) // APIContext is a specific context for API service type APIContext struct { *Base - Cache cache.Cache + Cache cache.StringCache Doer *user_model.User // current signed-in user IsSigned bool @@ -217,7 +215,7 @@ func APIContexter() func(http.Handler) http.Handler { base, baseCleanUp := NewBaseContext(w, req) ctx := &APIContext{ Base: base, - Cache: mc.GetCache(), + Cache: cache.GetCache(), Repo: &Repository{PullRequest: &PullRequest{}}, Org: &APIOrganization{}, } diff --git a/services/context/captcha.go b/services/context/captcha.go index fa8d779f5655..41afe0e7d25a 100644 --- a/services/context/captcha.go +++ b/services/context/captcha.go @@ -30,7 +30,7 @@ func GetImageCaptcha() *captcha.Captcha { cpt = captcha.NewCaptcha(captcha.Options{ SubURL: setting.AppSubURL, }) - cpt.Store = cache.GetCache() + cpt.Store = cache.GetCache().ChiCache() }) return cpt } diff --git a/services/context/context.go b/services/context/context.go index 4b318f7e338f..7ab48afb73de 100644 --- a/services/context/context.go +++ b/services/context/context.go @@ -17,7 +17,7 @@ import ( "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user" - mc "code.gitea.io/gitea/modules/cache" + "code.gitea.io/gitea/modules/cache" "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/httpcache" "code.gitea.io/gitea/modules/setting" @@ -27,7 +27,6 @@ import ( "code.gitea.io/gitea/modules/web/middleware" web_types "code.gitea.io/gitea/modules/web/types" - "gitea.com/go-chi/cache" "gitea.com/go-chi/session" ) @@ -46,7 +45,7 @@ type Context struct { Render Render PageData map[string]any // data used by JavaScript modules in one page, it's `window.config.pageData` - Cache cache.Cache + Cache cache.StringCache Csrf CSRFProtector Flash *middleware.Flash Session session.Store @@ -111,7 +110,7 @@ func NewWebContext(base *Base, render Render, session session.Store) *Context { Render: render, Session: session, - Cache: mc.GetCache(), + Cache: cache.GetCache(), Link: setting.AppSubURL + strings.TrimSuffix(base.Req.URL.EscapedPath(), "/"), Repo: &Repository{PullRequest: &PullRequest{}}, Org: &Organization{}, diff --git a/services/repository/branch.go b/services/repository/branch.go index 229ac54f307d..d74e5819a1c4 100644 --- a/services/repository/branch.go +++ b/services/repository/branch.go @@ -26,6 +26,7 @@ import ( "code.gitea.io/gitea/modules/queue" repo_module "code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/timeutil" + "code.gitea.io/gitea/modules/util" webhook_module "code.gitea.io/gitea/modules/webhook" notify_service "code.gitea.io/gitea/services/notify" files_service "code.gitea.io/gitea/services/repository/files" @@ -119,17 +120,15 @@ func getDivergenceCacheKey(repoID int64, branchName string) string { // getDivergenceFromCache gets the divergence from cache func getDivergenceFromCache(repoID int64, branchName string) (*git.DivergeObject, bool) { - data := cache.GetCache().Get(getDivergenceCacheKey(repoID, branchName)) + data, ok := cache.GetCache().Get(getDivergenceCacheKey(repoID, branchName)) res := git.DivergeObject{ Ahead: -1, Behind: -1, } - s, ok := data.([]byte) - if !ok || len(s) == 0 { + if !ok || data == "" { return &res, false } - - if err := json.Unmarshal(s, &res); err != nil { + if err := json.Unmarshal(util.UnsafeStringToBytes(data), &res); err != nil { log.Error("json.UnMarshal failed: %v", err) return &res, false } @@ -141,7 +140,7 @@ func putDivergenceFromCache(repoID int64, branchName string, divergence *git.Div if err != nil { return err } - return cache.GetCache().Put(getDivergenceCacheKey(repoID, branchName), bs, 30*24*60*60) + return cache.GetCache().Put(getDivergenceCacheKey(repoID, branchName), util.UnsafeBytesToString(bs), 30*24*60*60) } func DelDivergenceFromCache(repoID int64, branchName string) error { diff --git a/services/repository/commitstatus/commitstatus.go b/services/repository/commitstatus/commitstatus.go index 7c1c6c260938..8a62a603d460 100644 --- a/services/repository/commitstatus/commitstatus.go +++ b/services/repository/commitstatus/commitstatus.go @@ -34,7 +34,7 @@ type commitStatusCacheValue struct { func getCommitStatusCache(repoID int64, branchName string) *commitStatusCacheValue { c := cache.GetCache() - statusStr, ok := c.Get(getCacheKey(repoID, branchName)).(string) + statusStr, ok := c.Get(getCacheKey(repoID, branchName)) if ok && statusStr != "" { var cv commitStatusCacheValue err := json.Unmarshal([]byte(statusStr), &cv) diff --git a/services/repository/contributors_graph.go b/services/repository/contributors_graph.go index 7c9f535ae01a..b0d6de99ca90 100644 --- a/services/repository/contributors_graph.go +++ b/services/repository/contributors_graph.go @@ -17,13 +17,12 @@ import ( "code.gitea.io/gitea/models/avatars" repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/cache" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/log" api "code.gitea.io/gitea/modules/structs" - - "gitea.com/go-chi/cache" ) const ( @@ -79,13 +78,13 @@ func findLastSundayBeforeDate(dateStr string) (string, error) { } // GetContributorStats returns contributors stats for git commits for given revision or default branch -func GetContributorStats(ctx context.Context, cache cache.Cache, repo *repo_model.Repository, revision string) (map[string]*ContributorData, error) { +func GetContributorStats(ctx context.Context, cache cache.StringCache, repo *repo_model.Repository, revision string) (map[string]*ContributorData, error) { // as GetContributorStats is resource intensive we cache the result cacheKey := fmt.Sprintf(contributorStatsCacheKey, repo.FullName(), revision) if !cache.IsExist(cacheKey) { genReady := make(chan struct{}) - // dont start multible async generations + // dont start multiple async generations _, run := generateLock.Load(cacheKey) if run { return nil, ErrAwaitGeneration @@ -104,15 +103,11 @@ func GetContributorStats(ctx context.Context, cache cache.Cache, repo *repo_mode } } // TODO: renew timeout of cache cache.UpdateTimeout(cacheKey, contributorStatsCacheTimeout) - - switch v := cache.Get(cacheKey).(type) { - case error: - return nil, v - case map[string]*ContributorData: - return v, nil - default: - return nil, fmt.Errorf("unexpected type in cache detected") + var res map[string]*ContributorData + if _, cacheErr := cache.GetJSON(cacheKey, &res); cacheErr != nil { + return nil, fmt.Errorf("cached error: %w", cacheErr.ToError()) } + return res, nil } // getExtendedCommitStats return the list of *ExtendedCommitStats for the given revision @@ -205,13 +200,12 @@ func getExtendedCommitStats(repo *git.Repository, revision string /*, limit int return extendedCommitStats, nil } -func generateContributorStats(genDone chan struct{}, cache cache.Cache, cacheKey string, repo *repo_model.Repository, revision string) { +func generateContributorStats(genDone chan struct{}, cache cache.StringCache, cacheKey string, repo *repo_model.Repository, revision string) { ctx := graceful.GetManager().HammerContext() gitRepo, closer, err := gitrepo.RepositoryFromContextOrOpen(ctx, repo) if err != nil { - err := fmt.Errorf("OpenRepository: %w", err) - _ = cache.Put(cacheKey, err, contributorStatsCacheTimeout) + _ = cache.PutJSON(cacheKey, fmt.Errorf("OpenRepository: %w", err), contributorStatsCacheTimeout) return } defer closer.Close() @@ -221,13 +215,11 @@ func generateContributorStats(genDone chan struct{}, cache cache.Cache, cacheKey } extendedCommitStats, err := getExtendedCommitStats(gitRepo, revision) if err != nil { - err := fmt.Errorf("ExtendedCommitStats: %w", err) - _ = cache.Put(cacheKey, err, contributorStatsCacheTimeout) + _ = cache.PutJSON(cacheKey, fmt.Errorf("ExtendedCommitStats: %w", err), contributorStatsCacheTimeout) return } if len(extendedCommitStats) == 0 { - err := fmt.Errorf("no commit stats returned for revision '%s'", revision) - _ = cache.Put(cacheKey, err, contributorStatsCacheTimeout) + _ = cache.PutJSON(cacheKey, fmt.Errorf("no commit stats returned for revision '%s'", revision), contributorStatsCacheTimeout) return } @@ -309,7 +301,7 @@ func generateContributorStats(genDone chan struct{}, cache cache.Cache, cacheKey total.TotalCommits++ } - _ = cache.Put(cacheKey, contributorsCommitStats, contributorStatsCacheTimeout) + _ = cache.PutJSON(cacheKey, contributorsCommitStats, contributorStatsCacheTimeout) generateLock.Delete(cacheKey) if genDone != nil { genDone <- struct{}{} diff --git a/services/repository/contributors_graph_test.go b/services/repository/contributors_graph_test.go index 3801a5eee4bf..f22c115276ee 100644 --- a/services/repository/contributors_graph_test.go +++ b/services/repository/contributors_graph_test.go @@ -10,9 +10,9 @@ import ( "code.gitea.io/gitea/models/db" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" - "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/cache" + "code.gitea.io/gitea/modules/setting" - "gitea.com/go-chi/cache" "github.com/stretchr/testify/assert" ) @@ -20,20 +20,18 @@ func TestRepository_ContributorsGraph(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}) assert.NoError(t, repo.LoadOwner(db.DefaultContext)) - mockCache, err := cache.NewCacher(cache.Options{ - Adapter: "memory", - Interval: 24 * 60, - }) + mockCache, err := cache.NewStringCache(setting.Cache{}) assert.NoError(t, err) generateContributorStats(nil, mockCache, "key", repo, "404ref") - err, isErr := mockCache.Get("key").(error) - assert.True(t, isErr) - assert.ErrorAs(t, err, &git.ErrNotExist{}) + var data map[string]*ContributorData + _, getErr := mockCache.GetJSON("key", &data) + assert.NotNil(t, getErr) + assert.ErrorContains(t, getErr.ToError(), "object does not exist") generateContributorStats(nil, mockCache, "key2", repo, "master") - data, isData := mockCache.Get("key2").(map[string]*ContributorData) - assert.True(t, isData) + exist, _ := mockCache.GetJSON("key2", &data) + assert.True(t, exist) var keys []string for k := range data { keys = append(keys, k) From c28bed27af82b664df943e5fd3d9bd07fb2f2b77 Mon Sep 17 00:00:00 2001 From: silverwind Date: Sat, 13 Apr 2024 10:54:36 +0200 Subject: [PATCH 21/35] Update JS and PY deps, lock eslint and related plugins (#30452) Update all JS dependencies and lock eslint and flat-only plugins. There is at least the blocker https://github.com/SonarSource/eslint-plugin-sonarjs/issues/454 preventing us from upgrading to eslint 9. Tested API spec, charts and absolute dates. --- package-lock.json | 477 ++++++++++++++++++++++------------------------ package.json | 24 +-- poetry.lock | 6 +- updates.config.js | 2 + 4 files changed, 248 insertions(+), 261 deletions(-) diff --git a/package-lock.json b/package-lock.json index 35bf886fc8da..61d86f6b7c26 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,7 +21,7 @@ "chartjs-adapter-dayjs-4": "1.0.4", "chartjs-plugin-zoom": "2.0.1", "clippie": "4.0.7", - "css-loader": "7.0.0", + "css-loader": "7.1.1", "dayjs": "1.11.10", "dropzone": "6.0.0-beta.2", "easymde": "2.18.0", @@ -44,9 +44,9 @@ "postcss-nesting": "12.1.1", "pretty-ms": "9.0.0", "sortablejs": "1.15.2", - "swagger-ui-dist": "5.13.0", + "swagger-ui-dist": "5.15.1", "tailwindcss": "3.4.3", - "temporal-polyfill": "0.2.3", + "temporal-polyfill": "0.2.4", "throttle-debounce": "5.0.0", "tinycolor2": "1.6.0", "tippy.js": "6.3.7", @@ -56,7 +56,7 @@ "vanilla-colorful": "0.7.2", "vue": "3.4.21", "vue-bar-graph": "2.0.0", - "vue-chartjs": "5.3.0", + "vue-chartjs": "5.3.1", "vue-loader": "17.4.2", "vue3-calendar-heatmap": "2.0.5", "webpack": "5.91.0", @@ -64,8 +64,8 @@ "wrap-ansi": "9.0.0" }, "devDependencies": { - "@eslint-community/eslint-plugin-eslint-comments": "4.1.0", - "@playwright/test": "1.42.1", + "@eslint-community/eslint-plugin-eslint-comments": "4.3.0", + "@playwright/test": "1.43.1", "@stoplight/spectral-cli": "6.11.1", "@stylistic/eslint-plugin-js": "1.7.0", "@stylistic/stylelint-plugin": "2.1.1", @@ -77,15 +77,15 @@ "eslint-plugin-jquery": "1.5.1", "eslint-plugin-no-jquery": "2.7.0", "eslint-plugin-no-use-extend-native": "0.5.0", - "eslint-plugin-regexp": "2.4.0", + "eslint-plugin-regexp": "2.5.0", "eslint-plugin-sonarjs": "0.25.1", "eslint-plugin-unicorn": "52.0.0", "eslint-plugin-vitest": "0.4.1", "eslint-plugin-vitest-globals": "1.5.0", - "eslint-plugin-vue": "9.24.0", + "eslint-plugin-vue": "9.24.1", "eslint-plugin-vue-scoped-css": "2.8.0", - "eslint-plugin-wc": "2.0.4", - "happy-dom": "14.5.0", + "eslint-plugin-wc": "2.1.0", + "happy-dom": "14.7.1", "markdownlint-cli": "0.39.0", "postcss-html": "1.6.0", "stylelint": "16.3.1", @@ -93,9 +93,9 @@ "stylelint-declaration-strict-value": "1.10.4", "stylelint-value-no-unknown-custom-properties": "6.0.1", "svgo": "3.2.0", - "updates": "16.0.0", + "updates": "16.0.1", "vite-string-plugin": "1.1.5", - "vitest": "1.4.0" + "vitest": "1.5.0" }, "engines": { "node": ">= 18.0.0" @@ -865,9 +865,9 @@ } }, "node_modules/@eslint-community/eslint-plugin-eslint-comments": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-plugin-eslint-comments/-/eslint-plugin-eslint-comments-4.1.0.tgz", - "integrity": "sha512-B2mwipifrBS5E00vN8vME68laPMZ0h3sNGOEDj5g9iUN9k5EU99Omq0Nc325eKNoFFDnDtiHp3DqIjO+1bstag==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-plugin-eslint-comments/-/eslint-plugin-eslint-comments-4.3.0.tgz", + "integrity": "sha512-6e93KtgsndNkvwCCa07LOQJSwzzLLxwrFll3+huyFoiiQXWG0KBcmo0Q1bVgYQQDLfWOOZl2VPBsXqZL6vHIBQ==", "dev": true, "dependencies": { "escape-string-regexp": "^4.0.0", @@ -877,7 +877,7 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0" } }, "node_modules/@eslint-community/eslint-utils": { @@ -1344,12 +1344,12 @@ } }, "node_modules/@playwright/test": { - "version": "1.42.1", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.42.1.tgz", - "integrity": "sha512-Gq9rmS54mjBL/7/MvBaNOBwbfnh7beHvS6oS4srqXFcQHpQCV1+c8JXWE8VLPyRDhgS3H8x8A7hztqI9VnwrAQ==", + "version": "1.43.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.43.1.tgz", + "integrity": "sha512-HgtQzFgNEEo4TE22K/X7sYTYNqEMMTZmFS8kTq6m8hXj+m1D8TgwgIbumHddJa9h4yl4GkKb8/bgAl2+g7eDgA==", "dev": true, "dependencies": { - "playwright": "1.42.1" + "playwright": "1.43.1" }, "bin": { "playwright": "cli.js" @@ -1420,9 +1420,9 @@ "dev": true }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.14.0.tgz", - "integrity": "sha512-jwXtxYbRt1V+CdQSy6Z+uZti7JF5irRKF8hlKfEnF/xJpcNGuuiZMBvuoYM+x9sr9iWGnzrlM0+9hvQ1kgkf1w==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.14.2.tgz", + "integrity": "sha512-ahxSgCkAEk+P/AVO0vYr7DxOD3CwAQrT0Go9BJyGQ9Ef0QxVOfjDZMiF4Y2s3mLyPrjonchIMH/tbWHucJMykQ==", "cpu": [ "arm" ], @@ -1433,9 +1433,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.14.0.tgz", - "integrity": "sha512-fI9nduZhCccjzlsA/OuAwtFGWocxA4gqXGTLvOyiF8d+8o0fZUeSztixkYjcGq1fGZY3Tkq4yRvHPFxU+jdZ9Q==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.14.2.tgz", + "integrity": "sha512-lAarIdxZWbFSHFSDao9+I/F5jDaKyCqAPMq5HqnfpBw8dKDiCaaqM0lq5h1pQTLeIqueeay4PieGR5jGZMWprw==", "cpu": [ "arm64" ], @@ -1446,9 +1446,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.14.0.tgz", - "integrity": "sha512-BcnSPRM76/cD2gQC+rQNGBN6GStBs2pl/FpweW8JYuz5J/IEa0Fr4AtrPv766DB/6b2MZ/AfSIOSGw3nEIP8SA==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.14.2.tgz", + "integrity": "sha512-SWsr8zEUk82KSqquIMgZEg2GE5mCSfr9sE/thDROkX6pb3QQWPp8Vw8zOq2GyxZ2t0XoSIUlvHDkrf5Gmf7x3Q==", "cpu": [ "arm64" ], @@ -1459,9 +1459,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.14.0.tgz", - "integrity": "sha512-LDyFB9GRolGN7XI6955aFeI3wCdCUszFWumWU0deHA8VpR3nWRrjG6GtGjBrQxQKFevnUTHKCfPR4IvrW3kCgQ==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.14.2.tgz", + "integrity": "sha512-o/HAIrQq0jIxJAhgtIvV5FWviYK4WB0WwV91SLUnsliw1lSAoLsmgEEgRWzDguAFeUEUUoIWXiJrPqU7vGiVkA==", "cpu": [ "x64" ], @@ -1472,9 +1472,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.14.0.tgz", - "integrity": "sha512-ygrGVhQP47mRh0AAD0zl6QqCbNsf0eTo+vgwkY6LunBcg0f2Jv365GXlDUECIyoXp1kKwL5WW6rsO429DBY/bA==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.14.2.tgz", + "integrity": "sha512-nwlJ65UY9eGq91cBi6VyDfArUJSKOYt5dJQBq8xyLhvS23qO+4Nr/RreibFHjP6t+5ap2ohZrUJcHv5zk5ju/g==", "cpu": [ "arm" ], @@ -1485,9 +1485,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.14.0.tgz", - "integrity": "sha512-x+uJ6MAYRlHGe9wi4HQjxpaKHPM3d3JjqqCkeC5gpnnI6OWovLdXTpfa8trjxPLnWKyBsSi5kne+146GAxFt4A==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.14.2.tgz", + "integrity": "sha512-Pg5TxxO2IVlMj79+c/9G0LREC9SY3HM+pfAwX7zj5/cAuwrbfj2Wv9JbMHIdPCfQpYsI4g9mE+2Bw/3aeSs2rQ==", "cpu": [ "arm64" ], @@ -1498,9 +1498,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.14.0.tgz", - "integrity": "sha512-nrRw8ZTQKg6+Lttwqo6a2VxR9tOroa2m91XbdQ2sUUzHoedXlsyvY1fN4xWdqz8PKmf4orDwejxXHjh7YBGUCA==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.14.2.tgz", + "integrity": "sha512-cAOTjGNm84gc6tS02D1EXtG7tDRsVSDTBVXOLbj31DkwfZwgTPYZ6aafSU7rD/4R2a34JOwlF9fQayuTSkoclA==", "cpu": [ "arm64" ], @@ -1511,11 +1511,11 @@ ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.14.0.tgz", - "integrity": "sha512-xV0d5jDb4aFu84XKr+lcUJ9y3qpIWhttO3Qev97z8DKLXR62LC3cXT/bMZXrjLF9X+P5oSmJTzAhqwUbY96PnA==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.14.2.tgz", + "integrity": "sha512-4RyT6v1kXb7C0fn6zV33rvaX05P0zHoNzaXI/5oFHklfKm602j+N4mn2YvoezQViRLPnxP8M1NaY4s/5kXO5cw==", "cpu": [ - "ppc64le" + "ppc64" ], "dev": true, "optional": true, @@ -1524,9 +1524,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.14.0.tgz", - "integrity": "sha512-SDDhBQwZX6LPRoPYjAZWyL27LbcBo7WdBFWJi5PI9RPCzU8ijzkQn7tt8NXiXRiFMJCVpkuMkBf4OxSxVMizAw==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.14.2.tgz", + "integrity": "sha512-KNUH6jC/vRGAKSorySTyc/yRYlCwN/5pnMjXylfBniwtJx5O7X17KG/0efj8XM3TZU7raYRXJFFReOzNmL1n1w==", "cpu": [ "riscv64" ], @@ -1537,9 +1537,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.14.0.tgz", - "integrity": "sha512-RxB/qez8zIDshNJDufYlTT0ZTVut5eCpAZ3bdXDU9yTxBzui3KhbGjROK2OYTTor7alM7XBhssgoO3CZ0XD3qA==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.14.2.tgz", + "integrity": "sha512-xPV4y73IBEXToNPa3h5lbgXOi/v0NcvKxU0xejiFw6DtIYQqOTMhZ2DN18/HrrP0PmiL3rGtRG9gz1QE8vFKXQ==", "cpu": [ "s390x" ], @@ -1550,9 +1550,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.14.0.tgz", - "integrity": "sha512-C6y6z2eCNCfhZxT9u+jAM2Fup89ZjiG5pIzZIDycs1IwESviLxwkQcFRGLjnDrP+PT+v5i4YFvlcfAs+LnreXg==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.14.2.tgz", + "integrity": "sha512-QBhtr07iFGmF9egrPOWyO5wciwgtzKkYPNLVCFZTmr4TWmY0oY2Dm/bmhHjKRwZoGiaKdNcKhFtUMBKvlchH+Q==", "cpu": [ "x64" ], @@ -1563,9 +1563,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.14.0.tgz", - "integrity": "sha512-i0QwbHYfnOMYsBEyjxcwGu5SMIi9sImDVjDg087hpzXqhBSosxkE7gyIYFHgfFl4mr7RrXksIBZ4DoLoP4FhJg==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.14.2.tgz", + "integrity": "sha512-8zfsQRQGH23O6qazZSFY5jP5gt4cFvRuKTpuBsC1ZnSWxV8ZKQpPqOZIUtdfMOugCcBvFGRa1pDC/tkf19EgBw==", "cpu": [ "x64" ], @@ -1576,9 +1576,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.14.0.tgz", - "integrity": "sha512-Fq52EYb0riNHLBTAcL0cun+rRwyZ10S9vKzhGKKgeD+XbwunszSY0rVMco5KbOsTlwovP2rTOkiII/fQ4ih/zQ==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.14.2.tgz", + "integrity": "sha512-H4s8UjgkPnlChl6JF5empNvFHp77Jx+Wfy2EtmYPe9G22XV+PMuCinZVHurNe8ggtwoaohxARJZbaH/3xjB/FA==", "cpu": [ "arm64" ], @@ -1589,9 +1589,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.14.0.tgz", - "integrity": "sha512-e/PBHxPdJ00O9p5Ui43+vixSgVf4NlLsmV6QneGERJ3lnjIua/kim6PRFe3iDueT1rQcgSkYP8ZBBXa/h4iPvw==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.14.2.tgz", + "integrity": "sha512-djqpAjm/i8erWYF0K6UY4kRO3X5+T4TypIqw60Q8MTqSBaQNpNXDhxdjpZ3ikgb+wn99svA7jxcXpiyg9MUsdw==", "cpu": [ "ia32" ], @@ -1602,9 +1602,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.14.0.tgz", - "integrity": "sha512-aGg7iToJjdklmxlUlJh/PaPNa4PmqHfyRMLunbL3eaMO0gp656+q1zOKkpJ/CVe9CryJv6tAN1HDoR8cNGzkag==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.14.2.tgz", + "integrity": "sha512-teAqzLT0yTYZa8ZP7zhFKEx4cotS8Tkk5XiqNMJhD4CpaWB1BHARE4Qy+RzwnXvSAYv+Q3jAqCVBS+PS+Yee8Q==", "cpu": [ "x64" ], @@ -2217,9 +2217,9 @@ } }, "node_modules/@types/eslint": { - "version": "8.56.7", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.7.tgz", - "integrity": "sha512-SjDvI/x3zsZnOkYZ3lCt9lOZWZLB2jIlNKz+LBgCtDurK0JZcwucxYHn1w2BJkD34dgX9Tjnak0txtq4WTggEA==", + "version": "8.56.9", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.9.tgz", + "integrity": "sha512-W4W3KcqzjJ0sHg2vAq9vfml6OhsJ53TcUjUqfzzZf/EChUtwspszj/S0pzMxnfRcO55/iGq47dscXw71Fxc4Zg==", "dependencies": { "@types/estree": "*", "@types/json-schema": "*" @@ -2269,9 +2269,9 @@ "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==" }, "node_modules/@types/node": { - "version": "20.12.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.4.tgz", - "integrity": "sha512-E+Fa9z3wSQpzgYQdYmme5X3OTuejnnTx88A6p6vkkJosR3KBz+HpE3kqNm98VE6cfLFcISx7zW7MsJkH6KwbTw==", + "version": "20.12.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.7.tgz", + "integrity": "sha512-wq0cICSkRLVaf3UGLMGItu/PtdY7oaXaI/RVU+xliKVOtRna3PRY57ZDfztpDL0n11vfymMUnXv8QwYCO7L1wg==", "dependencies": { "undici-types": "~5.26.4" } @@ -2314,22 +2314,22 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.5.0.tgz", - "integrity": "sha512-HpqNTH8Du34nLxbKgVMGljZMG0rJd2O9ecvr2QLYp+7512ty1j42KnsFwspPXg1Vh8an9YImf6CokUBltisZFQ==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.6.0.tgz", + "integrity": "sha512-gKmTNwZnblUdnTIJu3e9kmeRRzV2j1a/LUO27KNNAnIC5zjy1aSvXSRp4rVNlmAoHlQ7HzX42NbKpcSr4jF80A==", "dev": true, "dependencies": { - "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "7.5.0", - "@typescript-eslint/type-utils": "7.5.0", - "@typescript-eslint/utils": "7.5.0", - "@typescript-eslint/visitor-keys": "7.5.0", + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "7.6.0", + "@typescript-eslint/type-utils": "7.6.0", + "@typescript-eslint/utils": "7.6.0", + "@typescript-eslint/visitor-keys": "7.6.0", "debug": "^4.3.4", "graphemer": "^1.4.0", - "ignore": "^5.2.4", + "ignore": "^5.3.1", "natural-compare": "^1.4.0", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -2349,15 +2349,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.5.0.tgz", - "integrity": "sha512-cj+XGhNujfD2/wzR1tabNsidnYRaFfEkcULdcIyVBYcXjBvBKOes+mpMBP7hMpOyk+gBcfXsrg4NBGAStQyxjQ==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.6.0.tgz", + "integrity": "sha512-usPMPHcwX3ZoPWnBnhhorc14NJw9J4HpSXQX4urF2TPKG0au0XhJoZyX62fmvdHONUkmyUe74Hzm1//XA+BoYg==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "7.5.0", - "@typescript-eslint/types": "7.5.0", - "@typescript-eslint/typescript-estree": "7.5.0", - "@typescript-eslint/visitor-keys": "7.5.0", + "@typescript-eslint/scope-manager": "7.6.0", + "@typescript-eslint/types": "7.6.0", + "@typescript-eslint/typescript-estree": "7.6.0", + "@typescript-eslint/visitor-keys": "7.6.0", "debug": "^4.3.4" }, "engines": { @@ -2377,13 +2377,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.5.0.tgz", - "integrity": "sha512-Z1r7uJY0MDeUlql9XJ6kRVgk/sP11sr3HKXn268HZyqL7i4cEfrdFuSSY/0tUqT37l5zT0tJOsuDP16kio85iA==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.6.0.tgz", + "integrity": "sha512-ngttyfExA5PsHSx0rdFgnADMYQi+Zkeiv4/ZxGYUWd0nLs63Ha0ksmp8VMxAIC0wtCFxMos7Lt3PszJssG/E6w==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.5.0", - "@typescript-eslint/visitor-keys": "7.5.0" + "@typescript-eslint/types": "7.6.0", + "@typescript-eslint/visitor-keys": "7.6.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -2394,15 +2394,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.5.0.tgz", - "integrity": "sha512-A021Rj33+G8mx2Dqh0nMO9GyjjIBK3MqgVgZ2qlKf6CJy51wY/lkkFqq3TqqnH34XyAHUkq27IjlUkWlQRpLHw==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.6.0.tgz", + "integrity": "sha512-NxAfqAPNLG6LTmy7uZgpK8KcuiS2NZD/HlThPXQRGwz6u7MDBWRVliEEl1Gj6U7++kVJTpehkhZzCJLMK66Scw==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "7.5.0", - "@typescript-eslint/utils": "7.5.0", + "@typescript-eslint/typescript-estree": "7.6.0", + "@typescript-eslint/utils": "7.6.0", "debug": "^4.3.4", - "ts-api-utils": "^1.0.1" + "ts-api-utils": "^1.3.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -2421,9 +2421,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.5.0.tgz", - "integrity": "sha512-tv5B4IHeAdhR7uS4+bf8Ov3k793VEVHd45viRRkehIUZxm0WF82VPiLgHzA/Xl4TGPg1ZD49vfxBKFPecD5/mg==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.6.0.tgz", + "integrity": "sha512-h02rYQn8J+MureCvHVVzhl69/GAfQGPQZmOMjG1KfCl7o3HtMSlPaPUAPu6lLctXI5ySRGIYk94clD/AUMCUgQ==", "dev": true, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -2434,19 +2434,19 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.5.0.tgz", - "integrity": "sha512-YklQQfe0Rv2PZEueLTUffiQGKQneiIEKKnfIqPIOxgM9lKSZFCjT5Ad4VqRKj/U4+kQE3fa8YQpskViL7WjdPQ==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.6.0.tgz", + "integrity": "sha512-+7Y/GP9VuYibecrCQWSKgl3GvUM5cILRttpWtnAu8GNL9j11e4tbuGZmZjJ8ejnKYyBRb2ddGQ3rEFCq3QjMJw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.5.0", - "@typescript-eslint/visitor-keys": "7.5.0", + "@typescript-eslint/types": "7.6.0", + "@typescript-eslint/visitor-keys": "7.6.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", - "minimatch": "9.0.3", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -2461,34 +2461,19 @@ } } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/@typescript-eslint/utils": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.5.0.tgz", - "integrity": "sha512-3vZl9u0R+/FLQcpy2EHyRGNqAS/ofJ3Ji8aebilfJe+fobK8+LbIFmrHciLVDxjDoONmufDcnVSF38KwMEOjzw==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.6.0.tgz", + "integrity": "sha512-x54gaSsRRI+Nwz59TXpCsr6harB98qjXYzsRxGqvA5Ue3kQH+FxS7FYU81g/omn22ML2pZJkisy6Q+ElK8pBCA==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@types/json-schema": "^7.0.12", - "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "7.5.0", - "@typescript-eslint/types": "7.5.0", - "@typescript-eslint/typescript-estree": "7.5.0", - "semver": "^7.5.4" + "@types/json-schema": "^7.0.15", + "@types/semver": "^7.5.8", + "@typescript-eslint/scope-manager": "7.6.0", + "@typescript-eslint/types": "7.6.0", + "@typescript-eslint/typescript-estree": "7.6.0", + "semver": "^7.6.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -2502,13 +2487,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.5.0.tgz", - "integrity": "sha512-mcuHM/QircmA6O7fy6nn2w/3ditQkj+SgtOc8DW3uQ10Yfj42amm2i+6F2K4YAOPNNTmE6iM1ynM6lrSwdendA==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.6.0.tgz", + "integrity": "sha512-4eLB7t+LlNUmXzfOu1VAIAdkjbu5xNSerURS9X/S5TUKWFRpXRQZbmtPqgKmYx8bj3J0irtQXSiWAOY82v+cgw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.5.0", - "eslint-visitor-keys": "^3.4.1" + "@typescript-eslint/types": "7.6.0", + "eslint-visitor-keys": "^3.4.3" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -2538,13 +2523,13 @@ } }, "node_modules/@vitest/expect": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.4.0.tgz", - "integrity": "sha512-Jths0sWCJZ8BxjKe+p+eKsoqev1/T8lYcrjavEaz8auEJ4jAVY0GwW3JKmdVU4mmNPLPHixh4GNXP7GFtAiDHA==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.5.0.tgz", + "integrity": "sha512-0pzuCI6KYi2SIC3LQezmxujU9RK/vwC1U9R0rLuGlNGcOuDWxqWKu6nUdFsX9tH1WU0SXtAxToOsEjeUn1s3hA==", "dev": true, "dependencies": { - "@vitest/spy": "1.4.0", - "@vitest/utils": "1.4.0", + "@vitest/spy": "1.5.0", + "@vitest/utils": "1.5.0", "chai": "^4.3.10" }, "funding": { @@ -2552,12 +2537,12 @@ } }, "node_modules/@vitest/runner": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.4.0.tgz", - "integrity": "sha512-EDYVSmesqlQ4RD2VvWo3hQgTJ7ZrFQ2VSJdfiJiArkCerDAGeyF1i6dHkmySqk573jLp6d/cfqCN+7wUB5tLgg==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.5.0.tgz", + "integrity": "sha512-7HWwdxXP5yDoe7DTpbif9l6ZmDwCzcSIK38kTSIt6CFEpMjX4EpCgT6wUmS0xTXqMI6E/ONmfgRKmaujpabjZQ==", "dev": true, "dependencies": { - "@vitest/utils": "1.4.0", + "@vitest/utils": "1.5.0", "p-limit": "^5.0.0", "pathe": "^1.1.1" }, @@ -2593,9 +2578,9 @@ } }, "node_modules/@vitest/snapshot": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.4.0.tgz", - "integrity": "sha512-saAFnt5pPIA5qDGxOHxJ/XxhMFKkUSBJmVt5VgDsAqPTX6JP326r5C/c9UuCMPoXNzuudTPsYDZCoJ5ilpqG2A==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.5.0.tgz", + "integrity": "sha512-qpv3fSEuNrhAO3FpH6YYRdaECnnRjg9VxbhdtPwPRnzSfHVXnNzzrpX4cJxqiwgRMo7uRMWDFBlsBq4Cr+rO3A==", "dev": true, "dependencies": { "magic-string": "^0.30.5", @@ -2619,9 +2604,9 @@ } }, "node_modules/@vitest/spy": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.4.0.tgz", - "integrity": "sha512-Ywau/Qs1DzM/8Uc+yA77CwSegizMlcgTJuYGAi0jujOteJOUf1ujunHThYo243KG9nAyWT3L9ifPYZ5+As/+6Q==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.5.0.tgz", + "integrity": "sha512-vu6vi6ew5N5MMHJjD5PoakMRKYdmIrNJmyfkhRpQt5d9Ewhw9nZ5Aqynbi3N61bvk9UvZ5UysMT6ayIrZ8GA9w==", "dev": true, "dependencies": { "tinyspy": "^2.2.0" @@ -2631,9 +2616,9 @@ } }, "node_modules/@vitest/utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.4.0.tgz", - "integrity": "sha512-mx3Yd1/6e2Vt/PUC98DcqTirtfxUyAZ32uK82r8rZzbtBeBo+nqgnjx/LvqQdWsrvNtm14VmurNgcf4nqY5gJg==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.5.0.tgz", + "integrity": "sha512-BDU0GNL8MWkRkSRdNFvCUCAVOeHaUlVJ9Tx0TYBZyXaaOTmGtUFObzchCivIBrIwKzvZA7A9sCejVhXM2aY98A==", "dev": true, "dependencies": { "diff-sequences": "^29.6.3", @@ -3566,9 +3551,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001605", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001605.tgz", - "integrity": "sha512-nXwGlFWo34uliI9z3n6Qc0wZaf7zaZWA1CPZ169La5mV3I/gem7bst0vr5XQH5TJXZIMfDeZyOrZnSlVzKxxHQ==", + "version": "1.0.30001609", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001609.tgz", + "integrity": "sha512-JFPQs34lHKx1B5t1EpQpWH4c+29zIyn/haGsbpfq3suuV9v56enjFt23zqijxGTMwy1p/4H2tjnQMY+p1WoAyA==", "funding": [ { "type": "opencollective", @@ -3960,9 +3945,9 @@ } }, "node_modules/css-loader": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-7.0.0.tgz", - "integrity": "sha512-WrO4FVoamxt5zY9CauZjoJgXRi/LZKIk+Ta7YvpSGr5r/eMYPNp5/T9ODlMe4/1rF5DYlycG1avhV4g3A/tiAw==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-7.1.1.tgz", + "integrity": "sha512-OxIR5P2mjO1PSXk44bWuQ8XtMK4dpEqpIyERCx3ewOo3I8EmbcxMPUc5ScLtQfgXtOojoMv57So4V/C02HQLsw==", "dependencies": { "icss-utils": "^5.1.0", "postcss": "^8.4.33", @@ -4812,9 +4797,9 @@ } }, "node_modules/dompurify": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.0.11.tgz", - "integrity": "sha512-Fan4uMuyB26gFV3ovPoEoQbxRRPfTu3CvImyZnhGq5fsIEO+gEFLp45ISFt+kQBWsK5ulDdT0oV28jS1UrwQLg==" + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.1.0.tgz", + "integrity": "sha512-yoU4rhgPKCo+p5UrWWWNKiIq+ToGqmVVhk0PmMYBK4kRsR3/qhemNFL8f6CFmBd4gMwm3F4T7HBoydP5uY07fA==" }, "node_modules/domutils": { "version": "3.1.0", @@ -4857,9 +4842,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.727", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.727.tgz", - "integrity": "sha512-brpv4KTeC4g0Fx2FeIKytLd4UGn1zBQq5Lauy7zEWT9oqkaj5mgsxblEZIAOf1HHLlXxzr6adGViiBy5Z39/CA==" + "version": "1.4.736", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.736.tgz", + "integrity": "sha512-Rer6wc3ynLelKNM4lOCg7/zPQj8tPOCB2hzD32PX9wd3hgRRi9MxEbmkFCokzcEhRVMiOVLjnL9ig9cefJ+6+Q==" }, "node_modules/elkjs": { "version": "0.9.2", @@ -4911,9 +4896,9 @@ } }, "node_modules/envinfo": { - "version": "7.11.1", - "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.11.1.tgz", - "integrity": "sha512-8PiZgZNIB4q/Lw4AhOvAfB/ityHAd2bli3lESSWmWSzSsl5dKpy5N1d1Rfkd2teq/g9xN90lc6o98DOjMeYHpg==", + "version": "7.12.0", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.12.0.tgz", + "integrity": "sha512-Iw9rQJBGpJRd3rwXm9ft/JiGoAZmLxxJZELYDQoPRZ4USVhkKtIcNBPw6U+/K2mBpaqM25JSV6Yl4Az9vO2wJg==", "bin": { "envinfo": "dist/cli.js" }, @@ -5689,9 +5674,9 @@ } }, "node_modules/eslint-plugin-regexp": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-regexp/-/eslint-plugin-regexp-2.4.0.tgz", - "integrity": "sha512-OL2S6VPjQhs9s/NclQ0qattVq1J0GU8ox70/HIVy5Dxw+qbbdd7KQkyucsez2clEQjvdtDe12DTnPphFFUyXFg==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-regexp/-/eslint-plugin-regexp-2.5.0.tgz", + "integrity": "sha512-I7vKcP0o75WS5SHiVNXN+Eshq49sbrweMQIuqSL3AId9AwDe9Dhbfug65vw64LxmOd4v+yf5l5Xt41y9puiq0g==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", @@ -5785,9 +5770,9 @@ "dev": true }, "node_modules/eslint-plugin-vue": { - "version": "9.24.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.24.0.tgz", - "integrity": "sha512-9SkJMvF8NGMT9aQCwFc5rj8Wo1XWSMSHk36i7ZwdI614BU7sIOR28ZjuFPKp8YGymZN12BSEbiSwa7qikp+PBw==", + "version": "9.24.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.24.1.tgz", + "integrity": "sha512-wk3SuwmS1pZdcuJlokGYEi/buDOwD6KltvhIZyOnpJ/378dcQ4zchu9PAMbbLAaydCz1iYc5AozszcOOgZIIOg==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", @@ -5803,7 +5788,7 @@ "node": "^14.17.0 || >=16.0.0" }, "peerDependencies": { - "eslint": "^6.2.0 || ^7.0.0 || ^8.0.0" + "eslint": "^6.2.0 || ^7.0.0 || ^8.0.0 || ^9.0.0" } }, "node_modules/eslint-plugin-vue-scoped-css": { @@ -5833,9 +5818,9 @@ } }, "node_modules/eslint-plugin-wc": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/eslint-plugin-wc/-/eslint-plugin-wc-2.0.4.tgz", - "integrity": "sha512-ORu7MBv0hXIvq894EJad70m+AvHGbmrDdKT6lcgtCVVhEbuIAyxg0ilfqqqHOmsh8PfcUBeEae3y7CElKvm1KQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-wc/-/eslint-plugin-wc-2.1.0.tgz", + "integrity": "sha512-s/BGOtmpgQ2yifR6EC1OM9t0DwYLgg4ZAL07Kw4eXvBb5TYaPafI+65tswvnZvhH8FqcjERLbBZPPvYsvinkfg==", "dev": true, "dependencies": { "is-valid-element-name": "^1.0.0", @@ -6594,9 +6579,9 @@ } }, "node_modules/happy-dom": { - "version": "14.5.0", - "resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-14.5.0.tgz", - "integrity": "sha512-KvOtCq7eamc7cjihM0F1wj6FptuXzooc3Typa7Vgu6ns2uKGXC4BIFlK80SdH2w8zcW0gtxpBVI/sUqbYtljDA==", + "version": "14.7.1", + "resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-14.7.1.tgz", + "integrity": "sha512-v60Q0evZ4clvMcrAh5/F8EdxDdfHdFrtffz/CNe10jKD+nFweZVxM91tW+UyY2L4AtpgIaXdZ7TQmiO1pfcwbg==", "dev": true, "dependencies": { "entities": "^4.5.0", @@ -9381,12 +9366,12 @@ "dev": true }, "node_modules/playwright": { - "version": "1.42.1", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.42.1.tgz", - "integrity": "sha512-PgwB03s2DZBcNRoW+1w9E+VkLBxweib6KTXM0M3tkiT4jVxKSi6PmVJ591J+0u10LUrgxB7dLRbiJqO5s2QPMg==", + "version": "1.43.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.43.1.tgz", + "integrity": "sha512-V7SoH0ai2kNt1Md9E3Gwas5B9m8KR2GVvwZnAI6Pg0m3sh7UvgiYhRrhsziCmqMJNouPckiOhk8T+9bSAK0VIA==", "dev": true, "dependencies": { - "playwright-core": "1.42.1" + "playwright-core": "1.43.1" }, "bin": { "playwright": "cli.js" @@ -9399,9 +9384,9 @@ } }, "node_modules/playwright-core": { - "version": "1.42.1", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.42.1.tgz", - "integrity": "sha512-mxz6zclokgrke9p1vtdy/COWBH+eOZgYUVVU34C73M+4j4HLlQJHtfcqiqqxpP0o8HhMkflvfbquLX5dg6wlfA==", + "version": "1.43.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.43.1.tgz", + "integrity": "sha512-EI36Mto2Vrx6VF7rm708qSnesVQKbxEWvPrfA1IPY6HgczBplDx7ENtx+K2n4kJ41sLLkuGfmb0ZLSSXlDhqPg==", "dev": true, "bin": { "playwright-core": "cli.js" @@ -11241,9 +11226,9 @@ } }, "node_modules/swagger-ui-dist": { - "version": "5.13.0", - "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.13.0.tgz", - "integrity": "sha512-uaWhh6j18IIs5tOX0arvIBnVINAzpTXaQXkr7qAk8zoupegJVg0UU/5+S/FgsgVCnzVsJ9d7QLjIxkswEeTg0Q==" + "version": "5.15.1", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.15.1.tgz", + "integrity": "sha512-Et/WY0NFdKj8sUBOyEx5P3VybsvGl7bo/y9JvgQ22TkH1a/KscQ0ZiQST2YeJ3cwCrIjYTbHbt165fkku0y1Ig==" }, "node_modules/sync-fetch": { "version": "0.4.5", @@ -11379,17 +11364,17 @@ } }, "node_modules/temporal-polyfill": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/temporal-polyfill/-/temporal-polyfill-0.2.3.tgz", - "integrity": "sha512-7ZJRc7wq/1XjrOQYkkNpgo2qfE9XLrUU8D/DS+LAC/T0bYqZ46rW6dow0sOTXTPZS4bwer8bD/0OyuKQBfA3yw==", + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/temporal-polyfill/-/temporal-polyfill-0.2.4.tgz", + "integrity": "sha512-WA5p0CjQTkMjF9m8sP4wSYgpqI8m2d4q7wPUyaJOWhy4bI9mReLb2yGvTV4qf/DPMTe6H6M/Dig5KmTMB7ev6Q==", "dependencies": { - "temporal-spec": "^0.2.0" + "temporal-spec": "^0.2.4" } }, "node_modules/temporal-spec": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/temporal-spec/-/temporal-spec-0.2.0.tgz", - "integrity": "sha512-r1AT0XdEp8TMQ13FLvOt8mOtAxDQsRt2QU5rSWCA7YfshddU651Y1NHVrceLANvixKdf9fYO8B/S9fXHodB7HQ==" + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/temporal-spec/-/temporal-spec-0.2.4.tgz", + "integrity": "sha512-lDMFv4nKQrSjlkHKAlHVqKrBG4DyFfa9F74cmBZ3Iy3ed8yvWnlWSIdi4IKfSqwmazAohBNwiN64qGx4y5Q3IQ==" }, "node_modules/terser": { "version": "5.30.3", @@ -11749,9 +11734,9 @@ } }, "node_modules/typescript": { - "version": "5.4.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.4.tgz", - "integrity": "sha512-dGE2Vv8cpVvw28v8HCPqyb08EzbBURxDpuhJvTrusShUfGnhHBafDsLdS1EhhxyL6BJQE+2cT3dDPAv+MQ6oLw==", + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", "devOptional": true, "peer": true, "bin": { @@ -11855,9 +11840,9 @@ } }, "node_modules/updates": { - "version": "16.0.0", - "resolved": "https://registry.npmjs.org/updates/-/updates-16.0.0.tgz", - "integrity": "sha512-Ra3QUu/rfbSCsG83zNNvoRQt0FVT3qULBSALYTlwTDX6oyz7R5GQAYwqJoIG/RDjYAXpwr3usrInoyHHTP6cag==", + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/updates/-/updates-16.0.1.tgz", + "integrity": "sha512-If3NQKzGcA3aVgz2VyOXqQ+4uqYjPUPqh2PeZPtD+OKT4CTmxRYqoyFO+T3nwfccy4SiWy5AabWrBXXhVQ89Aw==", "dev": true, "bin": { "updates": "dist/updates.js" @@ -12003,9 +11988,9 @@ } }, "node_modules/vite-node": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.4.0.tgz", - "integrity": "sha512-VZDAseqjrHgNd4Kh8icYHWzTKSCZMhia7GyHfhtzLW33fZlG9SwsB6CEhgyVOWkJfJ2pFLrp/Gj1FSfAiqH9Lw==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.5.0.tgz", + "integrity": "sha512-tV8h6gMj6vPzVCa7l+VGq9lwoJjW8Y79vst8QZZGiuRAfijU+EEWuc0kFpmndQrWhMMhet1jdSF+40KSZUqIIw==", "dev": true, "dependencies": { "cac": "^6.7.14", @@ -12051,9 +12036,9 @@ } }, "node_modules/vite/node_modules/rollup": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.14.0.tgz", - "integrity": "sha512-Qe7w62TyawbDzB4yt32R0+AbIo6m1/sqO7UPzFS8Z/ksL5mrfhA0v4CavfdmFav3D+ub4QeAgsGEe84DoWe/nQ==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.14.2.tgz", + "integrity": "sha512-WkeoTWvuBoFjFAhsEOHKRoZ3r9GfTyhh7Vff1zwebEFLEFjT1lG3784xEgKiTa7E+e70vsC81roVL2MP4tgEEQ==", "dev": true, "dependencies": { "@types/estree": "1.0.5" @@ -12066,35 +12051,35 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.14.0", - "@rollup/rollup-android-arm64": "4.14.0", - "@rollup/rollup-darwin-arm64": "4.14.0", - "@rollup/rollup-darwin-x64": "4.14.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.14.0", - "@rollup/rollup-linux-arm64-gnu": "4.14.0", - "@rollup/rollup-linux-arm64-musl": "4.14.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.14.0", - "@rollup/rollup-linux-riscv64-gnu": "4.14.0", - "@rollup/rollup-linux-s390x-gnu": "4.14.0", - "@rollup/rollup-linux-x64-gnu": "4.14.0", - "@rollup/rollup-linux-x64-musl": "4.14.0", - "@rollup/rollup-win32-arm64-msvc": "4.14.0", - "@rollup/rollup-win32-ia32-msvc": "4.14.0", - "@rollup/rollup-win32-x64-msvc": "4.14.0", + "@rollup/rollup-android-arm-eabi": "4.14.2", + "@rollup/rollup-android-arm64": "4.14.2", + "@rollup/rollup-darwin-arm64": "4.14.2", + "@rollup/rollup-darwin-x64": "4.14.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.14.2", + "@rollup/rollup-linux-arm64-gnu": "4.14.2", + "@rollup/rollup-linux-arm64-musl": "4.14.2", + "@rollup/rollup-linux-powerpc64le-gnu": "4.14.2", + "@rollup/rollup-linux-riscv64-gnu": "4.14.2", + "@rollup/rollup-linux-s390x-gnu": "4.14.2", + "@rollup/rollup-linux-x64-gnu": "4.14.2", + "@rollup/rollup-linux-x64-musl": "4.14.2", + "@rollup/rollup-win32-arm64-msvc": "4.14.2", + "@rollup/rollup-win32-ia32-msvc": "4.14.2", + "@rollup/rollup-win32-x64-msvc": "4.14.2", "fsevents": "~2.3.2" } }, "node_modules/vitest": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.4.0.tgz", - "integrity": "sha512-gujzn0g7fmwf83/WzrDTnncZt2UiXP41mHuFYFrdwaLRVQ6JYQEiME2IfEjU3vcFL3VKa75XhI3lFgn+hfVsQw==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.5.0.tgz", + "integrity": "sha512-d8UKgR0m2kjdxDWX6911uwxout6GHS0XaGH1cksSIVVG8kRlE7G7aBw7myKQCvDI5dT4j7ZMa+l706BIORMDLw==", "dev": true, "dependencies": { - "@vitest/expect": "1.4.0", - "@vitest/runner": "1.4.0", - "@vitest/snapshot": "1.4.0", - "@vitest/spy": "1.4.0", - "@vitest/utils": "1.4.0", + "@vitest/expect": "1.5.0", + "@vitest/runner": "1.5.0", + "@vitest/snapshot": "1.5.0", + "@vitest/spy": "1.5.0", + "@vitest/utils": "1.5.0", "acorn-walk": "^8.3.2", "chai": "^4.3.10", "debug": "^4.3.4", @@ -12106,9 +12091,9 @@ "std-env": "^3.5.0", "strip-literal": "^2.0.0", "tinybench": "^2.5.1", - "tinypool": "^0.8.2", + "tinypool": "^0.8.3", "vite": "^5.0.0", - "vite-node": "1.4.0", + "vite-node": "1.5.0", "why-is-node-running": "^2.2.2" }, "bin": { @@ -12123,8 +12108,8 @@ "peerDependencies": { "@edge-runtime/vm": "*", "@types/node": "^18.0.0 || >=20.0.0", - "@vitest/browser": "1.4.0", - "@vitest/ui": "1.4.0", + "@vitest/browser": "1.5.0", + "@vitest/ui": "1.5.0", "happy-dom": "*", "jsdom": "*" }, @@ -12191,9 +12176,9 @@ } }, "node_modules/vue-chartjs": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/vue-chartjs/-/vue-chartjs-5.3.0.tgz", - "integrity": "sha512-8XqX0JU8vFZ+WA2/knz4z3ThClduni2Nm0BMe2u0mXgTfd9pXrmJ07QBI+WAij5P/aPmPMX54HCE1seWL37ZdQ==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/vue-chartjs/-/vue-chartjs-5.3.1.tgz", + "integrity": "sha512-rZjqcHBxKiHrBl0CIvcOlVEBwRhpWAVf6rDU3vUfa7HuSRmGtCslc0Oc8m16oAVuk0erzc1FCtH1VCriHsrz+A==", "peerDependencies": { "chart.js": "^4.1.1", "vue": "^3.0.0-0 || ^2.7.0" diff --git a/package.json b/package.json index f58c3b4d8f22..ff1ae4d49e6a 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "chartjs-adapter-dayjs-4": "1.0.4", "chartjs-plugin-zoom": "2.0.1", "clippie": "4.0.7", - "css-loader": "7.0.0", + "css-loader": "7.1.1", "dayjs": "1.11.10", "dropzone": "6.0.0-beta.2", "easymde": "2.18.0", @@ -43,9 +43,9 @@ "postcss-nesting": "12.1.1", "pretty-ms": "9.0.0", "sortablejs": "1.15.2", - "swagger-ui-dist": "5.13.0", + "swagger-ui-dist": "5.15.1", "tailwindcss": "3.4.3", - "temporal-polyfill": "0.2.3", + "temporal-polyfill": "0.2.4", "throttle-debounce": "5.0.0", "tinycolor2": "1.6.0", "tippy.js": "6.3.7", @@ -55,7 +55,7 @@ "vanilla-colorful": "0.7.2", "vue": "3.4.21", "vue-bar-graph": "2.0.0", - "vue-chartjs": "5.3.0", + "vue-chartjs": "5.3.1", "vue-loader": "17.4.2", "vue3-calendar-heatmap": "2.0.5", "webpack": "5.91.0", @@ -63,8 +63,8 @@ "wrap-ansi": "9.0.0" }, "devDependencies": { - "@eslint-community/eslint-plugin-eslint-comments": "4.1.0", - "@playwright/test": "1.42.1", + "@eslint-community/eslint-plugin-eslint-comments": "4.3.0", + "@playwright/test": "1.43.1", "@stoplight/spectral-cli": "6.11.1", "@stylistic/eslint-plugin-js": "1.7.0", "@stylistic/stylelint-plugin": "2.1.1", @@ -76,15 +76,15 @@ "eslint-plugin-jquery": "1.5.1", "eslint-plugin-no-jquery": "2.7.0", "eslint-plugin-no-use-extend-native": "0.5.0", - "eslint-plugin-regexp": "2.4.0", + "eslint-plugin-regexp": "2.5.0", "eslint-plugin-sonarjs": "0.25.1", "eslint-plugin-unicorn": "52.0.0", "eslint-plugin-vitest": "0.4.1", "eslint-plugin-vitest-globals": "1.5.0", - "eslint-plugin-vue": "9.24.0", + "eslint-plugin-vue": "9.24.1", "eslint-plugin-vue-scoped-css": "2.8.0", - "eslint-plugin-wc": "2.0.4", - "happy-dom": "14.5.0", + "eslint-plugin-wc": "2.1.0", + "happy-dom": "14.7.1", "markdownlint-cli": "0.39.0", "postcss-html": "1.6.0", "stylelint": "16.3.1", @@ -92,9 +92,9 @@ "stylelint-declaration-strict-value": "1.10.4", "stylelint-value-no-unknown-custom-properties": "6.0.1", "svgo": "3.2.0", - "updates": "16.0.0", + "updates": "16.0.1", "vite-string-plugin": "1.1.5", - "vitest": "1.4.0" + "vitest": "1.5.0" }, "browserslist": [ "defaults" diff --git a/poetry.lock b/poetry.lock index 951a0fa7a885..1533ddc5ec19 100644 --- a/poetry.lock +++ b/poetry.lock @@ -113,13 +113,13 @@ six = ">=1.13.0" [[package]] name = "json5" -version = "0.9.24" +version = "0.9.25" description = "A Python implementation of the JSON5 data format." optional = false python-versions = ">=3.8" files = [ - {file = "json5-0.9.24-py3-none-any.whl", hash = "sha256:4ca101fd5c7cb47960c055ef8f4d0e31e15a7c6c48c3b6f1473fc83b6c462a13"}, - {file = "json5-0.9.24.tar.gz", hash = "sha256:0c638399421da959a20952782800e5c1a78c14e08e1dc9738fa10d8ec14d58c8"}, + {file = "json5-0.9.25-py3-none-any.whl", hash = "sha256:34ed7d834b1341a86987ed52f3f76cd8ee184394906b6e22a1e0deb9ab294e8f"}, + {file = "json5-0.9.25.tar.gz", hash = "sha256:548e41b9be043f9426776f05df8635a00fe06104ea51ed24b67f908856e151ae"}, ] [[package]] diff --git a/updates.config.js b/updates.config.js index 11908dea8e5b..bd072fe6cbda 100644 --- a/updates.config.js +++ b/updates.config.js @@ -1,6 +1,8 @@ export default { exclude: [ '@mcaptcha/vanilla-glue', // breaking changes in rc versions need to be handled + 'eslint', // need to migrate to eslint flat config first 'eslint-plugin-array-func', // need to migrate to eslint flat config first + 'eslint-plugin-vitest', // need to migrate to eslint flat config first ], }; From 92e27e15c38b95be2309dae316b896ee1d80324b Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Sat, 13 Apr 2024 17:31:40 +0800 Subject: [PATCH 22/35] Add comment for ContainsRedirectURI about the exact match (#30457) Close #26897 Replace #30336 --- models/auth/oauth2.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/models/auth/oauth2.go b/models/auth/oauth2.go index 9d53fffc7869..bc1bcaef6321 100644 --- a/models/auth/oauth2.go +++ b/models/auth/oauth2.go @@ -137,6 +137,11 @@ func (app *OAuth2Application) TableName() string { // ContainsRedirectURI checks if redirectURI is allowed for app func (app *OAuth2Application) ContainsRedirectURI(redirectURI string) bool { + // OAuth2 requires the redirect URI to be an exact match, no dynamic parts are allowed. + // https://stackoverflow.com/questions/55524480/should-dynamic-query-parameters-be-present-in-the-redirection-uri-for-an-oauth2 + // https://www.rfc-editor.org/rfc/rfc6819#section-5.2.3.3 + // https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest + // https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics-12#section-3.1 contains := func(s string) bool { s = strings.TrimSuffix(strings.ToLower(s), "/") for _, u := range app.RedirectURIs { From 18dd9f9a3f3ef68e3cb2c0b032f751b64ea0e6e8 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Sat, 13 Apr 2024 18:05:33 +0800 Subject: [PATCH 23/35] Fix label rendering (#30456) 1. Check whether the label is for an issue or a pull request. 2. Don't use space to layout 3. Make sure the test strings have trailing spaces explicitly, to avoid some IDE removing the trailing spaces automatically. --- modules/templates/util_render.go | 7 ++-- modules/templates/util_render_test.go | 40 ++++++++++++++----- .../repo/issue/view_content/comments.tmpl | 6 +-- 3 files changed, 38 insertions(+), 15 deletions(-) diff --git a/modules/templates/util_render.go b/modules/templates/util_render.go index 0b53965f2580..659422aee798 100644 --- a/modules/templates/util_render.go +++ b/modules/templates/util_render.go @@ -216,15 +216,16 @@ func RenderMarkdownToHtml(ctx context.Context, input string) template.HTML { //n return output } -func RenderLabels(ctx context.Context, locale translation.Locale, labels []*issues_model.Label, repoLink string) template.HTML { +func RenderLabels(ctx context.Context, locale translation.Locale, labels []*issues_model.Label, repoLink string, issue *issues_model.Issue) template.HTML { + isPullRequest := issue != nil && issue.IsPull + baseLink := fmt.Sprintf("%s/%s", repoLink, util.Iif(isPullRequest, "pulls", "issues")) htmlCode := `` for _, label := range labels { // Protect against nil value in labels - shouldn't happen but would cause a panic if so if label == nil { continue } - htmlCode += fmt.Sprintf("%s ", - repoLink, label.ID, RenderLabel(ctx, locale, label)) + htmlCode += fmt.Sprintf(`%s`, baseLink, label.ID, RenderLabel(ctx, locale, label)) } htmlCode += "" return template.HTML(htmlCode) diff --git a/modules/templates/util_render_test.go b/modules/templates/util_render_test.go index 15aee8912d1e..47c5da6485c3 100644 --- a/modules/templates/util_render_test.go +++ b/modules/templates/util_render_test.go @@ -7,17 +7,21 @@ import ( "context" "html/template" "os" + "strings" "testing" + "code.gitea.io/gitea/models/issues" "code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/markup" + "code.gitea.io/gitea/modules/translation" "github.com/stretchr/testify/assert" ) -const testInput = ` space @mention-user +func testInput() string { + s := ` space @mention-user /just/a/path.bin https://example.com/file.bin [local link](file.bin) @@ -36,8 +40,10 @@ com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit mail@domain.com @mention-user test #123 - space + space ` + return strings.ReplaceAll(s, "", " ") +} var testMetas = map[string]string{ "user": "user13", @@ -121,23 +127,23 @@ com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit #123 space` - assert.EqualValues(t, expected, RenderCommitBody(context.Background(), testInput, testMetas)) + assert.EqualValues(t, expected, RenderCommitBody(context.Background(), testInput(), testMetas)) } func TestRenderCommitMessage(t *testing.T) { expected := `space @mention-user ` - assert.EqualValues(t, expected, RenderCommitMessage(context.Background(), testInput, testMetas)) + assert.EqualValues(t, expected, RenderCommitMessage(context.Background(), testInput(), testMetas)) } func TestRenderCommitMessageLinkSubject(t *testing.T) { expected := `space @mention-user` - assert.EqualValues(t, expected, RenderCommitMessageLinkSubject(context.Background(), testInput, "https://example.com/link", testMetas)) + assert.EqualValues(t, expected, RenderCommitMessageLinkSubject(context.Background(), testInput(), "https://example.com/link", testMetas)) } func TestRenderIssueTitle(t *testing.T) { - expected := ` space @mention-user + expected := ` space @mention-user /just/a/path.bin https://example.com/file.bin [local link](file.bin) @@ -156,9 +162,10 @@ com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit mail@domain.com @mention-user test #123 - space + space ` - assert.EqualValues(t, expected, RenderIssueTitle(context.Background(), testInput, testMetas)) + expected = strings.ReplaceAll(expected, "", " ") + assert.EqualValues(t, expected, RenderIssueTitle(context.Background(), testInput(), testMetas)) } func TestRenderMarkdownToHtml(t *testing.T) { @@ -183,5 +190,20 @@ com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit #123 space

` - assert.EqualValues(t, expected, RenderMarkdownToHtml(context.Background(), testInput)) + assert.EqualValues(t, expected, RenderMarkdownToHtml(context.Background(), testInput())) +} + +func TestRenderLabels(t *testing.T) { + ctx := context.Background() + locale := &translation.MockLocale{} + + label := &issues.Label{ID: 123, Name: "label-name", Color: "label-color"} + issue := &issues.Issue{} + expected := `/owner/repo/issues?labels=123` + assert.Contains(t, RenderLabels(ctx, locale, []*issues.Label{label}, "/owner/repo", issue), expected) + + label = &issues.Label{ID: 123, Name: "label-name", Color: "label-color"} + issue = &issues.Issue{IsPull: true} + expected = `/owner/repo/pulls?labels=123` + assert.Contains(t, RenderLabels(ctx, locale, []*issues.Label{label}, "/owner/repo", issue), expected) } diff --git a/templates/repo/issue/view_content/comments.tmpl b/templates/repo/issue/view_content/comments.tmpl index f65dc6ee90c8..d900d23c4711 100644 --- a/templates/repo/issue/view_content/comments.tmpl +++ b/templates/repo/issue/view_content/comments.tmpl @@ -173,11 +173,11 @@ {{template "shared/user/authorlink" .Poster}} {{if and .AddedLabels (not .RemovedLabels)}} - {{ctx.Locale.TrN (len .AddedLabels) "repo.issues.add_label" "repo.issues.add_labels" (RenderLabels $.Context ctx.Locale .AddedLabels $.RepoLink) $createdStr}} + {{ctx.Locale.TrN (len .AddedLabels) "repo.issues.add_label" "repo.issues.add_labels" (RenderLabels ctx ctx.Locale .AddedLabels $.RepoLink .Issue) $createdStr}} {{else if and (not .AddedLabels) .RemovedLabels}} - {{ctx.Locale.TrN (len .RemovedLabels) "repo.issues.remove_label" "repo.issues.remove_labels" (RenderLabels $.Context ctx.Locale .RemovedLabels $.RepoLink) $createdStr}} + {{ctx.Locale.TrN (len .RemovedLabels) "repo.issues.remove_label" "repo.issues.remove_labels" (RenderLabels ctx ctx.Locale .RemovedLabels $.RepoLink .Issue) $createdStr}} {{else}} - {{ctx.Locale.Tr "repo.issues.add_remove_labels" (RenderLabels $.Context ctx.Locale .AddedLabels $.RepoLink) (RenderLabels $.Context ctx.Locale .RemovedLabels $.RepoLink) $createdStr}} + {{ctx.Locale.Tr "repo.issues.add_remove_labels" (RenderLabels ctx ctx.Locale .AddedLabels $.RepoLink .Issue) (RenderLabels ctx ctx.Locale .RemovedLabels $.RepoLink .Issue) $createdStr}} {{end}}
From fd59cd9450cbd511ad4a0790bf51f8d5d2c18aa3 Mon Sep 17 00:00:00 2001 From: Jason Song Date: Sat, 13 Apr 2024 23:41:57 +0800 Subject: [PATCH 24/35] Avoid losing token when updating mirror settings (#30429) Fix #30416. Before (it shows as "Unset" while there's a token): image After: image The username shows as "oauth2" because of https://github.com/go-gitea/gitea/blob/f9fdac9809335729b2ac3227b2a5f71a62fc64ad/services/migrations/dump.go#L99 I have checked that all usage of `MirrorRemoteAddress` has been updated. image However, it needs to be checked again when backporting. --------- Co-authored-by: Giteabot --- modules/templates/util_misc.go | 38 +++++++++++++++------------- services/mirror/mirror_pull.go | 12 +++++++-- templates/repo/settings/options.tmpl | 2 +- 3 files changed, 32 insertions(+), 20 deletions(-) diff --git a/modules/templates/util_misc.go b/modules/templates/util_misc.go index 6c1b4ab240e3..774385483b4c 100644 --- a/modules/templates/util_misc.go +++ b/modules/templates/util_misc.go @@ -142,35 +142,39 @@ type remoteAddress struct { Password string } -func mirrorRemoteAddress(ctx context.Context, m *repo_model.Repository, remoteName string, ignoreOriginalURL bool) remoteAddress { - a := remoteAddress{} - - remoteURL := m.OriginalURL - if ignoreOriginalURL || remoteURL == "" { - var err error - remoteURL, err = git.GetRemoteAddress(ctx, m.RepoPath(), remoteName) - if err != nil { - log.Error("GetRemoteURL %v", err) - return a - } +func mirrorRemoteAddress(ctx context.Context, m *repo_model.Repository, remoteName string) remoteAddress { + ret := remoteAddress{} + remoteURL, err := git.GetRemoteAddress(ctx, m.RepoPath(), remoteName) + if err != nil { + log.Error("GetRemoteURL %v", err) + return ret } u, err := giturl.Parse(remoteURL) if err != nil { log.Error("giturl.Parse %v", err) - return a + return ret } if u.Scheme != "ssh" && u.Scheme != "file" { if u.User != nil { - a.Username = u.User.Username() - a.Password, _ = u.User.Password() + ret.Username = u.User.Username() + ret.Password, _ = u.User.Password() } - u.User = nil } - a.Address = u.String() - return a + // The URL stored in the git repo could contain authentication, + // erase it, or it will be shown in the UI. + u.User = nil + ret.Address = u.String() + // Why not use m.OriginalURL to set ret.Address? + // It should be OK to use it, since m.OriginalURL should be the same as the authentication-erased URL from the Git repository. + // However, the old code has already stored authentication in m.OriginalURL when updating mirror settings. + // That means we need to use "giturl.Parse" for m.OriginalURL again to ensure authentication is erased. + // Instead of doing this, why not directly use the authentication-erased URL from the Git repository? + // It should be the same as long as there are no bugs. + + return ret } func FilenameIsImage(filename string) bool { diff --git a/services/mirror/mirror_pull.go b/services/mirror/mirror_pull.go index 21d5f08205de..fa23986c54ea 100644 --- a/services/mirror/mirror_pull.go +++ b/services/mirror/mirror_pull.go @@ -13,6 +13,7 @@ import ( system_model "code.gitea.io/gitea/models/system" "code.gitea.io/gitea/modules/cache" "code.gitea.io/gitea/modules/git" + giturl "code.gitea.io/gitea/modules/git/url" "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/lfs" "code.gitea.io/gitea/modules/log" @@ -30,10 +31,15 @@ const gitShortEmptySha = "0000000" // UpdateAddress writes new address to Git repository and database func UpdateAddress(ctx context.Context, m *repo_model.Mirror, addr string) error { + u, err := giturl.Parse(addr) + if err != nil { + return fmt.Errorf("invalid addr: %v", err) + } + remoteName := m.GetRemoteName() repoPath := m.GetRepository(ctx).RepoPath() // Remove old remote - _, _, err := git.NewCommand(ctx, "remote", "rm").AddDynamicArguments(remoteName).RunStdString(&git.RunOpts{Dir: repoPath}) + _, _, err = git.NewCommand(ctx, "remote", "rm").AddDynamicArguments(remoteName).RunStdString(&git.RunOpts{Dir: repoPath}) if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") { return err } @@ -70,7 +76,9 @@ func UpdateAddress(ctx context.Context, m *repo_model.Mirror, addr string) error } } - m.Repo.OriginalURL = addr + // erase authentication before storing in database + u.User = nil + m.Repo.OriginalURL = u.String() return repo_model.UpdateRepositoryCols(ctx, m.Repo, "original_url") } diff --git a/templates/repo/settings/options.tmpl b/templates/repo/settings/options.tmpl index b8fa4759b132..df6ccbf6bc2f 100644 --- a/templates/repo/settings/options.tmpl +++ b/templates/repo/settings/options.tmpl @@ -156,7 +156,7 @@
- {{$address := MirrorRemoteAddress $.Context .Repository .PullMirror.GetRemoteName false}} + {{$address := MirrorRemoteAddress $.Context .Repository .PullMirror.GetRemoteName}}
From af02b8a0e9b00a324fb92f1f73ea386dd9595c3d Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Sun, 14 Apr 2024 01:17:01 +0900 Subject: [PATCH 25/35] Fix network error when open/close organization/individual projects and redirect to project page (#30387) Follow #27734 ![image](https://github.com/go-gitea/gitea/assets/18380374/02ed6b9a-cbb6-4f49-a54a-ca76a0d052a9) Updated: Redirect to project page instead of project list page. --- routers/web/org/projects.go | 8 ++++---- routers/web/repo/projects.go | 9 ++------- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/routers/web/org/projects.go b/routers/web/org/projects.go index 596a370d2e55..d439b11cf926 100644 --- a/routers/web/org/projects.go +++ b/routers/web/org/projects.go @@ -7,7 +7,6 @@ import ( "errors" "fmt" "net/http" - "net/url" "strconv" "strings" @@ -195,14 +194,15 @@ func NewProjectPost(ctx *context.Context) { // ChangeProjectStatus updates the status of a project between "open" and "close" func ChangeProjectStatus(ctx *context.Context) { - toClose := false + var toClose bool switch ctx.Params(":action") { case "open": toClose = false case "close": toClose = true default: - ctx.Redirect(ctx.ContextUser.HomeLink() + "/-/projects") + ctx.JSONRedirect(ctx.ContextUser.HomeLink() + "/-/projects") + return } id := ctx.ParamsInt64(":id") @@ -210,7 +210,7 @@ func ChangeProjectStatus(ctx *context.Context) { ctx.NotFoundOrServerError("ChangeProjectStatusByRepoIDAndID", project_model.IsErrProjectNotExist, err) return } - ctx.Redirect(ctx.ContextUser.HomeLink() + "/-/projects?state=" + url.QueryEscape(ctx.Params(":action"))) + ctx.JSONRedirect(fmt.Sprintf("%s/-/projects/%d", ctx.ContextUser.HomeLink(), id)) } // DeleteProject delete a project diff --git a/routers/web/repo/projects.go b/routers/web/repo/projects.go index a2db1fc770a5..9b765e89e877 100644 --- a/routers/web/repo/projects.go +++ b/routers/web/repo/projects.go @@ -7,7 +7,6 @@ import ( "errors" "fmt" "net/http" - "net/url" "strings" "code.gitea.io/gitea/models/db" @@ -181,14 +180,10 @@ func ChangeProjectStatus(ctx *context.Context) { id := ctx.ParamsInt64(":id") if err := project_model.ChangeProjectStatusByRepoIDAndID(ctx, ctx.Repo.Repository.ID, id, toClose); err != nil { - if project_model.IsErrProjectNotExist(err) { - ctx.NotFound("", err) - } else { - ctx.ServerError("ChangeProjectStatusByIDAndRepoID", err) - } + ctx.NotFoundOrServerError("ChangeProjectStatusByRepoIDAndID", project_model.IsErrProjectNotExist, err) return } - ctx.JSONRedirect(ctx.Repo.RepoLink + "/projects?state=" + url.QueryEscape(ctx.Params(":action"))) + ctx.JSONRedirect(fmt.Sprintf("%s/projects/%d", ctx.Repo.RepoLink, id)) } // DeleteProject delete a project From c77e8140bc2ac6521dbebfb77613dce2648bfcb8 Mon Sep 17 00:00:00 2001 From: silverwind Date: Sat, 13 Apr 2024 19:32:15 +0200 Subject: [PATCH 26/35] Add `interface{}` to `any` replacement to `make fmt`, exclude `*.pb.go` (#30461) Since https://github.com/go-gitea/gitea/pull/25686, a few `interface{}` have sneaked into the codebase. Add this replacement to `make fmt` to prevent this from happening again. Ideally a linter would do this, but I haven't found any suitable. --- .gitattributes | 1 + Makefile | 2 +- build/code-batch-process.go | 15 +++------------ modules/optional/serialization.go | 2 +- services/actions/auth_test.go | 2 +- 5 files changed, 7 insertions(+), 15 deletions(-) diff --git a/.gitattributes b/.gitattributes index 467b8a47b5d6..9fb4a4e83dd7 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,5 +1,6 @@ * text=auto eol=lf *.tmpl linguist-language=Handlebars +*.pb.go linguist-generated /assets/*.json linguist-generated /public/assets/img/svg/*.svg linguist-generated /templates/swagger/v1_json.tmpl linguist-generated diff --git a/Makefile b/Makefile index f1acfbc81e3c..594f13ead8ac 100644 --- a/Makefile +++ b/Makefile @@ -295,7 +295,7 @@ clean: .PHONY: fmt fmt: - GOFUMPT_PACKAGE=$(GOFUMPT_PACKAGE) $(GO) run build/code-batch-process.go gitea-fmt -w '{file-list}' + @GOFUMPT_PACKAGE=$(GOFUMPT_PACKAGE) $(GO) run build/code-batch-process.go gitea-fmt -w '{file-list}' $(eval TEMPLATES := $(shell find templates -type f -name '*.tmpl')) @# strip whitespace after '{{' or '(' and before '}}' or ')' unless there is only @# whitespace before it diff --git a/build/code-batch-process.go b/build/code-batch-process.go index b3ee3994207d..cc2ab680268c 100644 --- a/build/code-batch-process.go +++ b/build/code-batch-process.go @@ -69,6 +69,7 @@ func newFileCollector(fileFilter string, batchSize int) (*fileCollector, error) co.includePatterns = append(co.includePatterns, regexp.MustCompile(`.*\.go$`)) co.excludePatterns = append(co.excludePatterns, regexp.MustCompile(`.*\bbindata\.go$`)) + co.excludePatterns = append(co.excludePatterns, regexp.MustCompile(`\.pb\.go$`)) co.excludePatterns = append(co.excludePatterns, regexp.MustCompile(`tests/gitea-repositories-meta`)) co.excludePatterns = append(co.excludePatterns, regexp.MustCompile(`tests/integration/migration-test`)) co.excludePatterns = append(co.excludePatterns, regexp.MustCompile(`modules/git/tests`)) @@ -203,17 +204,6 @@ Example: `, "file-batch-exec") } -func getGoVersion() string { - goModFile, err := os.ReadFile("go.mod") - if err != nil { - log.Fatalf(`Faild to read "go.mod": %v`, err) - os.Exit(1) - } - goModVersionRegex := regexp.MustCompile(`go \d+\.\d+`) - goModVersionLine := goModVersionRegex.Find(goModFile) - return string(goModVersionLine[3:]) -} - func newFileCollectorFromMainOptions(mainOptions map[string]string) (fc *fileCollector, err error) { fileFilter := mainOptions["file-filter"] if fileFilter == "" { @@ -278,7 +268,8 @@ func main() { log.Print("the -d option is not supported by gitea-fmt") } cmdErrors = append(cmdErrors, giteaFormatGoImports(files, containsString(subArgs, "-w"))) - cmdErrors = append(cmdErrors, passThroughCmd("go", append([]string{"run", os.Getenv("GOFUMPT_PACKAGE"), "-extra", "-lang", getGoVersion()}, substArgs...))) + cmdErrors = append(cmdErrors, passThroughCmd("gofmt", append([]string{"-w", "-r", "interface{} -> any"}, substArgs...))) + cmdErrors = append(cmdErrors, passThroughCmd("go", append([]string{"run", os.Getenv("GOFUMPT_PACKAGE"), "-extra"}, substArgs...))) default: log.Fatalf("unknown cmd: %s %v", subCmd, subArgs) } diff --git a/modules/optional/serialization.go b/modules/optional/serialization.go index 6688e78cd181..b120a0edf6b9 100644 --- a/modules/optional/serialization.go +++ b/modules/optional/serialization.go @@ -35,7 +35,7 @@ func (o *Option[T]) UnmarshalYAML(value *yaml.Node) error { return nil } -func (o Option[T]) MarshalYAML() (interface{}, error) { +func (o Option[T]) MarshalYAML() (any, error) { if !o.Has() { return nil, nil } diff --git a/services/actions/auth_test.go b/services/actions/auth_test.go index f73ae8ae4c36..12db2bae565d 100644 --- a/services/actions/auth_test.go +++ b/services/actions/auth_test.go @@ -20,7 +20,7 @@ func TestCreateAuthorizationToken(t *testing.T) { assert.Nil(t, err) assert.NotEqual(t, "", token) claims := jwt.MapClaims{} - _, err = jwt.ParseWithClaims(token, claims, func(t *jwt.Token) (interface{}, error) { + _, err = jwt.ParseWithClaims(token, claims, func(t *jwt.Token) (any, error) { return setting.GetGeneralTokenSigningSecret(), nil }) assert.Nil(t, err) From b18c04ebde94e23d97da4958173faea843d5344f Mon Sep 17 00:00:00 2001 From: Jonathan Tran Date: Sun, 14 Apr 2024 00:46:56 -0400 Subject: [PATCH 27/35] fix: Fix to delete cookie when AppSubURL is non-empty (#30375) Cookies may exist on "/subpath" and "/subpath/" for some legacy reasons (eg: changed CookiePath behavior in code). The legacy cookie should be removed correctly. --------- Co-authored-by: wxiaoguang Co-authored-by: Kyle D --- modules/session/store.go | 7 ++++++ modules/web/middleware/cookie.go | 32 +++++++++++++++++++++++----- services/auth/source/oauth2/store.go | 3 ++- 3 files changed, 36 insertions(+), 6 deletions(-) diff --git a/modules/session/store.go b/modules/session/store.go index 4fa4d2848f1b..2f7ab7760b59 100644 --- a/modules/session/store.go +++ b/modules/session/store.go @@ -6,6 +6,9 @@ package session import ( "net/http" + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/web/middleware" + "gitea.com/go-chi/session" ) @@ -18,6 +21,10 @@ type Store interface { // RegenerateSession regenerates the underlying session and returns the new store func RegenerateSession(resp http.ResponseWriter, req *http.Request) (Store, error) { + // Ensure that a cookie with a trailing slash does not take precedence over + // the cookie written by the middleware. + middleware.DeleteLegacySiteCookie(resp, setting.SessionConfig.CookieName) + s, err := session.RegenerateSession(resp, req) return s, err } diff --git a/modules/web/middleware/cookie.go b/modules/web/middleware/cookie.go index 621640895b95..0bed7267930c 100644 --- a/modules/web/middleware/cookie.go +++ b/modules/web/middleware/cookie.go @@ -45,10 +45,32 @@ func SetSiteCookie(resp http.ResponseWriter, name, value string, maxAge int) { SameSite: setting.SessionConfig.SameSite, } resp.Header().Add("Set-Cookie", cookie.String()) - if maxAge < 0 { - // There was a bug in "setting.SessionConfig.CookiePath" code, the old default value of it was empty "". - // So we have to delete the cookie on path="" again, because some old code leaves cookies on path="". - cookie.Path = strings.TrimSuffix(setting.SessionConfig.CookiePath, "/") - resp.Header().Add("Set-Cookie", cookie.String()) + // Previous versions would use a cookie path with a trailing /. + // These are more specific than cookies without a trailing /, so + // we need to delete these if they exist. + DeleteLegacySiteCookie(resp, name) +} + +// DeleteLegacySiteCookie deletes the cookie with the given name at the cookie +// path with a trailing /, which would unintentionally override the cookie. +func DeleteLegacySiteCookie(resp http.ResponseWriter, name string) { + if setting.SessionConfig.CookiePath == "" || strings.HasSuffix(setting.SessionConfig.CookiePath, "/") { + // If the cookie path ends with /, no legacy cookies will take + // precedence, so do nothing. The exception is that cookies with no + // path could override other cookies, but it's complicated and we don't + // currently handle that. + return } + + cookie := &http.Cookie{ + Name: name, + Value: "", + MaxAge: -1, + Path: setting.SessionConfig.CookiePath + "/", + Domain: setting.SessionConfig.Domain, + Secure: setting.SessionConfig.Secure, + HttpOnly: true, + SameSite: setting.SessionConfig.SameSite, + } + resp.Header().Add("Set-Cookie", cookie.String()) } diff --git a/services/auth/source/oauth2/store.go b/services/auth/source/oauth2/store.go index 394bf9946359..90fa965602a7 100644 --- a/services/auth/source/oauth2/store.go +++ b/services/auth/source/oauth2/store.go @@ -9,6 +9,7 @@ import ( "net/http" "code.gitea.io/gitea/modules/log" + session_module "code.gitea.io/gitea/modules/session" chiSession "gitea.com/go-chi/session" "github.com/gorilla/sessions" @@ -65,7 +66,7 @@ func (st *SessionsStore) Save(r *http.Request, w http.ResponseWriter, session *s chiStore := chiSession.GetSession(r) if session.IsNew { - _, _ = chiSession.RegenerateSession(w, r) + _, _ = session_module.RegenerateSession(w, r) session.IsNew = false } From ce130ae8daa37c977443045390209e9095dc42b1 Mon Sep 17 00:00:00 2001 From: silverwind Date: Sun, 14 Apr 2024 09:16:03 +0200 Subject: [PATCH 28/35] Fix JS error when opening to expanded code comment (#30463) Fix regression from https://github.com/go-gitea/gitea/commit/e0b018706fa7703ef1759d9a75a1399383715808 where opening to a code comment via hash link would give this error: image --------- Co-authored-by: wxiaoguang --- web_src/js/features/repo-issue.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/web_src/js/features/repo-issue.js b/web_src/js/features/repo-issue.js index 0d326aae581f..2b2eed58bbfb 100644 --- a/web_src/js/features/repo-issue.js +++ b/web_src/js/features/repo-issue.js @@ -449,12 +449,10 @@ export function initRepoPullRequestReview() { offset += $('.diff-detail-box').outerHeight() + $(diffHeader).outerHeight(); } - document.getElementById(`show-outdated-${id}`).classList.add('tw-hidden'); - document.getElementById(`code-comments-${id}`).classList.remove('tw-hidden'); - document.getElementById(`code-preview-${id}`).classList.remove('tw-hidden'); - document.getElementById(`hide-outdated-${id}`).classList.remove('tw-hidden'); + hideElem(`#show-outdated-${id}`); + showElem(`#code-comments-${id}, #code-preview-${id}, #hide-outdated-${id}`); // if the comment box is folded, expand it - if (ancestorDiffBox.getAttribute('data-folded') === 'true') { + if (ancestorDiffBox?.getAttribute('data-folded') === 'true') { setFileFolding(ancestorDiffBox, ancestorDiffBox.querySelector('.fold-file'), false); } From 6999a88fd9bef6baa0a8cc5f63e419079611fc9b Mon Sep 17 00:00:00 2001 From: silverwind Date: Sun, 14 Apr 2024 11:21:16 +0200 Subject: [PATCH 29/35] Pulse page improvements (#30149) 1. add border-radius and spacing to bars 2. use tailwind background classes 3. Add more space around activity list headers Screenshot 2024-03-27 at 23 40 54 Screenshot 2024-03-27 at 23 41 02 --- templates/repo/pulse.tmpl | 26 +++++++++++++++----------- web_src/css/dashboard.css | 1 - web_src/css/modules/divider.css | 6 +++++- web_src/css/modules/header.css | 1 - web_src/css/modules/label.css | 1 - web_src/css/repo.css | 14 +++++++++++++- 6 files changed, 33 insertions(+), 16 deletions(-) diff --git a/templates/repo/pulse.tmpl b/templates/repo/pulse.tmpl index cfb3ec1d3d1e..bc25563d488b 100644 --- a/templates/repo/pulse.tmpl +++ b/templates/repo/pulse.tmpl @@ -25,12 +25,14 @@
{{if gt .Activity.ActivePRCount 0}}
- - + {{if gt .Activity.MergedPRPerc 0}} + + {{end}} +
{{else}}
- +
{{end}} {{ctx.Locale.TrN .Activity.ActivePRCount "repo.activity.active_prs_count_1" "repo.activity.active_prs_count_n" .Activity.ActivePRCount}} @@ -40,8 +42,10 @@
{{if gt .Activity.ActiveIssueCount 0}}
- - + {{if gt .Activity.ClosedIssuePerc 0}} + + {{end}} +
{{else}}
@@ -108,7 +112,7 @@ {{end}} {{if gt .Activity.PublishedReleaseCount 0}} -

+

{{svg "octicon-tag" 16 "tw-mr-2"}} {{ctx.Locale.Tr "repo.activity.title.releases_published_by" (ctx.Locale.TrN .Activity.PublishedReleaseCount "repo.activity.title.releases_1" "repo.activity.title.releases_n" .Activity.PublishedReleaseCount) @@ -130,7 +134,7 @@ {{end}} {{if gt .Activity.MergedPRCount 0}} -

+

{{svg "octicon-git-pull-request" 16 "tw-mr-2"}} {{ctx.Locale.Tr "repo.activity.title.prs_merged_by" (ctx.Locale.TrN .Activity.MergedPRCount "repo.activity.title.prs_1" "repo.activity.title.prs_n" .Activity.MergedPRCount) @@ -149,7 +153,7 @@ {{end}} {{if gt .Activity.OpenedPRCount 0}} -

+

{{svg "octicon-git-branch" 16 "tw-mr-2"}} {{ctx.Locale.Tr "repo.activity.title.prs_opened_by" (ctx.Locale.TrN .Activity.OpenedPRCount "repo.activity.title.prs_1" "repo.activity.title.prs_n" .Activity.OpenedPRCount) @@ -168,7 +172,7 @@ {{end}} {{if gt .Activity.ClosedIssueCount 0}} -

+

{{svg "octicon-issue-closed" 16 "tw-mr-2"}} {{ctx.Locale.Tr "repo.activity.title.issues_closed_from" (ctx.Locale.TrN .Activity.ClosedIssueCount "repo.activity.title.issues_1" "repo.activity.title.issues_n" .Activity.ClosedIssueCount) @@ -187,7 +191,7 @@ {{end}} {{if gt .Activity.OpenedIssueCount 0}} -

+

{{svg "octicon-issue-opened" 16 "tw-mr-2"}} {{ctx.Locale.Tr "repo.activity.title.issues_created_by" (ctx.Locale.TrN .Activity.OpenedIssueCount "repo.activity.title.issues_1" "repo.activity.title.issues_n" .Activity.OpenedIssueCount) @@ -206,7 +210,7 @@ {{end}} {{if gt .Activity.UnresolvedIssueCount 0}} -

+

{{svg "octicon-comment-discussion" 16 "tw-mr-2"}} {{ctx.Locale.TrN .Activity.UnresolvedIssueCount "repo.activity.title.unresolved_conv_1" "repo.activity.title.unresolved_conv_n" .Activity.UnresolvedIssueCount}}

diff --git a/web_src/css/dashboard.css b/web_src/css/dashboard.css index d61e0c1cf290..0962215ac6ad 100644 --- a/web_src/css/dashboard.css +++ b/web_src/css/dashboard.css @@ -7,7 +7,6 @@ .dashboard.feeds .context.user.menu .ui.header, .dashboard.issues .context.user.menu .ui.header { font-size: 1rem; - text-transform: none; } .dashboard.feeds .filter.menu, diff --git a/web_src/css/modules/divider.css b/web_src/css/modules/divider.css index 48560bd3d908..acc8408f3760 100644 --- a/web_src/css/modules/divider.css +++ b/web_src/css/modules/divider.css @@ -2,12 +2,16 @@ margin: 10px 0; height: 0; font-weight: var(--font-weight-medium); - text-transform: uppercase; color: var(--color-text); font-size: 1rem; width: 100%; } +h4.divider { + margin-top: 1.25rem; + margin-bottom: 1.25rem; +} + .divider:not(.divider-text) { border-top: 1px solid var(--color-secondary); } diff --git a/web_src/css/modules/header.css b/web_src/css/modules/header.css index 05381e1185e8..9cec5fcbe632 100644 --- a/web_src/css/modules/header.css +++ b/web_src/css/modules/header.css @@ -9,7 +9,6 @@ font-family: var(--fonts-regular); font-weight: var(--font-weight-medium); line-height: 1.28571429; - text-transform: none; } .ui.header:first-child { diff --git a/web_src/css/modules/label.css b/web_src/css/modules/label.css index 0512c5fddbb0..32e772ea5b0f 100644 --- a/web_src/css/modules/label.css +++ b/web_src/css/modules/label.css @@ -10,7 +10,6 @@ background: var(--color-label-bg); color: var(--color-label-text); padding: 0.3em 0.5em; - text-transform: none; font-size: 0.85714286rem; font-weight: var(--font-weight-medium); border: 0 solid transparent; diff --git a/web_src/css/repo.css b/web_src/css/repo.css index c579745238de..edb3bc2e872c 100644 --- a/web_src/css/repo.css +++ b/web_src/css/repo.css @@ -2304,6 +2304,8 @@ td .commit-summary { .stats-table { display: table; width: 100%; + margin: 6px 0; + border-spacing: 2px; } .stats-table .table-cell { @@ -2311,7 +2313,17 @@ td .commit-summary { } .stats-table .table-cell.tiny { - height: 0.5em; + height: 8px; +} + +.stats-table .table-cell:first-child { + border-top-left-radius: 4px; + border-bottom-left-radius: 4px; +} + +.stats-table .table-cell:last-child { + border-top-right-radius: 4px; + border-bottom-right-radius: 4px; } .labels-list { From 4b1063f3dba6ef7a54c15f6e795409b504a62391 Mon Sep 17 00:00:00 2001 From: silverwind Date: Sun, 14 Apr 2024 12:44:11 +0200 Subject: [PATCH 30/35] Rewrite and restyle reaction selector and enable no-sizzle eslint rule (#30453) Enable `no-sizzle` lint rule, there was only one use in `initCompReactionSelector` and: - Remove all jQuery except the necessary fomantic dropdown init - Remove the recursion, instead bind event listeners to common parent container nodes --------- Co-authored-by: wxiaoguang Co-authored-by: Giteabot --- .eslintrc.yaml | 4 +- routers/web/repo/issue.go | 2 - services/context/context.go | 1 + templates/repo/diff/comments.tmpl | 4 +- templates/repo/issue/view_content.tmpl | 4 +- .../repo/issue/view_content/add_reaction.tmpl | 10 +- .../repo/issue/view_content/comments.tmpl | 8 +- .../repo/issue/view_content/conversation.tmpl | 8 +- .../repo/issue/view_content/reactions.tmpl | 8 +- web_src/css/base.css | 6 +- web_src/css/index.css | 1 + web_src/css/modules/comment.css | 2 +- web_src/css/repo.css | 124 ++---------------- web_src/css/repo/reactions.css | 70 ++++++++++ web_src/js/features/comp/ReactionSelector.js | 56 ++++---- web_src/js/features/repo-diff.js | 1 - web_src/js/features/repo-legacy.js | 2 +- 17 files changed, 133 insertions(+), 178 deletions(-) create mode 100644 web_src/css/repo/reactions.css diff --git a/.eslintrc.yaml b/.eslintrc.yaml index 5fd0a245f222..3e4c6ea50b72 100644 --- a/.eslintrc.yaml +++ b/.eslintrc.yaml @@ -318,7 +318,7 @@ rules: jquery/no-serialize: [2] jquery/no-show: [2] jquery/no-size: [2] - jquery/no-sizzle: [0] + jquery/no-sizzle: [2] jquery/no-slide: [0] jquery/no-submit: [0] jquery/no-text: [0] @@ -470,7 +470,7 @@ rules: no-jquery/no-selector-prop: [2] no-jquery/no-serialize: [2] no-jquery/no-size: [2] - no-jquery/no-sizzle: [0] + no-jquery/no-sizzle: [2] no-jquery/no-slide: [2] no-jquery/no-sub: [2] no-jquery/no-support: [2] diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index e4f2e9a2bc24..1364d7567661 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -3318,7 +3318,6 @@ func ChangeIssueReaction(ctx *context.Context) { } html, err := ctx.RenderToHTML(tplReactions, map[string]any{ - "ctxData": ctx.Data, "ActionURL": fmt.Sprintf("%s/issues/%d/reactions", ctx.Repo.RepoLink, issue.Index), "Reactions": issue.Reactions.GroupByType(), }) @@ -3425,7 +3424,6 @@ func ChangeCommentReaction(ctx *context.Context) { } html, err := ctx.RenderToHTML(tplReactions, map[string]any{ - "ctxData": ctx.Data, "ActionURL": fmt.Sprintf("%s/comments/%d/reactions", ctx.Repo.RepoLink, comment.ID), "Reactions": comment.Reactions.GroupByType(), }) diff --git a/services/context/context.go b/services/context/context.go index 7ab48afb73de..9d7787bf4b41 100644 --- a/services/context/context.go +++ b/services/context/context.go @@ -101,6 +101,7 @@ func NewTemplateContextForWeb(ctx *Context) TemplateContext { tmplCtx := NewTemplateContext(ctx) tmplCtx["Locale"] = ctx.Base.Locale tmplCtx["AvatarUtils"] = templates.NewAvatarUtils(ctx) + tmplCtx["RootData"] = ctx.Data return tmplCtx } diff --git a/templates/repo/diff/comments.tmpl b/templates/repo/diff/comments.tmpl index a9120465bdc4..c7f433718241 100644 --- a/templates/repo/diff/comments.tmpl +++ b/templates/repo/diff/comments.tmpl @@ -48,7 +48,7 @@
{{end}} {{end}} - {{template "repo/issue/view_content/add_reaction" dict "ctxData" $.root "ActionURL" (printf "%s/comments/%d/reactions" $.root.RepoLink .ID)}} + {{template "repo/issue/view_content/add_reaction" dict "ActionURL" (printf "%s/comments/%d/reactions" $.root.RepoLink .ID)}} {{template "repo/issue/view_content/context_menu" dict "ctxData" $.root "item" . "delete" true "issue" false "diff" true "IsCommentPoster" (and $.root.IsSigned (eq $.root.SignedUserID .PosterID))}}
@@ -68,7 +68,7 @@
{{$reactions := .Reactions.GroupByType}} {{if $reactions}} - {{template "repo/issue/view_content/reactions" dict "ctxData" $.root "ActionURL" (printf "%s/comments/%d/reactions" $.root.RepoLink .ID) "Reactions" $reactions}} + {{template "repo/issue/view_content/reactions" dict "ActionURL" (printf "%s/comments/%d/reactions" $.root.RepoLink .ID) "Reactions" $reactions}} {{end}}
diff --git a/templates/repo/issue/view_content.tmpl b/templates/repo/issue/view_content.tmpl index c65b79dea70b..06d0586683be 100644 --- a/templates/repo/issue/view_content.tmpl +++ b/templates/repo/issue/view_content.tmpl @@ -46,7 +46,7 @@
{{template "repo/issue/view_content/show_role" dict "ShowRole" .Issue.ShowRole "IgnorePoster" true}} {{if not $.Repository.IsArchived}} - {{template "repo/issue/view_content/add_reaction" dict "ctxData" $ "ActionURL" (printf "%s/issues/%d/reactions" $.RepoLink .Issue.Index)}} + {{template "repo/issue/view_content/add_reaction" dict "ActionURL" (printf "%s/issues/%d/reactions" $.RepoLink .Issue.Index)}} {{end}} {{template "repo/issue/view_content/context_menu" dict "ctxData" $ "item" .Issue "delete" false "issue" true "diff" false "IsCommentPoster" $.IsIssuePoster}}
@@ -67,7 +67,7 @@ {{$reactions := .Issue.Reactions.GroupByType}} {{if $reactions}} - {{template "repo/issue/view_content/reactions" dict "ctxData" $ "ActionURL" (printf "%s/issues/%d/reactions" $.RepoLink .Issue.Index) "Reactions" $reactions}} + {{template "repo/issue/view_content/reactions" dict "ActionURL" (printf "%s/issues/%d/reactions" $.RepoLink .Issue.Index) "Reactions" $reactions}} {{end}} diff --git a/templates/repo/issue/view_content/add_reaction.tmpl b/templates/repo/issue/view_content/add_reaction.tmpl index 37931f287e3d..6baded8fe946 100644 --- a/templates/repo/issue/view_content/add_reaction.tmpl +++ b/templates/repo/issue/view_content/add_reaction.tmpl @@ -1,11 +1,9 @@ -{{if .ctxData.IsSigned}} +{{if ctx.RootData.IsSigned}} {{$reactions := .Reactions.GroupByType}} {{if $reactions}} - {{template "repo/issue/view_content/reactions" dict "ctxData" $ "ActionURL" (printf "%s/comments/%d/reactions" $.RepoLink .ID) "Reactions" $reactions}} + {{template "repo/issue/view_content/reactions" dict "ActionURL" (printf "%s/comments/%d/reactions" $.RepoLink .ID) "Reactions" $reactions}} {{end}} @@ -427,7 +427,7 @@
{{template "repo/issue/view_content/show_role" dict "ShowRole" .ShowRole}} {{if not $.Repository.IsArchived}} - {{template "repo/issue/view_content/add_reaction" dict "ctxData" $ "ActionURL" (printf "%s/comments/%d/reactions" $.RepoLink .ID)}} + {{template "repo/issue/view_content/add_reaction" dict "ActionURL" (printf "%s/comments/%d/reactions" $.RepoLink .ID)}} {{template "repo/issue/view_content/context_menu" dict "ctxData" $ "item" . "delete" false "issue" true "diff" false "IsCommentPoster" (and $.IsSigned (eq $.SignedUserID .PosterID))}} {{end}}
@@ -448,7 +448,7 @@ {{$reactions := .Reactions.GroupByType}} {{if $reactions}} - {{template "repo/issue/view_content/reactions" dict "ctxData" $ "ActionURL" (printf "%s/comments/%d/reactions" $.RepoLink .ID) "Reactions" $reactions}} + {{template "repo/issue/view_content/reactions" dict "ActionURL" (printf "%s/comments/%d/reactions" $.RepoLink .ID) "Reactions" $reactions}} {{end}} diff --git a/templates/repo/issue/view_content/conversation.tmpl b/templates/repo/issue/view_content/conversation.tmpl index 79e7cb498bca..ac32a2db5d2a 100644 --- a/templates/repo/issue/view_content/conversation.tmpl +++ b/templates/repo/issue/view_content/conversation.tmpl @@ -55,8 +55,8 @@
{{range .comments}} {{$createdSubStr:= TimeSinceUnix .CreatedUnix ctx.Locale}} -
-
+
+
{{if not .OriginalAuthor}} @@ -82,7 +82,7 @@
{{template "repo/issue/view_content/show_role" dict "ShowRole" .ShowRole}} {{if not $.Repository.IsArchived}} - {{template "repo/issue/view_content/add_reaction" dict "ctxData" $ "ActionURL" (printf "%s/comments/%d/reactions" $.RepoLink .ID)}} + {{template "repo/issue/view_content/add_reaction" dict "ActionURL" (printf "%s/comments/%d/reactions" $.RepoLink .ID)}} {{template "repo/issue/view_content/context_menu" dict "ctxData" $ "item" . "delete" true "issue" true "diff" true "IsCommentPoster" (and $.IsSigned (eq $.SignedUserID .PosterID))}} {{end}}
@@ -103,7 +103,7 @@
{{$reactions := .Reactions.GroupByType}} {{if $reactions}} - {{template "repo/issue/view_content/reactions" dict "ctxData" $ "ActionURL" (printf "%s/comments/%d/reactions" $.RepoLink .ID) "Reactions" $reactions}} + {{template "repo/issue/view_content/reactions" dict "ActionURL" (printf "%s/comments/%d/reactions" $.RepoLink .ID) "Reactions" $reactions}} {{end}}
diff --git a/templates/repo/issue/view_content/reactions.tmpl b/templates/repo/issue/view_content/reactions.tmpl index da6319667bf7..0011efe8b286 100644 --- a/templates/repo/issue/view_content/reactions.tmpl +++ b/templates/repo/issue/view_content/reactions.tmpl @@ -1,7 +1,7 @@ -
+ diff --git a/web_src/css/base.css b/web_src/css/base.css index c6a22a5dc4bc..02067971a0f4 100644 --- a/web_src/css/base.css +++ b/web_src/css/base.css @@ -1366,8 +1366,7 @@ table th[data-sortt-desc] .svg { box-shadow: 0 0 0 1px var(--color-secondary) inset; } -.emoji, -.reaction { +.emoji { font-size: 1.25em; line-height: var(--line-height-default); font-style: normal !important; @@ -1375,8 +1374,7 @@ table th[data-sortt-desc] .svg { vertical-align: -0.075em; } -.emoji img, -.reaction img { +.emoji img { border-width: 0 !important; margin: 0 !important; width: 1em !important; diff --git a/web_src/css/index.css b/web_src/css/index.css index ad59f32636a2..73e10eedcb41 100644 --- a/web_src/css/index.css +++ b/web_src/css/index.css @@ -62,6 +62,7 @@ @import "./repo/linebutton.css"; @import "./repo/wiki.css"; @import "./repo/header.css"; +@import "./repo/reactions.css"; @import "./editor/fileeditor.css"; @import "./editor/combomarkdowneditor.css"; diff --git a/web_src/css/modules/comment.css b/web_src/css/modules/comment.css index 799eeb8574d2..cf080db22553 100644 --- a/web_src/css/modules/comment.css +++ b/web_src/css/modules/comment.css @@ -16,7 +16,7 @@ .ui.comments .comment { position: relative; background: none; - margin: 0.5em 0 0; + margin: 3px 0 0; padding: 0.5em 0 0; border: none; border-top: none; diff --git a/web_src/css/repo.css b/web_src/css/repo.css index edb3bc2e872c..4b7ad49677a9 100644 --- a/web_src/css/repo.css +++ b/web_src/css/repo.css @@ -913,6 +913,9 @@ td .commit-summary { .repository.view.issue .comment-list .ui.comments { max-width: 100%; + display: flex; + flex-direction: column; + gap: 3px; } .repository.view.issue .comment-list .comment > .content > div:first-child { @@ -928,6 +931,11 @@ td .commit-summary { .repository.view.issue .comment-list .comment .comment-container { border: 1px solid var(--color-secondary); border-radius: var(--border-radius); + background: var(--color-box-body); +} + +.repository.view.issue .comment-list .conversation-holder .comment .comment-container { + border: none; } @media (max-width: 767.98px) { @@ -1042,30 +1050,6 @@ td .commit-summary { margin-left: 42px; } -.repository.view.issue .comment-list .comment-code-cloud .segment.reactions { - margin-top: 16px !important; - margin-bottom: -8px !important; - border-top: none !important; -} - -.repository.view.issue .comment-list .comment-code-cloud .segment.reactions .ui.label { - border: 1px solid; - padding: 5px 8px !important; - margin: 0 2px; - border-radius: var(--border-radius); - border-color: var(--color-secondary-dark-1) !important; -} - -.repository.view.issue .comment-list .comment-code-cloud .segment.reactions .ui.label.basic.primary { - background-color: var(--color-reaction-active-bg) !important; - border-color: var(--color-primary-alpha-80) !important; -} - -.repository.view.issue .comment-list .comment-code-cloud .segment.reactions .ui.label.basic.primary:hover { - background-color: var(--color-reaction-hover-bg) !important; - border-color: var(--color-primary-alpha-80) !important; -} - .repository.view.issue .comment-list .comment-code-cloud button.comment-form-reply { margin: 0; } @@ -1902,98 +1886,6 @@ td .commit-summary { border-bottom: 1px solid var(--color-warning-border); } -.repository .segment.reactions.dropdown .menu, -.repository .select-reaction.dropdown .menu { - right: 0 !important; - left: auto !important; - min-width: 170px; -} - -.repository .segment.reactions.dropdown .menu > .header, -.repository .select-reaction.dropdown .menu > .header { - margin: 0.75rem 0 0.5rem; -} - -.repository .segment.reactions.dropdown .menu > .item, -.repository .select-reaction.dropdown .menu > .item { - float: left; - margin: 4px; - font-size: 20px; - width: 34px; - height: 34px; - min-height: 0 !important; - border-radius: var(--border-radius); - display: flex !important; - align-items: center; - justify-content: center; -} - -.repository .segment.reactions { - padding: 0; - display: flex; - border: none !important; - border-top: 1px solid var(--color-secondary) !important; - width: 100% !important; - max-width: 100% !important; - margin: 0 !important; - border-radius: 0 0 var(--border-radius) var(--border-radius); -} - -.repository .segment.reactions .ui.label { - max-height: 40px; - padding: 8px 16px !important; - display: flex !important; - align-items: center; - border: 0; - border-right: 1px solid; - border-radius: 0; - margin: 0; - font-size: 12px; - font-weight: var(--font-weight-normal); - border-color: var(--color-secondary) !important; - background: var(--color-reaction-bg); -} - -.repository .segment.reactions .ui.label:first-of-type { - border-bottom-left-radius: 3px; -} - -.repository .segment.reactions .ui.label.disabled { - cursor: default; - opacity: 1; -} - -.repository .segment.reactions .ui.label.basic.primary { - color: var(--color-primary) !important; - background-color: var(--color-reaction-active-bg) !important; - border-color: var(--color-secondary-dark-1) !important; -} - -.repository .segment.reactions .ui.label.basic:hover { - background-color: var(--color-reaction-hover-bg) !important; -} - -.repository .segment.reactions .reaction-count { - margin-left: 0.5rem; -} - -.repository .segment.reactions .select-reaction { - display: flex; - align-items: center; -} - -.repository .segment.reactions .select-reaction a { - padding: 0 14px; -} - -.repository .segment.reactions .select-reaction:not(.active) a { - display: none; -} - -.repository .segment.reactions:hover .select-reaction a { - display: block; -} - .repository .ui.fluid.action.input .ui.search.action.input { flex: auto; } diff --git a/web_src/css/repo/reactions.css b/web_src/css/repo/reactions.css new file mode 100644 index 000000000000..8fe01af4f067 --- /dev/null +++ b/web_src/css/repo/reactions.css @@ -0,0 +1,70 @@ +.bottom-reactions { + display: flex; + gap: 6px; + margin: 0 1em 1em; +} + +.timeline-item .conversation-holder .bottom-reactions { + margin: 1em 0 0 36px; + padding-bottom: 8px; +} + +.bottom-reactions .ui.label { + padding: 5px 8px; + font-weight: var(--font-weight-normal); +} + +.bottom-reactions .ui.label.primary { + background-color: var(--color-reaction-active-bg) !important; +} + +.bottom-reactions .ui.label:hover { + background-color: var(--color-reaction-hover-bg) !important; +} + +.bottom-reactions .ui.label.disabled { + cursor: default; + opacity: 1; +} + +.bottom-reactions .ui.label .reaction { + font-size: 16px; + display: flex; +} + +.bottom-reactions .ui.label .reaction img { + height: 16px; + aspect-ratio: 1; +} + +.bottom-reactions .reaction-count { + margin-left: 4px; +} + +.ui.dropdown.select-reaction .menu { + min-width: 170px; /* item-outer-width * 4 */ +} + +.ui.dropdown.select-reaction .menu > .item { + float: left; + margin: 4px; + font-size: 20px; + width: 34px; + height: 34px; + border-radius: var(--border-radius); + display: flex; + align-items: center; + justify-content: center; +} + +.bottom-reactions .select-reaction { + padding: 0 10px; +} + +.bottom-reactions .select-reaction:not(.active) { + visibility: hidden; +} + +.bottom-reactions:hover .select-reaction { + visibility: visible; +} diff --git a/web_src/js/features/comp/ReactionSelector.js b/web_src/js/features/comp/ReactionSelector.js index 2def3db51a7b..e507b89632af 100644 --- a/web_src/js/features/comp/ReactionSelector.js +++ b/web_src/js/features/comp/ReactionSelector.js @@ -1,38 +1,36 @@ import $ from 'jquery'; import {POST} from '../../modules/fetch.js'; -export function initCompReactionSelector($parent) { - $parent.find(`.select-reaction .item.reaction, .comment-reaction-button`).on('click', async function (e) { - e.preventDefault(); +export function initCompReactionSelector() { + for (const container of document.querySelectorAll('.issue-content, .diff-file-body')) { + container.addEventListener('click', async (e) => { + // there are 2 places for the "reaction" buttons, one is the top-right reaction menu, one is the bottom of the comment + const target = e.target.closest('.comment-reaction-button'); + if (!target) return; + e.preventDefault(); - if (this.classList.contains('disabled')) return; + if (target.classList.contains('disabled')) return; - const actionUrl = this.closest('[data-action-url]')?.getAttribute('data-action-url'); - const reactionContent = this.getAttribute('data-reaction-content'); - const hasReacted = this.closest('.ui.segment.reactions')?.querySelector(`a[data-reaction-content="${reactionContent}"]`)?.getAttribute('data-has-reacted') === 'true'; + const actionUrl = target.closest('[data-action-url]').getAttribute('data-action-url'); + const reactionContent = target.getAttribute('data-reaction-content'); - const res = await POST(`${actionUrl}/${hasReacted ? 'unreact' : 'react'}`, { - data: new URLSearchParams({content: reactionContent}), - }); + const commentContainer = target.closest('.comment-container'); - const data = await res.json(); - if (data && (data.html || data.empty)) { - const $content = $(this).closest('.content'); - let $react = $content.find('.segment.reactions'); - if ((!data.empty || data.html === '') && $react.length > 0) { - $react.remove(); - } - if (!data.empty) { - const $attachments = $content.find('.segment.bottom:first'); - $react = $(data.html); - if ($attachments.length > 0) { - $react.insertBefore($attachments); - } else { - $react.appendTo($content); - } - $react.find('.dropdown').dropdown(); - initCompReactionSelector($react); + const bottomReactions = commentContainer.querySelector('.bottom-reactions'); // may not exist if there is no reaction + const bottomReactionBtn = bottomReactions?.querySelector(`a[data-reaction-content="${CSS.escape(reactionContent)}"]`); + const hasReacted = bottomReactionBtn?.getAttribute('data-has-reacted') === 'true'; + + const res = await POST(`${actionUrl}/${hasReacted ? 'unreact' : 'react'}`, { + data: new URLSearchParams({content: reactionContent}), + }); + + const data = await res.json(); + bottomReactions?.remove(); + if (data.html) { + commentContainer.insertAdjacentHTML('beforeend', data.html); + const bottomReactionsDropdowns = commentContainer.querySelectorAll('.bottom-reactions .dropdown.select-reaction'); + $(bottomReactionsDropdowns).dropdown(); // re-init the dropdown } - } - }); + }); + } } diff --git a/web_src/js/features/repo-diff.js b/web_src/js/features/repo-diff.js index b2e8ec4866f9..00f74515df6a 100644 --- a/web_src/js/features/repo-diff.js +++ b/web_src/js/features/repo-diff.js @@ -87,7 +87,6 @@ function initRepoDiffConversationForm() { el.classList.add('tw-invisible'); } $newConversationHolder.find('.dropdown').dropdown(); - initCompReactionSelector($newConversationHolder); } catch (error) { console.error('Error:', error); showErrorToast(i18n.network_error); diff --git a/web_src/js/features/repo-legacy.js b/web_src/js/features/repo-legacy.js index e83de27e4cf4..18d98c891d06 100644 --- a/web_src/js/features/repo-legacy.js +++ b/web_src/js/features/repo-legacy.js @@ -393,7 +393,7 @@ export function initRepository() { initRepoIssueDependencyDelete(); initRepoIssueCodeCommentCancel(); initRepoPullRequestUpdate(); - initCompReactionSelector($(document)); + initCompReactionSelector(); initRepoPullRequestMergeForm(); initRepoPullRequestCommitStatus(); From 044cc169e75dccbf1d846f8774ef2feccd0da1fd Mon Sep 17 00:00:00 2001 From: silverwind Date: Sun, 14 Apr 2024 13:39:11 +0200 Subject: [PATCH 31/35] Use `flex-container` for dashboard layout (#30214) Added new class `flex-container-sidebar` to cover the dashboard sidebar. Previously this was 37.5% with more padding. Now there is less empty space between the two columns and this matches other pages like repo or admin settings page. Desktop: Screenshot 2024-03-31 at 15 11 36 Mobile: Screenshot 2024-03-31 at 15 11 44 --------- Co-authored-by: Giteabot --- templates/user/dashboard/dashboard.tmpl | 12 +++++------- templates/user/dashboard/repolist.tmpl | 2 +- web_src/css/modules/flexcontainer.css | 10 +++++++++- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/templates/user/dashboard/dashboard.tmpl b/templates/user/dashboard/dashboard.tmpl index d4553ea61ba2..030fd4994065 100644 --- a/templates/user/dashboard/dashboard.tmpl +++ b/templates/user/dashboard/dashboard.tmpl @@ -1,15 +1,13 @@ {{template "base/head" .}}
{{template "user/dashboard/navbar" .}} -
+
{{template "base/alert" .}} -
-
- {{template "user/heatmap" .}} - {{template "user/dashboard/feeds" .}} -
- {{template "user/dashboard/repolist" .}} +
+ {{template "user/heatmap" .}} + {{template "user/dashboard/feeds" .}}
+ {{template "user/dashboard/repolist" .}}
{{template "base/footer" .}} diff --git a/templates/user/dashboard/repolist.tmpl b/templates/user/dashboard/repolist.tmpl index a879f1fb9d87..be710675d560 100644 --- a/templates/user/dashboard/repolist.tmpl +++ b/templates/user/dashboard/repolist.tmpl @@ -56,4 +56,4 @@ data.organizationId = {{.ContextUser.ID}}; window.config.pageData.dashboardRepoList = data; -
+
diff --git a/web_src/css/modules/flexcontainer.css b/web_src/css/modules/flexcontainer.css index 1ca513687ffc..5d4e12cc129b 100644 --- a/web_src/css/modules/flexcontainer.css +++ b/web_src/css/modules/flexcontainer.css @@ -6,10 +6,16 @@ margin-top: var(--page-spacing); } +/* small options menu on the left, used in settings/admin pages */ .flex-container-nav { width: 240px; } +/* wide sidebar on the right, used in frontpage */ +.flex-container-sidebar { + width: 35%; +} + .flex-container-main { flex: 1; min-width: 0; /* make the "text truncate" work, otherwise the flex axis is not limited and the text just overflows */ @@ -19,7 +25,9 @@ .flex-container { flex-direction: column; } - .flex-container-nav { + .flex-container-nav, + .flex-container-sidebar { + order: -1; width: auto; } } From f3267548abfd4deda1aaeb4b336df6cd4b0e1d70 Mon Sep 17 00:00:00 2001 From: silverwind Date: Sun, 14 Apr 2024 13:43:46 +0200 Subject: [PATCH 32/35] Remove fomantic menu module (#30325) A lot of variants are in use, so the diff stat isn't so great. Co-authored-by: Giteabot --- web_src/css/base.css | 186 +-- web_src/css/index.css | 1 + web_src/css/modules/menu.css | 802 +++++++++++ web_src/css/repo.css | 5 - web_src/fomantic/build/semantic.css | 2010 --------------------------- web_src/fomantic/semantic.json | 1 - 6 files changed, 804 insertions(+), 2201 deletions(-) create mode 100644 web_src/css/modules/menu.css diff --git a/web_src/css/base.css b/web_src/css/base.css index 02067971a0f4..20f361617749 100644 --- a/web_src/css/base.css +++ b/web_src/css/base.css @@ -319,27 +319,6 @@ a.label, background-color: var(--color-label-bg); } -.ui.menu { - display: flex; -} - -.ui.menu, -.ui.vertical.menu { - background: var(--color-menu); - border-color: var(--color-secondary); - box-shadow: none; -} - -.ui.menu .item { - color: var(--color-text); - user-select: auto; - line-height: var(--line-height-default); /* fomantic uses "1" which causes overflow problems because "1" doesn't consider the descent part */ -} - -.ui.menu .item > .svg { - margin-right: 0.35em; -} - .ui.menu .dropdown.item:hover, .ui.menu a.item:hover, .ui.menu details.item summary:hover { @@ -347,42 +326,6 @@ a.label, background: var(--color-hover); } -.ui.menu .active.item, -.ui.menu .active.item:hover, -.ui.vertical.menu .active.item, -.ui.vertical.menu .active.item:hover { - color: var(--color-text); - background: var(--color-active); -} - -.ui.menu a.item:active { - color: var(--color-text); - background: none; -} - -.ui.ui.menu .item.disabled { - color: var(--color-text-light-3); -} - -.ui.menu .item::before, .ui.vertical.menu .item::before { - background: var(--color-secondary); -} - -/* sub menu of vertical menu */ -.ui.vertical.menu .item .menu .item { - color: var(--color-text-light-2); - text-indent: 16px; -} - -.ui.vertical.menu .item .menu .item:hover, -.ui.vertical.menu .item .menu a.item:hover { - color: var(--color-text-light-1); -} - -.ui.vertical.menu .item .menu .active.item { - color: var(--color-text); -} - /* slightly more contrast for filters on issue list */ .ui.ui.menu .dropdown.item.disabled { color: var(--color-text-light-2); @@ -441,11 +384,6 @@ a.label, background: var(--color-hover); } -.ui.menu .ui.dropdown .menu > .selected.item { - color: var(--color-text) !important; - background: var(--color-hover) !important; -} - .ui.dropdown .menu > .message:not(.ui) { color: var(--color-text-light-2); } @@ -462,58 +400,6 @@ a.label, color: var(--color-text-light-2); } -/* replace item margin on secondary menu items with gap and remove both the - negative margins on the menu as well as margin on the items */ -.ui.secondary.menu { - margin-left: 0; - margin-right: 0; - gap: .35714286em; -} -.ui.secondary.menu .item { - margin-left: 0; - margin-right: 0; -} - -.ui.secondary.menu .dropdown.item:hover, -.ui.secondary.menu a.item:hover { - color: var(--color-text); - background: var(--color-hover); -} - -.ui.secondary.menu .active.item, -.ui.secondary.menu .active.item:hover { - color: var(--color-text); - background: var(--color-active); -} - -.ui.secondary.menu.tight .item { - padding-left: 0.85714286em; - padding-right: 0.85714286em; -} - -/* remove the menu clearfix so that it won't add undesired gaps when using "gap" */ -.ui.menu::after { - content: normal; -} - -.ui.menu .dropdown.item .menu { - background: var(--color-body); -} - -.ui.menu .ui.dropdown .menu > .item { - color: var(--color-text) !important; -} - -.ui.menu .ui.dropdown .menu > .item:hover { - color: var(--color-text) !important; - background: var(--color-hover) !important; -} - -.ui.menu .ui.dropdown .menu > .active.item { - color: var(--color-text) !important; - background: var(--color-active) !important; -} - .ui.form textarea:not([rows]) { height: var(--min-height-textarea); /* override fomantic default 12em */ min-height: var(--min-height-textarea); /* override fomantic default 8em */ @@ -606,11 +492,6 @@ img.ui.avatar, margin-top: calc(var(--page-spacing) - 1rem); } -.ui.pagination.menu .active.item { - color: var(--color-text); - background: var(--color-active); -} - .ui.form .fields.error .field textarea, .ui.form .fields.error .field select, .ui.form .fields.error .field input:not([type]), @@ -786,7 +667,7 @@ input:-webkit-autofill:active, font-weight: var(--font-weight-normal); } -/* replace fomantic popover box shadows */ +/* popover box shadows */ .ui.dropdown .menu, .ui.upward.dropdown > .menu, .ui.menu .dropdown.item .menu, @@ -804,22 +685,6 @@ input:-webkit-autofill:active, background: var(--color-overlay-backdrop); } -/* Override semantic selector '.ui.menu:not(.vertical) .item > .button' */ -/* This fixes the commit graph button on the commits page */ -/* modal svg icons, copied from fomantic except width and height */ -/* center text in fomantic modal dialogs */ -.ui .menu:not(.vertical) .item > .button.compact { - padding: 0.58928571em 1.125em; -} - -.ui .menu:not(.vertical) .item > .button.small { - font-size: 0.92857143rem; -} - -.ui.menu .ui.dropdown.item .menu .item { - width: 100%; -} - .ui.dropdown .menu > .header { font-size: 0.8em; } @@ -1010,24 +875,6 @@ input:-webkit-autofill:active, border-color: var(--color-gold) !important; } -@media (max-width: 767.98px) { - .ui.pagination.menu .item:not(.active,.navigation), - .ui.pagination.menu .item.navigation span.navigation_label { - display: none; - } -} - -.ui.pagination.menu.narrow .item { - padding-left: 8px; - padding-right: 8px; - min-width: 1em; - text-align: center; -} - -.ui.pagination.menu.narrow .item .icon { - margin-right: 0; -} - .ui.floating.dropdown .overflow.menu .scrolling.menu.items { border-radius: 0 !important; box-shadow: none !important; @@ -1149,11 +996,6 @@ overflow-menu .ui.label { margin-top: 1px; } -.ui.menu .item > .label { - background: var(--color-label-bg); - color: var(--color-label-text); -} - .lines-blame-btn { padding: 0 0 0 5px; display: flex; @@ -1382,26 +1224,6 @@ table th[data-sortt-desc] .svg { vertical-align: -0.15em; } -.ui.tabular.menu { - border-color: var(--color-secondary); -} - -.ui.tabular.menu .active.item, -.ui.tabular.menu .active.item:hover { - background: var(--color-body); - border-color: var(--color-secondary); - color: var(--color-text); -} - -.ui.segment .ui.tabular.menu .active.item, -.ui.segment .ui.tabular.menu .active.item:hover { - background: var(--color-box-body); -} - -.ui.secondary.pointing.menu { - border-color: var(--color-secondary); -} - .ui.tabular.menu .item, .ui.secondary.pointing.menu .item { padding: 11.55px 12px !important; /* match .dashboard-navbar in height */ @@ -1413,12 +1235,6 @@ table th[data-sortt-desc] .svg { color: var(--color-text); } -.ui.secondary.pointing.menu .active.item, -.ui.secondary.pointing.menu .active.item:hover, -.ui.secondary.pointing.menu .dropdown.item:hover { - color: var(--color-text-dark); -} - .ui.tabular.menu .active.item, .ui.secondary.pointing.menu .active.item, .resize-for-semibold::before { diff --git a/web_src/css/index.css b/web_src/css/index.css index 73e10eedcb41..edd6cdca8bd5 100644 --- a/web_src/css/index.css +++ b/web_src/css/index.css @@ -11,6 +11,7 @@ @import "./modules/list.css"; @import "./modules/segment.css"; @import "./modules/grid.css"; +@import "./modules/menu.css"; @import "./modules/message.css"; @import "./modules/table.css"; @import "./modules/card.css"; diff --git a/web_src/css/modules/menu.css b/web_src/css/modules/menu.css new file mode 100644 index 000000000000..2581d8fab2ae --- /dev/null +++ b/web_src/css/modules/menu.css @@ -0,0 +1,802 @@ +.ui.menu { + display: flex; + margin: 1rem 0; + font-family: var(--fonts-regular); + font-weight: var(--font-weight-normal); + background: var(--color-menu); + border: 1px solid var(--color-secondary); + border-radius: 0.28571429rem; + min-height: 2.85714286em; + font-size: 1rem; +} +.ui.menu:first-child { + margin-top: 0; +} +.ui.menu:last-child { + margin-bottom: 0; +} + +.ui.menu .menu { + margin: 0; +} +.ui.menu:not(.vertical) > .menu { + display: flex; +} + +.ui.menu:not(.vertical) .item { + display: flex; + align-items: center; +} + +.ui.menu .item { + position: relative; + vertical-align: middle; + line-height: var(--line-height-default); + text-decoration: none; + flex: 0 0 auto; + background: none; + padding: 0.92857143em 1.14285714em; + color: var(--color-text); + font-weight: var(--font-weight-normal); +} +.ui.menu > .item:first-child { + border-radius: 0.28571429rem 0 0 0.28571429rem; +} + +.ui.menu .item::before { + position: absolute; + content: ""; + top: 0; + right: 0; + height: 100%; + width: 1px; + background: var(--color-secondary); +} + +.ui.menu .item > .svg { + margin-right: 0.35em; +} + +.ui.menu .item > a:not(.ui), +.ui.menu .item > p:only-child { + line-height: 1.3; +} +.ui.menu .item > p:first-child { + margin-top: 0; +} +.ui.menu .item > p:last-child { + margin-bottom: 0; +} + +.ui.menu .item > i.icon { + opacity: 0.9; + float: none; + margin: 0 0.35714286em 0 0; +} + +.ui.menu:not(.vertical) .item > .button { + position: relative; + top: 0; + margin: -0.5em 0; + padding: 0.58928571em 1.125em; + font-size: 1em; +} + +.ui.menu > .grid, +.ui.menu > .container { + display: flex; + align-items: inherit; + flex-direction: inherit; +} + +.ui.menu .item > .input { + width: 100%; +} +.ui.menu:not(.vertical) .item > .input { + position: relative; + top: 0; + margin: -0.5em 0; +} +.ui.menu .item > .input input { + font-size: 1em; + padding-top: 0.57142857em; + padding-bottom: 0.57142857em; +} + +.ui.menu .header.item, +.ui.vertical.menu .header.item { + margin: 0; + font-size: 1.1em; + background: var(--color-box-header); + font-weight: var(--font-weight-medium); +} +.ui.vertical.menu .item > .header:not(.ui) { + margin: 0 0 0.5em; + font-size: 1em; + font-weight: var(--font-weight-medium); +} + +.ui.menu .item > i.dropdown.icon { + padding: 0; + float: right; + margin: 0 0 0 1em; +} + +.ui.menu .dropdown.item .menu { + min-width: calc(100% - 1px); + border-radius: 0 0 0.28571429rem 0.28571429rem; + background: var(--color-body); + margin: 0; + flex-direction: column !important; +} + +.ui.menu .ui.dropdown .menu > .item { + margin: 0; + text-align: left; + font-size: 1em !important; + padding: 0.78571429em 1.14285714em !important; + background: transparent !important; + color: var(--color-text) !important; + font-weight: var(--font-weight-normal) !important; +} +.ui.menu .ui.dropdown .menu > .item:hover { + color: var(--color-text) !important; + background: var(--color-hover) !important; +} +.ui.menu .ui.dropdown .menu > .selected.item { + color: var(--color-text) !important; + background: var(--color-hover) !important; +} +.ui.menu .ui.dropdown .menu > .active.item { + color: var(--color-text) !important; + background: var(--color-active) !important; + font-weight: var(--font-weight-medium) !important; +} + +.ui.menu .ui.dropdown.item .menu .item { + width: 100%; +} + +.ui.menu .ui.dropdown.item .menu .item:not(.filtered) { + display: block; +} +.ui.menu .ui.dropdown .menu > .item > i.icon:not(.dropdown) { + display: inline-block; + font-size: 1em !important; + float: none; + margin: 0 0.75em 0 0 !important; +} + +.ui.secondary.menu .dropdown.item > .menu { + border-radius: 0.28571429rem; + margin-top: 0.35714286em; +} + +.ui.menu .pointing.dropdown.item .menu { + margin-top: 0.75em; +} + +.ui.menu .item > .label:not(.floating) { + margin-left: 1em; + padding: 0.3em 0.78571429em; +} +.ui.vertical.menu .item > .label { + margin-top: -0.15em; + margin-bottom: -0.15em; + padding: 0.3em 0.78571429em; + float: right; + text-align: center; +} +.ui.menu .item > .floating.label { + padding: 0.3em 0.78571429em; +} +.ui.menu .item > .label { + background: var(--color-label-bg); + color: var(--color-label-text); +} +.ui.menu .item > .image.label img { + margin: -0.2833em 0.8em -0.2833em -0.8em; + height: 1.5666em; +} + +.ui.menu .item > img:not(.ui) { + display: inline-block; + vertical-align: middle; + margin: -0.3em 0; + width: 2.5em; +} +.ui.vertical.menu .item > img:not(.ui):only-child { + display: block; + max-width: 100%; + width: auto; +} + +.ui.menu .list .item::before { + background: none !important; +} + +@media only screen and (max-width: 767.98px) { + .ui.menu > .ui.container { + width: 100% !important; + margin-left: 0 !important; + margin-right: 0 !important; + } +} + +.ui.menu .dropdown.item:hover, +.ui.menu a.item:hover { + cursor: pointer; +} + +.ui.menu a.item:active { + color: var(--color-text); + background: none; +} + +.ui.menu .active.item { + color: var(--color-text); + background: var(--color-active); + font-weight: var(--font-weight-normal); +} +.ui.menu .active.item > i.icon { + opacity: 1; +} + +.ui.ui.menu .item.disabled { + cursor: default; + background-color: transparent; + pointer-events: none; + opacity: var(--opacity-disabled); +} + +.ui.menu:not(.vertical) .left.item, +.ui.menu:not(.vertical) .left.menu { + display: flex; + margin-right: auto !important; +} + +.ui.menu:not(.vertical) .right.item, +.ui.menu:not(.vertical) .right.menu { + display: flex; + margin-left: auto !important; +} +.ui.menu:not(.vertical) :not(.dropdown) > .left.menu, +.ui.menu:not(.vertical) :not(.dropdown) > .right.menu { + display: inherit; +} + +.ui.menu:not(.vertical) .center.item { + display: flex; + margin-left: auto !important; + margin-right: auto !important; +} + +.ui.menu .right.item::before, +.ui.menu .right.menu > .item::before { + right: auto; + left: 0; +} + +.ui.menu .center.item:last-child::before { + display: none; +} + +.ui.vertical.menu { + display: block; + flex-direction: column; + background: var(--color-menu); + width: 15rem; +} + +.ui.vertical.menu .item { + display: block; + background: none; + border-top: none; + border-right: none; +} +.ui.vertical.menu > .item:first-child { + border-radius: 0.28571429rem 0.28571429rem 0 0; +} +.ui.vertical.menu > .item:last-child { + border-radius: 0 0 0.28571429rem 0.28571429rem; +} + +.ui.vertical.menu .item > i.icon { + width: 1.18em; + float: right; + margin: 0 0 0 0.5em; +} +.ui.vertical.menu .item > .label + i.icon { + float: none; + margin: 0 0.5em 0 0; +} + +.ui.vertical.menu .item::before { + position: absolute; + content: ""; + top: 0; + left: 0; + width: 100%; + height: 1px; + background: var(--color-secondary); +} +.ui.vertical.menu .item:first-child::before { + display: none !important; +} + +.ui.vertical.menu .item > .menu { + margin: 0.5em -1.14285714em 0; +} +.ui.vertical.menu .menu .item { + background: none; + padding: 0.5em 1.33333333em; + font-size: 0.85714286em; + color: var(--color-text-light-2); +} + +.ui.vertical.menu .item .menu .item { + color: var(--color-text-light-2); + text-indent: 16px; +} + +.ui.vertical.menu .item .menu .item:hover, +.ui.vertical.menu .item .menu a.item:hover { + color: var(--color-text-light-1); +} + +.ui.vertical.menu .item .menu .active.item { + background-color: transparent; + font-weight: var(--font-weight-medium); + color: var(--color-text); +} + +.ui.vertical.menu .item .menu a.item:hover { + color: var(--color-text); +} +.ui.vertical.menu .menu .item::before { + display: none; +} + +.ui.vertical.menu .active.item { + border-radius: 0; +} +.ui.vertical.menu > .active.item:first-child { + border-radius: 0.28571429rem 0.28571429rem 0 0; +} +.ui.vertical.menu > .active.item:last-child { + border-radius: 0 0 0.28571429rem 0.28571429rem; +} +.ui.vertical.menu > .active.item:only-child { + border-radius: 0.28571429rem; +} +.ui.vertical.menu .active.item .menu .active.item { + border-left: none; +} + +.ui.tabular.menu { + border-radius: 0; + border: none; + background: none transparent; + border-bottom: 1px solid var(--color-secondary); +} +.ui.tabular.fluid.menu { + width: calc(100% + 2px) !important; +} +.ui.tabular.menu .item { + background: transparent; + border-bottom: none; + border-left: 1px solid transparent; + border-right: 1px solid transparent; + border-top: 2px solid transparent; + color: var(--color-text-light-2); +} +.ui.tabular.menu .item::before { + display: none; +} + +.ui.tabular.menu .item:hover { + background-color: transparent; +} + +.ui.tabular.menu .active.item, +.ui.tabular.menu .active.item:hover { + background: var(--color-body); + border-top-width: 1px; + border-color: var(--color-secondary); + font-weight: var(--font-weight-medium); + margin-bottom: -1px; + border-radius: 0.28571429rem 0.28571429rem 0 0 !important; +} + +.ui.tabular.menu + .attached:not(.top).segment, +.ui.tabular.menu + .attached:not(.top).segment + .attached:not(.top).segment { + border-top: none; + margin-left: 0; + margin-top: 0; + margin-right: 0; + width: 100%; +} + +.ui.tabular.menu .active.dropdown.item { + margin-bottom: 0; + border-left: 1px solid transparent; + border-right: 1px solid transparent; + border-top: 2px solid transparent; + border-bottom: none; +} + +.ui.pagination.menu { + margin: 0; + display: inline-flex; + vertical-align: middle; +} +.ui.pagination.menu .item:last-child { + border-radius: 0 0.28571429rem 0.28571429rem 0; +} +.ui.compact.menu .item:last-child { + border-radius: 0 0.28571429rem 0.28571429rem 0; +} +.ui.pagination.menu .item:last-child::before { + display: none; +} +.ui.pagination.menu .item { + min-width: 3em; + text-align: center; +} +.ui.pagination.menu .icon.item i.icon { + vertical-align: top; +} + +.ui.pagination.menu .active.item, +.ui.pagination.menu .active.item:hover { + border-top: none; + padding-top: 0.92857143em; + color: var(--color-text); + background: var(--color-active); +} + +@media (max-width: 767.98px) { + .ui.pagination.menu .item:not(.active,.navigation), + .ui.pagination.menu .item.navigation span.navigation_label { + display: none; + } +} + +.ui.pagination.menu.narrow .item { + padding-left: 8px; + padding-right: 8px; + min-width: 1em; + text-align: center; +} + +.ui.pagination.menu.narrow .item .icon { + margin-right: 0; +} + +.ui.secondary.menu { + background: none; + margin-left: 0; + margin-right: 0; + gap: .35714286em; + border-radius: 0; + border: none; +} + +.ui.secondary.menu .item { + align-self: center; + border: none; + padding: 0.78571429em 0.92857143em; + margin: 0; + background: none; + border-radius: 0.28571429rem; +} + +.ui.secondary.menu .item::before { + display: none !important; +} + +.ui.secondary.menu .header.item { + border-radius: 0; + border-right: none; + background: none transparent; +} + +.ui.secondary.menu .item > img:not(.ui) { + margin: 0; +} + +.ui.secondary.menu .dropdown.item:hover, +.ui.secondary.menu a.item:hover { + color: var(--color-text); + background: var(--color-hover); +} + +.ui.secondary.menu .active.item, +.ui.secondary.menu .active.item:hover { + color: var(--color-text-dark); + background: var(--color-active); + border-radius: 0.28571429rem; +} + +.ui.secondary.item.menu { + margin-left: 0; + margin-right: 0; +} +.ui.secondary.item.menu .item:last-child { + margin-right: 0; +} + +.ui.vertical.secondary.menu .item:not(.dropdown) > .menu { + margin: 0 -0.92857143em; +} +.ui.vertical.secondary.menu .item:not(.dropdown) > .menu > .item { + margin: 0; + padding: 0.5em 1.33333333em; +} +.ui.secondary.vertical.menu > .item { + border: none; + margin: 0 0 0.35714286em; + border-radius: 0.28571429rem !important; +} +.ui.secondary.vertical.menu > .header.item { + border-radius: 0; +} + +.ui.vertical.secondary.menu .item > .menu .item { + background-color: transparent; +} + +.ui.secondary.pointing.menu { + margin-left: 0; + margin-right: 0; + border-bottom: 2px solid var(--color-secondary); +} +.ui.secondary.pointing.menu .item { + border-bottom-color: transparent; + border-bottom-style: solid; + border-radius: 0; + align-self: flex-end; + margin: 0 0 -2px; + padding: 0.85714286em 1.14285714em; + border-bottom-width: 2px; +} +.ui.secondary.pointing.menu .ui.dropdown .menu .item { + border-bottom-width: 0; +} +.ui.secondary.pointing.menu .item > .label:not(.floating) { + margin-top: -0.3em; + margin-bottom: -0.3em; +} +.ui.secondary.pointing.menu .item > .circular.label { + margin-top: -0.5em; + margin-bottom: -0.5em; +} + +.ui.secondary.pointing.menu .header.item { + color: var(--color-text) !important; +} +.ui.secondary.pointing.menu .item::after { + display: none; +} + +.ui.secondary.pointing.menu .dropdown.item:hover, +.ui.secondary.pointing.menu a.item:hover { + background-color: transparent; + color: var(--color-text); +} + +.ui.secondary.pointing.menu .dropdown.item:active, +.ui.secondary.pointing.menu a.item:active { + background-color: transparent; + border-color: var(--color-secondary); +} + +.ui.secondary.pointing.menu .active.item { + background-color: transparent; + border-color: currentcolor; + font-weight: var(--font-weight-medium); +} + +.ui.secondary.pointing.menu .active.item, +.ui.secondary.pointing.menu .active.item:hover, +.ui.secondary.pointing.menu .dropdown.item:hover { + color: var(--color-text-dark); +} + +.ui.secondary.pointing.menu .active.dropdown.item { + border-color: transparent; +} + +@media only screen and (max-width: 767.98px) { + .ui.stackable.menu { + flex-direction: column; + } + .ui.stackable.menu .item { + width: 100% !important; + } + .ui.stackable.menu .left.menu, + .ui.stackable.menu .left.item { + margin-right: 0 !important; + } + .ui.stackable.menu .right.menu, + .ui.stackable.menu .right.item { + margin-left: 0 !important; + } + .ui.stackable.menu .center.item { + margin-left: 0 !important; + margin-right: 0 !important; + } + .ui.stackable.menu .right.menu, + .ui.stackable.menu .left.menu { + flex-direction: column; + } +} + +.ui.floated.menu { + float: left; + margin: 0 0.5rem 0 0; +} +.ui.floated.menu .item:last-child::before { + display: none; +} +.ui.right.floated.menu { + float: right; + margin: 0 0 0 0.5rem; +} + +.ui.borderless.menu .item::before, +.ui.borderless.menu .item .menu .item::before, +.ui.menu .borderless.item::before { + background: none !important; +} + +.ui.compact.menu { + display: inline-flex; + margin: 0; + vertical-align: middle; +} +.ui.compact.vertical.menu { + display: inline-block; + width: auto !important; +} +.ui.compact.menu:not(.secondary) .item:last-child { + border-radius: 0 0.28571429rem 0.28571429rem 0; +} +.ui.compact.menu .item:last-child::before { + display: none; +} +.ui.compact.vertical.menu .item:last-child::before { + display: block; +} + +.ui.menu.fluid, +.ui.vertical.menu.fluid { + width: 100% !important; +} + +.ui.item.menu, +.ui.item.menu .item { + width: 100%; + padding-left: 0 !important; + padding-right: 0 !important; + margin-left: 0 !important; + margin-right: 0 !important; + text-align: center; + justify-content: center; +} +.ui.attached.item.menu:not(.tabular) { + margin: 0 -1px !important; +} +.ui.item.menu .item:last-child::before { + display: none; +} +.ui.menu.two.item .item { + width: 50%; +} + +.ui.pointing.menu .item::after { + visibility: hidden; + position: absolute; + content: ""; + top: 100%; + left: 50%; + transform: translateX(-50%) translateY(-50%) rotate(45deg); + background: none; + margin: 0.5px 0 0; + width: 0.57142857em; + height: 0.57142857em; + border: none; + border-bottom: 1px solid var(--color-secondary); + border-right: 1px solid var(--color-secondary); + z-index: 2; +} +.ui.pointing.menu .ui.dropdown .menu .item::after { + display: none; +} + +.ui.pointing.menu .active.item::after { + visibility: visible; +} +.ui.pointing.menu .active.dropdown.item::after { + visibility: hidden; +} + +.ui.pointing.menu .dropdown.active.item::after, +.ui.pointing.menu .active.item .menu .active.item::after { + display: none; +} + +.ui.pointing.menu .active.item::after, +.ui.pointing.menu .active.item:hover::after { + background-color: var(--color-active); +} + +.ui.attached.menu { + top: 0; + bottom: 0; + border-radius: 0; + margin: 0 -1px; + width: calc(100% + 2px); + max-width: calc(100% + 2px); +} +.ui.attached + .ui.attached.menu:not(.top) { + border-top: none; +} + +.ui[class*="top attached"].menu { + bottom: 0; + margin-bottom: 0; + top: 0; + margin-top: 1rem; + border-radius: 0.28571429rem 0.28571429rem 0 0; +} +.ui.menu[class*="top attached"]:first-child { + margin-top: 0; +} + +.ui.top.attached.menu > .item:first-child { + border-radius: 0.28571429rem 0 0; +} + +.ui.attached.menu:not(.tabular) { + border: 1px solid var(--color-secondary); +} +.ui.attached.tabular.menu { + margin-left: 0; + margin-right: 0; + width: 100%; +} + +.ui.mini.menu, +.ui.mini.menu .dropdown, +.ui.mini.menu .dropdown .menu > .item { + font-size: 0.78571429rem; +} +.ui.mini.vertical.menu:not(.icon) { + width: 9rem; +} +.ui.tiny.menu, +.ui.tiny.menu .dropdown, +.ui.tiny.menu .dropdown .menu > .item { + font-size: 0.85714286rem; +} +.ui.tiny.vertical.menu:not(.icon) { + width: 11rem; +} +.ui.small.menu, +.ui.small.menu .dropdown, +.ui.small.menu .dropdown .menu > .item { + font-size: 0.92857143rem; +} +.ui.small.vertical.menu:not(.icon) { + width: 13rem; +} + +.ui .menu:not(.vertical) .item > .button.small { + font-size: 0.92857143rem; +} + +.ui.segment .ui.tabular.menu .active.item, +.ui.segment .ui.tabular.menu .active.item:hover { + background: var(--color-box-body); +} diff --git a/web_src/css/repo.css b/web_src/css/repo.css index 4b7ad49677a9..52f9d5a6ca83 100644 --- a/web_src/css/repo.css +++ b/web_src/css/repo.css @@ -2079,11 +2079,6 @@ td .commit-summary { padding: 10px 0 0; } -.ui.vertical.menu .header.item { - font-size: 1.1em; - background: var(--color-box-header); -} - .comment:target .comment-container { border-color: var(--color-primary) !important; box-shadow: 0 0 0 3px var(--color-primary-alpha-30) !important; diff --git a/web_src/fomantic/build/semantic.css b/web_src/fomantic/build/semantic.css index 49c00c4dad7a..8ce9ee24ea8f 100644 --- a/web_src/fomantic/build/semantic.css +++ b/web_src/fomantic/build/semantic.css @@ -6474,2016 +6474,6 @@ select.ui.dropdown { Theme Overrides *******************************/ -/******************************* - Site Overrides -*******************************/ -/* - * # Fomantic - Menu - * http://github.com/fomantic/Fomantic-UI/ - * - * - * Copyright 2015 Contributor - * Released under the MIT license - * http://opensource.org/licenses/MIT - * - */ - -/******************************* - Standard -*******************************/ - -/*-------------- - Menu ----------------*/ - -.ui.menu { - display: flex; - margin: 1rem 0; - font-family: var(--fonts-regular); - background: #FFFFFF; - font-weight: normal; - border: 1px solid rgba(34, 36, 38, 0.15); - box-shadow: 0 1px 2px 0 rgba(34, 36, 38, 0.15); - border-radius: 0.28571429rem; - min-height: 2.85714286em; -} - -.ui.menu:after { - content: ''; - display: block; - height: 0; - clear: both; - visibility: hidden; -} - -.ui.menu:first-child { - margin-top: 0; -} - -.ui.menu:last-child { - margin-bottom: 0; -} - -/*-------------- - Sub-Menu ----------------*/ - -.ui.menu .menu { - margin: 0; -} - -.ui.menu:not(.vertical) > .menu { - display: flex; -} - -/*-------------- - Item ----------------*/ - -.ui.menu:not(.vertical) .item { - display: flex; - align-items: center; -} - -.ui.menu .item { - position: relative; - vertical-align: middle; - line-height: 1; - text-decoration: none; - -webkit-tap-highlight-color: transparent; - flex: 0 0 auto; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none; - background: none; - padding: 0.92857143em 1.14285714em; - text-transform: none; - color: rgba(0, 0, 0, 0.87); - font-weight: normal; - transition: background 0.1s ease, box-shadow 0.1s ease, color 0.1s ease; -} - -.ui.menu > .item:first-child { - border-radius: 0.28571429rem 0 0 0.28571429rem; -} - -/* Border */ - -.ui.menu .item:before { - position: absolute; - content: ''; - top: 0; - right: 0; - height: 100%; - width: 1px; - background: rgba(34, 36, 38, 0.1); -} - -/*-------------- - Text Content ----------------*/ - -.ui.menu .text.item > *, -.ui.menu .item > a:not(.ui), -.ui.menu .item > p:only-child { - -webkit-user-select: text; - -moz-user-select: text; - user-select: text; - line-height: 1.3; -} - -.ui.menu .item > p:first-child { - margin-top: 0; -} - -.ui.menu .item > p:last-child { - margin-bottom: 0; -} - -/*-------------- - Icons ----------------*/ - -.ui.menu .item > i.icon { - opacity: 0.9; - float: none; - margin: 0 0.35714286em 0 0; -} - -/*-------------- - Button ----------------*/ - -.ui.menu:not(.vertical) .item > .button { - position: relative; - top: 0; - margin: -0.5em 0; - padding-bottom: 0.78571429em; - padding-top: 0.78571429em; - font-size: 1em; -} - -/*---------------- - Grid / Container ------------------*/ - -.ui.menu > .grid, -.ui.menu > .container { - display: flex; - align-items: inherit; - flex-direction: inherit; -} - -/*-------------- - Inputs ----------------*/ - -.ui.menu .item > .input { - width: 100%; -} - -.ui.menu:not(.vertical) .item > .input { - position: relative; - top: 0; - margin: -0.5em 0; -} - -.ui.menu .item > .input input { - font-size: 1em; - padding-top: 0.57142857em; - padding-bottom: 0.57142857em; -} - -/*-------------- - Header ----------------*/ - -.ui.menu .header.item, -.ui.vertical.menu .header.item { - margin: 0; - background: ''; - text-transform: normal; - font-weight: 500; -} - -.ui.vertical.menu .item > .header:not(.ui) { - margin: 0 0 0.5em; - font-size: 1em; - font-weight: 500; -} - -/*-------------- - Dropdowns ----------------*/ - -/* Dropdown Icon */ - -.ui.menu .item > i.dropdown.icon { - padding: 0; - float: right; - margin: 0 0 0 1em; -} - -/* Menu */ - -.ui.menu .dropdown.item .menu { - min-width: calc(100% - 1px); - border-radius: 0 0 0.28571429rem 0.28571429rem; - background: #FFFFFF; - margin: 0 0 0; - box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.08); - flex-direction: column !important; -} - -/* Menu Items */ - -.ui.menu .ui.dropdown .menu > .item { - margin: 0; - text-align: left; - font-size: 1em !important; - padding: 0.78571429em 1.14285714em !important; - background: transparent !important; - color: rgba(0, 0, 0, 0.87) !important; - text-transform: none !important; - font-weight: normal !important; - box-shadow: none !important; - transition: none !important; -} - -.ui.menu .ui.dropdown .menu > .item:hover { - background: rgba(0, 0, 0, 0.05) !important; - color: rgba(0, 0, 0, 0.95) !important; -} - -.ui.menu .ui.dropdown .menu > .selected.item { - background: rgba(0, 0, 0, 0.05) !important; - color: rgba(0, 0, 0, 0.95) !important; -} - -.ui.menu .ui.dropdown .menu > .active.item { - background: rgba(0, 0, 0, 0.03) !important; - font-weight: 500 !important; - color: rgba(0, 0, 0, 0.95) !important; -} - -.ui.menu .ui.dropdown.item .menu .item:not(.filtered) { - display: block; -} - -.ui.menu .ui.dropdown .menu > .item > .icons, -.ui.menu .ui.dropdown .menu > .item > i.icon:not(.dropdown) { - display: inline-block; - font-size: 1em !important; - float: none; - margin: 0 0.75em 0 0 !important; -} - -/* Secondary */ - -.ui.secondary.menu .dropdown.item > .menu, -.ui.text.menu .dropdown.item > .menu { - border-radius: 0.28571429rem; - margin-top: 0.35714286em; -} - -/* Pointing */ - -.ui.menu .pointing.dropdown.item .menu { - margin-top: 0.75em; -} - -/* Vertical */ - -.ui.vertical.menu .dropdown.item > i.icon { - float: right; - content: "\f0da"; - margin-left: 1em; -} - -.ui.vertical.menu .dropdown.item .menu { - left: 100%; - /* IE needs 0, all others support max-content to show dropdown icon inline, so keep both settings! */ - min-width: 0; - min-width: -moz-max-content; - min-width: max-content; - margin: 0 0 0 0; - box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.08); - border-radius: 0 0.28571429rem 0.28571429rem 0.28571429rem; -} - -.ui.vertical.menu .dropdown.item.upward .menu { - bottom: 0; -} - -.ui.vertical.menu .dropdown.item:not(.upward) .menu { - top: 0; -} - -.ui.vertical.menu .active.dropdown.item { - border-top-right-radius: 0; - border-bottom-right-radius: 0; -} - -.ui.vertical.menu .dropdown.active.item { - box-shadow: none; -} - -/* Evenly Divided */ - -.ui.item.menu .dropdown .menu .item { - width: 100%; -} - -/*-------------- - Labels ----------------*/ - -.ui.menu .item > .label:not(.floating) { - margin-left: 1em; - padding: 0.3em 0.78571429em; -} - -.ui.vertical.menu .item > .label { - margin-top: -0.15em; - margin-bottom: -0.15em; - padding: 0.3em 0.78571429em; -} - -.ui.menu .item > .floating.label { - padding: 0.3em 0.78571429em; -} - -.ui.menu .item > .label { - background: #999999; - color: #FFFFFF; -} - -.ui.menu .item > .image.label img { - margin: -0.2833em 0.8em -0.2833em -0.8em; - height: 1.5666em; -} - -/*-------------- - Images ----------------*/ - -.ui.menu .item > img:not(.ui) { - display: inline-block; - vertical-align: middle; - margin: -0.3em 0; - width: 2.5em; -} - -.ui.vertical.menu .item > img:not(.ui):only-child { - display: block; - max-width: 100%; - width: auto; -} - -/******************************* - Coupling -*******************************/ - -/*-------------- - List ----------------*/ - -/* Menu divider shouldnt apply */ - -.ui.menu .list .item:before { - background: none !important; -} - -/*-------------- - Sidebar - ---------------*/ - -/* Show vertical dividers below last */ - -.ui.vertical.sidebar.menu > .item:first-child:before { - display: block !important; -} - -.ui.vertical.sidebar.menu > .item::before { - top: auto; - bottom: 0; -} - -/*-------------- - Container ----------------*/ - -@media only screen and (max-width: 767.98px) { - .ui.menu > .ui.container { - width: 100% !important; - margin-left: 0 !important; - margin-right: 0 !important; - } -} - -@media only screen and (min-width: 768px) { - .ui.menu:not(.secondary):not(.text):not(.tabular):not(.borderless) > .container > .item:not(.right):not(.borderless):first-child { - border-left: 1px solid rgba(34, 36, 38, 0.1); - } - - .ui.menu:not(.secondary):not(.text):not(.tabular):not(.borderless) > .container > .right.item:not(.borderless):last-child, - .ui.menu:not(.secondary):not(.text):not(.tabular):not(.borderless) > .container > .right.menu > .item:not(.borderless):last-child { - border-right: 1px solid rgba(34, 36, 38, 0.1); - } -} - -/******************************* - States -*******************************/ - -/*-------------- - Hover ----------------*/ - -.ui.link.menu .item:hover, -.ui.menu .dropdown.item:hover, -.ui.menu .link.item:hover, -.ui.menu a.item:hover { - cursor: pointer; - background: rgba(0, 0, 0, 0.03); - color: rgba(0, 0, 0, 0.95); -} - -/*-------------- - Pressed ----------------*/ - -.ui.link.menu .item:active, -.ui.menu .link.item:active, -.ui.menu a.item:active { - background: rgba(0, 0, 0, 0.03); - color: rgba(0, 0, 0, 0.95); -} - -/*-------------- - Active ----------------*/ - -.ui.menu .active.item { - background: rgba(0, 0, 0, 0.05); - color: rgba(0, 0, 0, 0.95); - font-weight: normal; - box-shadow: none; -} - -.ui.menu .active.item > i.icon { - opacity: 1; -} - -/*-------------- - Active Hover ----------------*/ - -.ui.menu .active.item:hover, -.ui.vertical.menu .active.item:hover { - background-color: rgba(0, 0, 0, 0.05); - color: rgba(0, 0, 0, 0.95); -} - -/*-------------- - Disabled ----------------*/ - -.ui.ui.menu .item.disabled { - cursor: default; - background-color: transparent; - color: rgba(40, 40, 40, 0.3); - pointer-events: none; -} - -/******************************* - Types -*******************************/ - -/*------------------ -Floated Menu / Item --------------------*/ - -/* Left Floated */ - -.ui.menu:not(.vertical) .left.item, -.ui.menu:not(.vertical) .left.menu { - display: flex; - margin-right: auto !important; -} - -/* Right Floated */ - -.ui.menu:not(.vertical) .right.item, -.ui.menu:not(.vertical) .right.menu { - display: flex; - margin-left: auto !important; -} - -.ui.menu:not(.vertical) :not(.dropdown) > .left.menu, -.ui.menu:not(.vertical) :not(.dropdown) > .right.menu { - display: inherit; -} - -/* Center */ - -.ui.menu:not(.vertical) .center.item, -.ui.menu:not(.vertical) .center.menu { - display: flex; - margin-left: auto !important; - margin-right: auto !important; -} - -/* Swapped Borders */ - -.ui.menu .right.item::before, -.ui.menu .right.menu > .item::before { - right: auto; - left: 0; -} - -/* Remove Outer Borders */ - -.ui.menu .center.item:last-child::before, -.ui.menu .center.menu > .item:last-child::before { - display: none; -} - -/*-------------- - Vertical - ---------------*/ - -.ui.vertical.menu { - display: block; - flex-direction: column; - background: #FFFFFF; - box-shadow: 0 1px 2px 0 rgba(34, 36, 38, 0.15); -} - -/*--- Item ---*/ - -.ui.vertical.menu .item { - display: block; - background: none; - border-top: none; - border-right: none; -} - -.ui.vertical.menu > .item:first-child { - border-radius: 0.28571429rem 0.28571429rem 0 0; -} - -.ui.vertical.menu > .item:last-child { - border-radius: 0 0 0.28571429rem 0.28571429rem; -} - -/*--- Label ---*/ - -.ui.vertical.menu .item > .label { - float: right; - text-align: center; -} - -/*--- Icon ---*/ - -.ui.vertical.menu .item > i.icon, -.ui.vertical.menu .item > i.icons { - width: 1.18em; - float: right; - margin: 0 0 0 0.5em; -} - -.ui.vertical.menu .item > .label + i.icon { - float: none; - margin: 0 0.5em 0 0; -} - -/*--- Border ---*/ - -.ui.vertical.menu .item:before { - position: absolute; - content: ''; - top: 0; - left: 0; - width: 100%; - height: 1px; - background: rgba(34, 36, 38, 0.1); -} - -.ui.vertical.menu .item:first-child:before { - display: none !important; -} - -/*--- Sub Menu ---*/ - -.ui.vertical.menu .item > .menu { - margin: 0.5em -1.14285714em 0; -} - -.ui.vertical.menu .menu .item { - background: none; - padding: 0.5em 1.33333333em; - font-size: 0.85714286em; - color: rgba(0, 0, 0, 0.5); -} - -.ui.vertical.menu .item .menu a.item:hover, -.ui.vertical.menu .item .menu .link.item:hover { - color: rgba(0, 0, 0, 0.85); -} - -.ui.vertical.menu .menu .item:before { - display: none; -} - -/* Vertical Active */ - -.ui.vertical.menu .active.item { - background: rgba(0, 0, 0, 0.05); - border-radius: 0; - box-shadow: none; -} - -.ui.vertical.menu > .active.item:first-child { - border-radius: 0.28571429rem 0.28571429rem 0 0; -} - -.ui.vertical.menu > .active.item:last-child { - border-radius: 0 0 0.28571429rem 0.28571429rem; -} - -.ui.vertical.menu > .active.item:only-child { - border-radius: 0.28571429rem; -} - -.ui.vertical.menu .active.item .menu .active.item { - border-left: none; -} - -.ui.vertical.menu .item .menu .active.item { - background-color: transparent; - font-weight: 500; - color: rgba(0, 0, 0, 0.95); -} - -/*-------------- - Tabular - ---------------*/ - -.ui.tabular.menu { - border-radius: 0; - box-shadow: none !important; - border: none; - background: none transparent; - border-bottom: 1px solid #D4D4D5; -} - -.ui.tabular.fluid.menu { - width: calc(100% + 2px) !important; -} - -.ui.tabular.menu .item { - background: transparent; - border-bottom: none; - border-left: 1px solid transparent; - border-right: 1px solid transparent; - border-top: 2px solid transparent; - padding: 0.92857143em 1.42857143em; - color: rgba(0, 0, 0, 0.87); -} - -.ui.tabular.menu .item:before { - display: none; -} - -/* Hover */ - -.ui.tabular.menu .item:hover { - background-color: transparent; - color: rgba(0, 0, 0, 0.8); -} - -/* Active */ - -.ui.tabular.menu .active.item { - background: none #FFFFFF; - color: rgba(0, 0, 0, 0.95); - border-top-width: 1px; - border-color: #D4D4D5; - font-weight: 500; - margin-bottom: -1px; - box-shadow: none; - border-radius: 0.28571429rem 0.28571429rem 0 0 !important; -} - -/* Coupling with segment for attachment */ - -.ui.tabular.menu + .attached:not(.top).segment, -.ui.tabular.menu + .attached:not(.top).segment + .attached:not(.top).segment { - border-top: none; - margin-left: 0; - margin-top: 0; - margin-right: 0; - width: 100%; -} - -.top.attached.segment + .ui.bottom.tabular.menu { - position: relative; - width: calc(100% + 2px); - left: -1px; -} - -/* Bottom Vertical Tabular */ - -.ui.bottom.tabular.menu { - background: none transparent; - border-radius: 0; - box-shadow: none !important; - border-bottom: none; - border-top: 1px solid #D4D4D5; -} - -.ui.bottom.tabular.menu .item { - background: none; - border-left: 1px solid transparent; - border-right: 1px solid transparent; - border-bottom: 1px solid transparent; - border-top: none; -} - -.ui.bottom.tabular.menu .active.item { - background: none #FFFFFF; - color: rgba(0, 0, 0, 0.95); - border-color: #D4D4D5; - margin: -1px 0 0 0; - border-radius: 0 0 0.28571429rem 0.28571429rem !important; -} - -/* Vertical Tabular (Left) */ - -.ui.vertical.tabular.menu { - background: none transparent; - border-radius: 0; - box-shadow: none !important; - border-bottom: none; - border-right: 1px solid #D4D4D5; -} - -.ui.vertical.tabular.menu .item { - background: none; - border-left: 1px solid transparent; - border-bottom: 1px solid transparent; - border-top: 1px solid transparent; - border-right: none; -} - -.ui.vertical.tabular.menu .active.item { - background: none #FFFFFF; - color: rgba(0, 0, 0, 0.95); - border-color: #D4D4D5; - margin: 0 -1px 0 0; - border-radius: 0.28571429rem 0 0 0.28571429rem !important; -} - -/* Vertical Right Tabular */ - -.ui.vertical.right.tabular.menu { - background: none transparent; - border-radius: 0; - box-shadow: none !important; - border-bottom: none; - border-right: none; - border-left: 1px solid #D4D4D5; -} - -.ui.vertical.right.tabular.menu .item { - background: none; - border-right: 1px solid transparent; - border-bottom: 1px solid transparent; - border-top: 1px solid transparent; - border-left: none; -} - -.ui.vertical.right.tabular.menu .active.item { - background: none #FFFFFF; - color: rgba(0, 0, 0, 0.95); - border-color: #D4D4D5; - margin: 0 0 0 -1px; - border-radius: 0 0.28571429rem 0.28571429rem 0 !important; -} - -/* Dropdown */ - -.ui.tabular.menu .active.dropdown.item { - margin-bottom: 0; - border-left: 1px solid transparent; - border-right: 1px solid transparent; - border-top: 2px solid transparent; - border-bottom: none; -} - -/*-------------- - Pagination - ---------------*/ - -.ui.pagination.menu { - margin: 0; - display: inline-flex; - vertical-align: middle; -} - -.ui.pagination.menu .item:last-child { - border-radius: 0 0.28571429rem 0.28571429rem 0; -} - -.ui.compact.menu .item:last-child { - border-radius: 0 0.28571429rem 0.28571429rem 0; -} - -.ui.pagination.menu .item:last-child:before { - display: none; -} - -.ui.pagination.menu .item { - min-width: 3em; - text-align: center; -} - -.ui.pagination.menu .icon.item i.icon { - vertical-align: top; -} - -/* Active */ - -.ui.pagination.menu .active.item { - border-top: none; - padding-top: 0.92857143em; - background-color: rgba(0, 0, 0, 0.05); - color: rgba(0, 0, 0, 0.95); - box-shadow: none; -} - -/*-------------- - Secondary - ---------------*/ - -.ui.secondary.menu { - background: none; - margin-left: -0.35714286em; - margin-right: -0.35714286em; - border-radius: 0; - border: none; - box-shadow: none; -} - -/* Item */ - -.ui.secondary.menu .item { - align-self: center; - box-shadow: none; - border: none; - padding: 0.78571429em 0.92857143em; - margin: 0 0.35714286em; - background: none; - transition: color 0.1s ease; - border-radius: 0.28571429rem; -} - -/* No Divider */ - -.ui.secondary.menu .item:before { - display: none !important; -} - -/* Header */ - -.ui.secondary.menu .header.item { - border-radius: 0; - border-right: none; - background: none transparent; -} - -/* Image */ - -.ui.secondary.menu .item > img:not(.ui) { - margin: 0; -} - -/* Hover */ - -.ui.secondary.menu .dropdown.item:hover, -.ui.secondary.menu .link.item:hover, -.ui.secondary.menu a.item:hover { - background: rgba(0, 0, 0, 0.05); - color: rgba(0, 0, 0, 0.95); -} - -/* Active */ - -.ui.secondary.menu .active.item { - box-shadow: none; - background: rgba(0, 0, 0, 0.05); - color: rgba(0, 0, 0, 0.95); - border-radius: 0.28571429rem; -} - -/* Active Hover */ - -.ui.secondary.menu .active.item:hover { - box-shadow: none; - background: rgba(0, 0, 0, 0.05); - color: rgba(0, 0, 0, 0.95); -} - -/* Fix item margins */ - -.ui.secondary.item.menu { - margin-left: 0; - margin-right: 0; -} - -.ui.secondary.item.menu .item:last-child { - margin-right: 0; -} - -.ui.secondary.attached.menu { - box-shadow: none; -} - -/*--------------------- - Secondary Vertical - -----------------------*/ - -/* Sub Menu */ - -.ui.vertical.secondary.menu .item:not(.dropdown) > .menu { - margin: 0 -0.92857143em; -} - -.ui.vertical.secondary.menu .item:not(.dropdown) > .menu > .item { - margin: 0; - padding: 0.5em 1.33333333em; -} - -.ui.secondary.vertical.menu > .item { - border: none; - margin: 0 0 0.35714286em; - border-radius: 0.28571429rem !important; -} - -.ui.secondary.vertical.menu > .header.item { - border-radius: 0; -} - -/* Sub Menu */ - -.ui.vertical.secondary.menu .item > .menu .item { - background-color: transparent; -} - -/* Inverted */ - -.ui.secondary.inverted.menu { - background-color: transparent; -} - -/*--------------------- - Secondary Pointing - -----------------------*/ - -.ui.secondary.pointing.menu { - margin-left: 0; - margin-right: 0; - border-bottom: 2px solid rgba(34, 36, 38, 0.15); -} - -.ui.secondary.pointing.menu .item { - border-bottom-color: transparent; - border-bottom-style: solid; - border-radius: 0; - align-self: flex-end; - margin: 0 0 -2px; - padding: 0.85714286em 1.14285714em; - border-bottom-width: 2px; - transition: color 0.1s ease; -} - -.ui.secondary.pointing.menu .ui.dropdown .menu .item { - border-bottom-width: 0; -} - -.ui.secondary.pointing.menu .item > .label:not(.floating) { - margin-top: -0.3em; - margin-bottom: -0.3em; -} - -.ui.secondary.pointing.menu .item > .circular.label { - margin-top: -0.5em; - margin-bottom: -0.5em; -} - -/* Item Types */ - -.ui.secondary.pointing.menu .header.item { - color: rgba(0, 0, 0, 0.85) !important; -} - -.ui.secondary.pointing.menu .text.item { - box-shadow: none !important; -} - -.ui.secondary.pointing.menu .item:after { - display: none; -} - -/* Hover */ - -.ui.secondary.pointing.menu .dropdown.item:hover, -.ui.secondary.pointing.menu .link.item:hover, -.ui.secondary.pointing.menu a.item:hover { - background-color: transparent; - color: rgba(0, 0, 0, 0.87); -} - -/* Pressed */ - -.ui.secondary.pointing.menu .dropdown.item:active, -.ui.secondary.pointing.menu .link.item:active, -.ui.secondary.pointing.menu a.item:active { - background-color: transparent; - border-color: rgba(34, 36, 38, 0.15); -} - -/* Active */ - -.ui.secondary.pointing.menu .active.item { - background-color: transparent; - box-shadow: none; - border-color: currentColor; - font-weight: 500; - color: rgba(0, 0, 0, 0.95); -} - -/* Active Hover */ - -.ui.secondary.pointing.menu .active.item:hover { - border-color: currentColor; - color: rgba(0, 0, 0, 0.95); -} - -/* Active Dropdown */ - -.ui.secondary.pointing.menu .active.dropdown.item { - border-color: transparent; -} - -/* Vertical Pointing */ - -.ui.secondary.vertical.pointing.menu { - border-bottom-width: 0; - border-right-width: 2px; - border-right-style: solid; - border-right-color: rgba(34, 36, 38, 0.15); -} - -.ui.secondary.vertical.pointing.menu .item { - border-bottom: none; - border-right-style: solid; - border-right-color: transparent; - border-radius: 0 !important; - margin: 0 -2px 0 0; - border-right-width: 2px; -} - -/* Vertical Active */ - -.ui.secondary.vertical.pointing.menu .active.item { - border-color: currentColor; -} - -/*-------------- - Text Menu - ---------------*/ - -.ui.text.menu { - background: none transparent; - border-radius: 0; - box-shadow: none; - border: none; - margin: 1em -0.5em; -} - -.ui.text.menu .item { - border-radius: 0; - box-shadow: none; - align-self: center; - margin: 0 0; - padding: 0.35714286em 0.5em; - font-weight: normal; - color: rgba(0, 0, 0, 0.6); - transition: opacity 0.1s ease; -} - -/* Border */ - -.ui.text.menu .item:before, -.ui.text.menu .menu .item:before { - display: none !important; -} - -/* Header */ - -.ui.text.menu .header.item { - background-color: transparent; - opacity: 1; - color: rgba(0, 0, 0, 0.85); - font-size: 0.92857143em; - text-transform: uppercase; - font-weight: 500; -} - -/* Image */ - -.ui.text.menu .item > img:not(.ui) { - margin: 0; -} - -/*--- fluid text ---*/ - -.ui.text.item.menu .item { - margin: 0; -} - -/*--- vertical text ---*/ - -.ui.vertical.text.menu { - margin: 1em 0; -} - -.ui.vertical.text.menu:first-child { - margin-top: 0; -} - -.ui.vertical.text.menu:last-child { - margin-bottom: 0; -} - -.ui.vertical.text.menu .item { - margin: 0.57142857em 0; - padding-left: 0; - padding-right: 0; -} - -.ui.vertical.text.menu .item > i.icon { - float: none; - margin: 0 0.35714286em 0 0; -} - -.ui.vertical.text.menu .header.item { - margin: 0.57142857em 0 0.71428571em; -} - -/* Vertical Sub Menu */ - -.ui.vertical.text.menu .item:not(.dropdown) > .menu { - margin: 0; -} - -.ui.vertical.text.menu .item:not(.dropdown) > .menu > .item { - margin: 0; - padding: 0.5em 0; -} - -/*--- hover ---*/ - -.ui.text.menu .item:hover { - opacity: 1; - background-color: transparent; -} - -/*--- active ---*/ - -.ui.text.menu .active.item { - background-color: transparent; - border: none; - box-shadow: none; - font-weight: normal; - color: rgba(0, 0, 0, 0.95); -} - -/*--- active hover ---*/ - -.ui.text.menu .active.item:hover { - background-color: transparent; -} - -/* Disable Bariations */ - -.ui.text.pointing.menu .active.item:after { - box-shadow: none; -} - -.ui.text.attached.menu { - box-shadow: none; -} - -/* Fluid */ - -.ui.fluid.text.menu { - margin-left: 0; - margin-right: 0; -} - -/*-------------- - Icon Only ----------------*/ - -/* Vertical Menu */ - -.ui.vertical.icon.menu { - display: inline-block; - width: auto; -} - -/* Item */ - -.ui.icon.menu .item { - height: auto; - text-align: center; - color: #1B1C1D; -} - -/* Icon */ - -.ui.icon.menu .item > i.icon:not(.dropdown) { - margin: 0; - opacity: 1; -} - -/* Icon Gylph */ - -.ui.icon.menu i.icon:before { - opacity: 1; -} - -/* (x) Item Icon */ - -.ui.menu .icon.item > i.icon { - width: auto; - margin: 0 auto; -} - -/* Vertical Icon */ - -.ui.vertical.icon.menu .item > i.icon:not(.dropdown) { - display: block; - opacity: 1; - margin: 0 auto; - float: none; -} - -/* Inverted */ - -.ui.inverted.icon.menu .item { - color: #FFFFFF; -} - -/*-------------- - Labeled Icon - ---------------*/ - -/* Menu */ - -.ui.labeled.icon.menu { - text-align: center; -} - -/* Item */ - -.ui.labeled.icon.menu .item { - min-width: 6em; - flex-direction: column; -} - -/* Icon */ - -.ui.labeled.icon.menu > .item > i.icon:not(.dropdown) { - height: 1em; - display: block; - font-size: 1.71428571em !important; - margin: 0 auto 0.5rem !important; -} - -/* Fluid */ - -.ui.fluid.labeled.icon.menu > .item { - min-width: 0; -} - -/******************************* - Variations -*******************************/ - -/*-------------- - Stackable - ---------------*/ - -@media only screen and (max-width: 767.98px) { - .ui.stackable.menu { - flex-direction: column; - } - - .ui.stackable.menu .item { - width: 100% !important; - } - - .ui.stackable.menu .item:before { - position: absolute; - content: ''; - top: auto; - bottom: 0; - left: 0; - width: 100%; - height: 1px; - background: rgba(34, 36, 38, 0.1); - } - - .ui.stackable.menu .left.menu, - .ui.stackable.menu .left.item { - margin-right: 0 !important; - } - - .ui.stackable.menu .right.menu, - .ui.stackable.menu .right.item { - margin-left: 0 !important; - } - - .ui.stackable.menu .center.menu, - .ui.stackable.menu .center.item { - margin-left: 0 !important; - margin-right: 0 !important; - } - - .ui.stackable.menu .right.menu, - .ui.stackable.menu .center.menu, - .ui.stackable.menu .left.menu { - flex-direction: column; - } -} - -/*-------------- - Colors ----------------*/ - -.ui.ui.ui.menu .primary.active.item, -.ui.ui.primary.menu .active.item:hover, -.ui.ui.primary.menu .active.item { - color: #2185D0; -} - -.ui.ui.ui.menu .red.active.item, -.ui.ui.red.menu .active.item:hover, -.ui.ui.red.menu .active.item { - color: #DB2828; -} - -.ui.ui.ui.menu .orange.active.item, -.ui.ui.orange.menu .active.item:hover, -.ui.ui.orange.menu .active.item { - color: #F2711C; -} - -.ui.ui.ui.menu .yellow.active.item, -.ui.ui.yellow.menu .active.item:hover, -.ui.ui.yellow.menu .active.item { - color: #FBBD08; -} - -.ui.ui.ui.menu .olive.active.item, -.ui.ui.olive.menu .active.item:hover, -.ui.ui.olive.menu .active.item { - color: #B5CC18; -} - -.ui.ui.ui.menu .green.active.item, -.ui.ui.green.menu .active.item:hover, -.ui.ui.green.menu .active.item { - color: #21BA45; -} - -.ui.ui.ui.menu .teal.active.item, -.ui.ui.teal.menu .active.item:hover, -.ui.ui.teal.menu .active.item { - color: #00B5AD; -} - -.ui.ui.ui.menu .blue.active.item, -.ui.ui.blue.menu .active.item:hover, -.ui.ui.blue.menu .active.item { - color: #2185D0; -} - -.ui.ui.ui.menu .violet.active.item, -.ui.ui.violet.menu .active.item:hover, -.ui.ui.violet.menu .active.item { - color: #6435C9; -} - -.ui.ui.ui.menu .purple.active.item, -.ui.ui.purple.menu .active.item:hover, -.ui.ui.purple.menu .active.item { - color: #A333C8; -} - -.ui.ui.ui.menu .pink.active.item, -.ui.ui.pink.menu .active.item:hover, -.ui.ui.pink.menu .active.item { - color: #E03997; -} - -.ui.ui.ui.menu .brown.active.item, -.ui.ui.brown.menu .active.item:hover, -.ui.ui.brown.menu .active.item { - color: #A5673F; -} - -.ui.ui.ui.menu .grey.active.item, -.ui.ui.grey.menu .active.item:hover, -.ui.ui.grey.menu .active.item { - color: #767676; -} - -.ui.ui.ui.menu .black.active.item, -.ui.ui.black.menu .active.item:hover, -.ui.ui.black.menu .active.item { - color: #1B1C1D; -} - -/*-------------- - Floated - ---------------*/ - -.ui.floated.menu { - float: left; - margin: 0 0.5rem 0 0; -} - -.ui.floated.menu .item:last-child:before { - display: none; -} - -.ui.right.floated.menu { - float: right; - margin: 0 0 0 0.5rem; -} - -/*-------------- - Fitted - ---------------*/ - -.ui.fitted.menu .item, -.ui.fitted.menu .item .menu .item, -.ui.menu .fitted.item { - padding: 0; -} - -.ui.horizontally.fitted.menu .item, -.ui.horizontally.fitted.menu .item .menu .item, -.ui.menu .horizontally.fitted.item { - padding-top: 0.92857143em; - padding-bottom: 0.92857143em; -} - -.ui.vertically.fitted.menu .item, -.ui.vertically.fitted.menu .item .menu .item, -.ui.menu .vertically.fitted.item { - padding-left: 1.14285714em; - padding-right: 1.14285714em; -} - -/*-------------- - Borderless - ---------------*/ - -.ui.borderless.menu .item:before, -.ui.borderless.menu .item .menu .item:before, -.ui.menu .borderless.item:before { - background: none !important; -} - -/*------------------- - Compact - --------------------*/ - -.ui.compact.menu { - display: inline-flex; - margin: 0; - vertical-align: middle; -} - -.ui.compact.vertical.menu { - /* IE hack to make dropdown icons appear inline */ - display: -ms-inline-flexbox !important; - display: inline-block; -} - -.ui.compact.menu:not(.secondary) .item:last-child { - border-radius: 0 0.28571429rem 0.28571429rem 0; -} - -.ui.compact.menu .item:last-child:before { - display: none; -} - -.ui.compact.vertical.menu { - width: auto !important; -} - -.ui.compact.vertical.menu .item:last-child::before { - display: block; -} - -/*------------------- - Fluid - --------------------*/ - -.ui.menu.fluid, -.ui.vertical.menu.fluid { - width: 100% !important; -} - -/*------------------- - Evenly Sized ---------------------*/ - -.ui.item.menu, -.ui.item.menu .item { - width: 100%; - padding-left: 0 !important; - padding-right: 0 !important; - margin-left: 0 !important; - margin-right: 0 !important; - text-align: center; - justify-content: center; -} - -.ui.attached.item.menu:not(.tabular) { - margin: 0 -1px !important; -} - -.ui.item.menu .item:last-child:before { - display: none; -} - -.ui.menu.two.item .item { - width: 50%; -} - -.ui.menu.three.item .item { - width: 33.333%; -} - -.ui.menu.four.item .item { - width: 25%; -} - -.ui.menu.five.item .item { - width: 20%; -} - -.ui.menu.six.item .item { - width: 16.666%; -} - -.ui.menu.seven.item .item { - width: 14.285%; -} - -.ui.menu.eight.item .item { - width: 12.5%; -} - -.ui.menu.nine.item .item { - width: 11.11%; -} - -.ui.menu.ten.item .item { - width: 10%; -} - -.ui.menu.eleven.item .item { - width: 9.09%; -} - -.ui.menu.twelve.item .item { - width: 8.333%; -} - -/*-------------- - Fixed - ---------------*/ - -.ui.menu.fixed { - position: fixed; - z-index: 101; - margin: 0; - width: 100%; -} - -.ui.menu.fixed, -.ui.menu.fixed .item:first-child, -.ui.menu.fixed .item:last-child { - border-radius: 0 !important; -} - -.ui.fixed.menu, -.ui[class*="top fixed"].menu { - top: 0; - left: 0; - right: auto; - bottom: auto; -} - -.ui[class*="top fixed"].menu { - border-top: none; - border-left: none; - border-right: none; -} - -.ui[class*="right fixed"].menu { - border-top: none; - border-bottom: none; - border-right: none; - top: 0; - right: 0; - left: auto; - bottom: auto; - width: auto; - height: 100%; -} - -.ui[class*="bottom fixed"].menu { - border-bottom: none; - border-left: none; - border-right: none; - bottom: 0; - left: 0; - top: auto; - right: auto; -} - -.ui[class*="left fixed"].menu { - border-top: none; - border-bottom: none; - border-left: none; - top: 0; - left: 0; - right: auto; - bottom: auto; - width: auto; - height: 100%; -} - -/* Coupling with Grid */ - -.ui.fixed.menu + .ui.grid { - padding-top: 2.75rem; -} - -/*------------------- - Pointing - --------------------*/ - -.ui.pointing.menu .item:after { - visibility: hidden; - position: absolute; - content: ''; - top: 100%; - left: 50%; - transform: translateX(-50%) translateY(-50%) rotate(45deg); - background: none; - margin: 0.5px 0 0; - width: 0.57142857em; - height: 0.57142857em; - border: none; - border-bottom: 1px solid #D4D4D5; - border-right: 1px solid #D4D4D5; - z-index: 2; - transition: background 0.1s ease; -} - -.ui.vertical.pointing.menu .item:after { - position: absolute; - top: 50%; - right: 0; - bottom: auto; - left: auto; - transform: translateX(50%) translateY(-50%) rotate(45deg); - margin: 0 -0.5px 0 0; - border: none; - border-top: 1px solid #D4D4D5; - border-right: 1px solid #D4D4D5; -} - -.ui.pointing.menu .ui.dropdown .menu .item:after, -.ui.vertical.pointing.menu .ui.dropdown .menu .item:after { - display: none; -} - -/* Active */ - -.ui.pointing.menu .active.item:after { - visibility: visible; -} - -.ui.pointing.menu .active.dropdown.item:after { - visibility: hidden; -} - -/* Don't double up pointers */ - -.ui.pointing.menu .dropdown.active.item:after, -.ui.pointing.menu .active.item .menu .active.item:after { - display: none; -} - -/* Colors */ - -.ui.pointing.menu .active.item:hover:after { - background-color: #F2F2F2; -} - -.ui.pointing.menu .active.item:after { - background-color: #F2F2F2; -} - -.ui.pointing.menu .active.item:hover:after { - background-color: #F2F2F2; -} - -.ui.vertical.pointing.menu .active.item:hover:after { - background-color: #F2F2F2; -} - -.ui.vertical.pointing.menu .active.item:after { - background-color: #F2F2F2; -} - -.ui.vertical.pointing.menu .menu .active.item:after { - background-color: #FFFFFF; -} - -.ui.inverted.pointing.menu .primary.active.item:after { - background-color: #2185D0; -} - -.ui.inverted.pointing.menu .secondary.active.item:after { - background-color: #1B1C1D; -} - -.ui.inverted.pointing.menu .red.active.item:after { - background-color: #DB2828; -} - -.ui.inverted.pointing.menu .orange.active.item:after { - background-color: #F2711C; -} - -.ui.inverted.pointing.menu .yellow.active.item:after { - background-color: #FBBD08; -} - -.ui.inverted.pointing.menu .olive.active.item:after { - background-color: #B5CC18; -} - -.ui.inverted.pointing.menu .green.active.item:after { - background-color: #21BA45; -} - -.ui.inverted.pointing.menu .teal.active.item:after { - background-color: #00B5AD; -} - -.ui.inverted.pointing.menu .blue.active.item:after { - background-color: #2185D0; -} - -.ui.inverted.pointing.menu .violet.active.item:after { - background-color: #6435C9; -} - -.ui.inverted.pointing.menu .purple.active.item:after { - background-color: #A333C8; -} - -.ui.inverted.pointing.menu .pink.active.item:after { - background-color: #E03997; -} - -.ui.inverted.pointing.menu .brown.active.item:after { - background-color: #A5673F; -} - -.ui.inverted.pointing.menu .grey.active.item:after { - background-color: #767676; -} - -.ui.inverted.pointing.menu .black.active.item:after { - background-color: #1B1C1D; -} - -/*-------------- - Attached - ---------------*/ - -/* Middle */ - -.ui.attached.menu { - top: 0; - bottom: 0; - border-radius: 0; - margin: 0 -1px; - width: calc(100% + 2px); - max-width: calc(100% + 2px); - box-shadow: none; -} - -.ui.attached + .ui.attached.menu:not(.top) { - border-top: none; -} - -/* Top */ - -.ui[class*="top attached"].menu { - bottom: 0; - margin-bottom: 0; - top: 0; - margin-top: 1rem; - border-radius: 0.28571429rem 0.28571429rem 0 0; -} - -.ui.menu[class*="top attached"]:first-child { - margin-top: 0; -} - -/* Bottom */ - -.ui[class*="bottom attached"].menu { - bottom: 0; - margin-top: 0; - top: 0; - margin-bottom: 1rem; - box-shadow: 0 1px 2px 0 rgba(34, 36, 38, 0.15), none; - border-radius: 0 0 0.28571429rem 0.28571429rem; -} - -.ui[class*="bottom attached"].menu:last-child { - margin-bottom: 0; -} - -/* Attached Menu Item */ - -.ui.top.attached.menu > .item:first-child { - border-radius: 0.28571429rem 0 0 0; -} - -.ui.bottom.attached.menu > .item:first-child { - border-radius: 0 0 0 0.28571429rem; -} - -/* Tabular Attached */ - -.ui.attached.menu:not(.tabular) { - border: 1px solid #D4D4D5; -} - -.ui.attached.tabular.menu { - margin-left: 0; - margin-right: 0; - width: 100%; -} - -/*-------------- - Sizes ----------------*/ - -.ui.menu { - font-size: 1rem; -} - -.ui.vertical.menu { - width: 15rem; -} - -.ui.mini.menu, -.ui.mini.menu .dropdown, -.ui.mini.menu .dropdown .menu > .item { - font-size: 0.78571429rem; -} - -.ui.mini.vertical.menu:not(.icon) { - width: 9rem; -} - -.ui.tiny.menu, -.ui.tiny.menu .dropdown, -.ui.tiny.menu .dropdown .menu > .item { - font-size: 0.85714286rem; -} - -.ui.tiny.vertical.menu:not(.icon) { - width: 11rem; -} - -.ui.small.menu, -.ui.small.menu .dropdown, -.ui.small.menu .dropdown .menu > .item { - font-size: 0.92857143rem; -} - -.ui.small.vertical.menu:not(.icon) { - width: 13rem; -} - -.ui.large.menu, -.ui.large.menu .dropdown, -.ui.large.menu .dropdown .menu > .item { - font-size: 1.07142857rem; -} - -.ui.large.vertical.menu:not(.icon) { - width: 18rem; -} - -.ui.big.menu, -.ui.big.menu .dropdown, -.ui.big.menu .dropdown .menu > .item { - font-size: 1.14285714rem; -} - -.ui.big.vertical.menu:not(.icon) { - width: 20rem; -} - -.ui.huge.menu, -.ui.huge.menu .dropdown, -.ui.huge.menu .dropdown .menu > .item { - font-size: 1.21428571rem; -} - -.ui.huge.vertical.menu:not(.icon) { - width: 22rem; -} - -.ui.massive.menu, -.ui.massive.menu .dropdown, -.ui.massive.menu .dropdown .menu > .item { - font-size: 1.28571429rem; -} - -.ui.massive.vertical.menu:not(.icon) { - width: 25rem; -} - -/*------------------- - Inverted dropdowns ---------------------*/ - -.ui.menu .ui.inverted.inverted.dropdown.item .menu { - background: #1B1C1D; - box-shadow: none; -} - -.ui.menu .ui.inverted.dropdown .menu > .item { - color: rgba(255, 255, 255, 0.8) !important; -} - -.ui.menu .ui.inverted.dropdown .menu > .active.item { - background: transparent !important; - color: rgba(255, 255, 255, 0.8) !important; -} - -.ui.menu .ui.inverted.dropdown .menu > .item:hover { - background: rgba(255, 255, 255, 0.08) !important; - color: rgba(255, 255, 255, 0.8) !important; -} - -.ui.menu .ui.inverted.dropdown .menu > .selected.item { - background: rgba(255, 255, 255, 0.15) !important; - color: rgba(255, 255, 255, 0.8) !important; -} - -/* Vertical */ - -.ui.vertical.menu .inverted.dropdown.item .menu { - box-shadow: none; -} - -/******************************* - Theme Overrides -*******************************/ - /******************************* Site Overrides *******************************/ diff --git a/web_src/fomantic/semantic.json b/web_src/fomantic/semantic.json index 5db57bc8d40e..5b3a480d5343 100644 --- a/web_src/fomantic/semantic.json +++ b/web_src/fomantic/semantic.json @@ -26,7 +26,6 @@ "dimmer", "dropdown", "form", - "menu", "modal", "search", "tab" From e20428d8f64697d7c6418b42d0a8e57d1d3f588c Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sun, 14 Apr 2024 20:16:54 +0800 Subject: [PATCH 33/35] Fix commitstatus summary (#30431) The target_url is necessary for the UI, but missed in commit_status_summary table. This PR fix it. --------- Co-authored-by: silverwind Co-authored-by: delvh Co-authored-by: Giteabot --- models/git/commit_status_summary.go | 32 ++++++++++++++++------------- models/migrations/migrations.go | 2 ++ models/migrations/v1_23/v296.go | 16 +++++++++++++++ 3 files changed, 36 insertions(+), 14 deletions(-) create mode 100644 models/migrations/v1_23/v296.go diff --git a/models/git/commit_status_summary.go b/models/git/commit_status_summary.go index 01674e943d04..7603e7aa6541 100644 --- a/models/git/commit_status_summary.go +++ b/models/git/commit_status_summary.go @@ -15,10 +15,11 @@ import ( // CommitStatusSummary holds the latest commit Status of a single Commit type CommitStatusSummary struct { - ID int64 `xorm:"pk autoincr"` - RepoID int64 `xorm:"INDEX UNIQUE(repo_id_sha)"` - SHA string `xorm:"VARCHAR(64) NOT NULL INDEX UNIQUE(repo_id_sha)"` - State api.CommitStatusState `xorm:"VARCHAR(7) NOT NULL"` + ID int64 `xorm:"pk autoincr"` + RepoID int64 `xorm:"INDEX UNIQUE(repo_id_sha)"` + SHA string `xorm:"VARCHAR(64) NOT NULL INDEX UNIQUE(repo_id_sha)"` + State api.CommitStatusState `xorm:"VARCHAR(7) NOT NULL"` + TargetURL string `xorm:"TEXT"` } func init() { @@ -44,9 +45,10 @@ func GetLatestCommitStatusForRepoAndSHAs(ctx context.Context, repoSHAs []RepoSHA commitStatuses := make([]*CommitStatus, 0, len(repoSHAs)) for _, summary := range summaries { commitStatuses = append(commitStatuses, &CommitStatus{ - RepoID: summary.RepoID, - SHA: summary.SHA, - State: summary.State, + RepoID: summary.RepoID, + SHA: summary.SHA, + State: summary.State, + TargetURL: summary.TargetURL, }) } return commitStatuses, nil @@ -61,22 +63,24 @@ func UpdateCommitStatusSummary(ctx context.Context, repoID int64, sha string) er // mysql will return 0 when update a record which state hasn't been changed which behaviour is different from other database, // so we need to use insert in on duplicate if setting.Database.Type.IsMySQL() { - _, err := db.GetEngine(ctx).Exec("INSERT INTO commit_status_summary (repo_id,sha,state) VALUES (?,?,?) ON DUPLICATE KEY UPDATE state=?", - repoID, sha, state.State, state.State) + _, err := db.GetEngine(ctx).Exec("INSERT INTO commit_status_summary (repo_id,sha,state,target_url) VALUES (?,?,?,?) ON DUPLICATE KEY UPDATE state=?", + repoID, sha, state.State, state.TargetURL, state.State) return err } if cnt, err := db.GetEngine(ctx).Where("repo_id=? AND sha=?", repoID, sha). - Cols("state"). + Cols("state, target_url"). Update(&CommitStatusSummary{ - State: state.State, + State: state.State, + TargetURL: state.TargetURL, }); err != nil { return err } else if cnt == 0 { _, err = db.GetEngine(ctx).Insert(&CommitStatusSummary{ - RepoID: repoID, - SHA: sha, - State: state.State, + RepoID: repoID, + SHA: sha, + State: state.State, + TargetURL: state.TargetURL, }) return err } diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go index 3ea8f2acbf26..5326d48f901b 100644 --- a/models/migrations/migrations.go +++ b/models/migrations/migrations.go @@ -580,6 +580,8 @@ var migrations = []Migration{ NewMigration("Add unique index for project issue table", v1_23.AddUniqueIndexForProjectIssue), // v295 -> v296 NewMigration("Add commit status summary table", v1_23.AddCommitStatusSummary), + // v296 -> v297 + NewMigration("Add missing field of commit status summary table", v1_23.AddCommitStatusSummary2), } // GetCurrentDBVersion returns the current db version diff --git a/models/migrations/v1_23/v296.go b/models/migrations/v1_23/v296.go new file mode 100644 index 000000000000..495ae2ab23d1 --- /dev/null +++ b/models/migrations/v1_23/v296.go @@ -0,0 +1,16 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package v1_23 //nolint + +import "xorm.io/xorm" + +func AddCommitStatusSummary2(x *xorm.Engine) error { + type CommitStatusSummary struct { + ID int64 `xorm:"pk autoincr"` + TargetURL string `xorm:"TEXT"` + } + // there is no migrations because if there is no data on this table, it will fall back to get data + // from commit status + return x.Sync(new(CommitStatusSummary)) +} From 4c6e2da088cf092a9790df5c84b7b338508fede7 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Mon, 15 Apr 2024 01:22:14 +0800 Subject: [PATCH 34/35] Improve "must-change-password" logic and document (#30472) Unify the behaviors of "user create" and "user change-password". Co-authored-by: KN4CK3R --- cmd/admin_user_change_password.go | 14 +++---- cmd/admin_user_create.go | 38 +++++++++++-------- .../administration/command-line.en-us.md | 5 +-- models/db/engine.go | 4 +- 4 files changed, 31 insertions(+), 30 deletions(-) diff --git a/cmd/admin_user_change_password.go b/cmd/admin_user_change_password.go index 824d66d11259..bd9063a8e4b8 100644 --- a/cmd/admin_user_change_password.go +++ b/cmd/admin_user_change_password.go @@ -36,6 +36,7 @@ var microcmdUserChangePassword = &cli.Command{ &cli.BoolFlag{ Name: "must-change-password", Usage: "User must change password", + Value: true, }, }, } @@ -57,23 +58,18 @@ func runChangePassword(c *cli.Context) error { return err } - var mustChangePassword optional.Option[bool] - if c.IsSet("must-change-password") { - mustChangePassword = optional.Some(c.Bool("must-change-password")) - } - opts := &user_service.UpdateAuthOptions{ Password: optional.Some(c.String("password")), - MustChangePassword: mustChangePassword, + MustChangePassword: optional.Some(c.Bool("must-change-password")), } if err := user_service.UpdateAuth(ctx, user, opts); err != nil { switch { case errors.Is(err, password.ErrMinLength): - return fmt.Errorf("Password is not long enough. Needs to be at least %d", setting.MinPasswordLength) + return fmt.Errorf("password is not long enough, needs to be at least %d characters", setting.MinPasswordLength) case errors.Is(err, password.ErrComplexity): - return errors.New("Password does not meet complexity requirements") + return errors.New("password does not meet complexity requirements") case errors.Is(err, password.ErrIsPwned): - return errors.New("The password you chose is on a list of stolen passwords previously exposed in public data breaches. Please try again with a different password.\nFor more details, see https://haveibeenpwned.com/Passwords") + return errors.New("the password is in a list of stolen passwords previously exposed in public data breaches, please try again with a different password, to see more details: https://haveibeenpwned.com/Passwords") default: return err } diff --git a/cmd/admin_user_create.go b/cmd/admin_user_create.go index a257ce21c8df..403e3ee8d82d 100644 --- a/cmd/admin_user_create.go +++ b/cmd/admin_user_create.go @@ -8,6 +8,7 @@ import ( "fmt" auth_model "code.gitea.io/gitea/models/auth" + "code.gitea.io/gitea/models/db" user_model "code.gitea.io/gitea/models/user" pwd "code.gitea.io/gitea/modules/auth/password" "code.gitea.io/gitea/modules/optional" @@ -46,8 +47,9 @@ var microcmdUserCreate = &cli.Command{ Usage: "Generate a random password for the user", }, &cli.BoolFlag{ - Name: "must-change-password", - Usage: "Set this option to false to prevent forcing the user to change their password after initial login, (Default: true)", + Name: "must-change-password", + Usage: "Set to false to prevent forcing the user to change their password after initial login", + DisableDefaultText: true, }, &cli.IntFlag{ Name: "random-password-length", @@ -71,10 +73,10 @@ func runCreateUser(c *cli.Context) error { } if c.IsSet("name") && c.IsSet("username") { - return errors.New("Cannot set both --name and --username flags") + return errors.New("cannot set both --name and --username flags") } if !c.IsSet("name") && !c.IsSet("username") { - return errors.New("One of --name or --username flags must be set") + return errors.New("one of --name or --username flags must be set") } if c.IsSet("password") && c.IsSet("random-password") { @@ -110,17 +112,21 @@ func runCreateUser(c *cli.Context) error { return errors.New("must set either password or random-password flag") } - // always default to true - changePassword := true - - // If this is the first user being created. - // Take it as the admin and don't force a password update. - if n := user_model.CountUsers(ctx, nil); n == 0 { - changePassword = false - } - + isAdmin := c.Bool("admin") + mustChangePassword := true // always default to true if c.IsSet("must-change-password") { - changePassword = c.Bool("must-change-password") + // if the flag is set, use the value provided by the user + mustChangePassword = c.Bool("must-change-password") + } else { + // check whether there are users in the database + hasUserRecord, err := db.IsTableNotEmpty(&user_model.User{}) + if err != nil { + return fmt.Errorf("IsTableNotEmpty: %w", err) + } + if !hasUserRecord && isAdmin { + // if this is the first admin being created, don't force to change password (keep the old behavior) + mustChangePassword = false + } } restricted := optional.None[bool]() @@ -136,8 +142,8 @@ func runCreateUser(c *cli.Context) error { Name: username, Email: c.String("email"), Passwd: password, - IsAdmin: c.Bool("admin"), - MustChangePassword: changePassword, + IsAdmin: isAdmin, + MustChangePassword: mustChangePassword, Visibility: visibility, } diff --git a/docs/content/administration/command-line.en-us.md b/docs/content/administration/command-line.en-us.md index 5049df35e050..752a8d4c6fd3 100644 --- a/docs/content/administration/command-line.en-us.md +++ b/docs/content/administration/command-line.en-us.md @@ -83,8 +83,7 @@ Admin operations: - `--email value`: Email. Required. - `--admin`: If provided, this makes the user an admin. Optional. - `--access-token`: If provided, an access token will be created for the user. Optional. (default: false). - - `--must-change-password`: If provided, the created user will be required to choose a newer password after the - initial login. Optional. (default: true). + - `--must-change-password`: The created user will be required to set a new password after the initial login, default: true. It could be disabled by `--must-change-password=false`. - `--random-password`: If provided, a randomly generated password will be used as the password of the created user. The value of `--password` will be discarded. Optional. - `--random-password-length`: If provided, it will be used to configure the length of the randomly generated @@ -95,7 +94,7 @@ Admin operations: - Options: - `--username value`, `-u value`: Username. Required. - `--password value`, `-p value`: New password. Required. - - `--must-change-password`: If provided, the user is required to choose a new password after the login. Optional. + - `--must-change-password`: The user is required to set a new password after the login, default: true. It could be disabled by `--must-change-password=false`. - Examples: - `gitea admin user change-password --username myname --password asecurepassword` - `must-change-password`: diff --git a/models/db/engine.go b/models/db/engine.go index 8684c4e2f162..26abf0b96c21 100755 --- a/models/db/engine.go +++ b/models/db/engine.go @@ -284,8 +284,8 @@ func MaxBatchInsertSize(bean any) int { } // IsTableNotEmpty returns true if table has at least one record -func IsTableNotEmpty(tableName string) (bool, error) { - return x.Table(tableName).Exist() +func IsTableNotEmpty(beanOrTableName any) (bool, error) { + return x.Table(beanOrTableName).Exist() } // DeleteAllRecords will delete all the records of this table From 99463532820d7a88cdea88e66e0606b996cc9fc7 Mon Sep 17 00:00:00 2001 From: silverwind Date: Sun, 14 Apr 2024 19:53:52 +0200 Subject: [PATCH 35/35] Remove fomantic button module (#30475) CSS-only module. Button colors are reduced to this: Screenshot 2024-04-14 at 15 36 07 --------- Co-authored-by: wxiaoguang --- templates/admin/notice.tmpl | 2 +- templates/admin/repo/unadopted.tmpl | 2 +- templates/base/modal_actions_confirm.tmpl | 10 +- templates/devtest/fomantic-modal.tmpl | 31 +- templates/devtest/gitea-ui.tmpl | 42 +- templates/org/team/sidebar.tmpl | 2 +- .../code/recently_pushed_new_branches.tmpl | 2 +- templates/repo/diff/compare.tmpl | 24 +- .../repo/issue/view_content/sidebar.tmpl | 2 +- templates/repo/settings/lfs.tmpl | 2 +- templates/repo/settings/lfs_pointers.tmpl | 2 +- templates/repo/settings/webhook/history.tmpl | 2 +- templates/user/settings/applications.tmpl | 2 +- web_src/css/base.css | 8 - web_src/css/helpers.css | 3 + web_src/css/modules/button.css | 769 ++---- web_src/css/modules/modal.css | 4 + web_src/css/repo.css | 14 +- web_src/fomantic/build/semantic.css | 2318 ----------------- web_src/fomantic/semantic.json | 1 - .../js/components/PullRequestMergeForm.vue | 2 +- 21 files changed, 253 insertions(+), 2991 deletions(-) diff --git a/templates/admin/notice.tmpl b/templates/admin/notice.tmpl index 33d8a2f9632d..68703cc88442 100644 --- a/templates/admin/notice.tmpl +++ b/templates/admin/notice.tmpl @@ -49,7 +49,7 @@
- diff --git a/templates/admin/repo/unadopted.tmpl b/templates/admin/repo/unadopted.tmpl index 6a8e203694ab..a95f6b5120e2 100644 --- a/templates/admin/repo/unadopted.tmpl +++ b/templates/admin/repo/unadopted.tmpl @@ -54,7 +54,7 @@ - {{template "base/modal_actions_confirm" (dict "ModalButtonColors" "yellow")}} + {{template "base/modal_actions_confirm"}}
diff --git a/templates/base/modal_actions_confirm.tmpl b/templates/base/modal_actions_confirm.tmpl index c44320deffbb..9f7eb4adf2d9 100644 --- a/templates/base/modal_actions_confirm.tmpl +++ b/templates/base/modal_actions_confirm.tmpl @@ -1,7 +1,6 @@ {{/* Two buttons (negative, positive): * ModalButtonTypes: "yes" (default) or "confirm" -* ModalButtonColors: "primary" (default) / "blue" / "yellow" * ModalButtonCancelText * ModalButtonOkText @@ -22,14 +21,7 @@ The ".ok.button" and ".cancel.button" selectors are also used by Fomantic Modal {{end}} {{if .ModalButtonCancelText}}{{$textNegitive = .ModalButtonCancelText}}{{end}} {{if .ModalButtonOkText}}{{$textPositive = .ModalButtonOkText}}{{end}} - - {{$stylePositive := "primary"}} - {{if eq .ModalButtonColors "blue"}} - {{$stylePositive = "blue"}} - {{else if eq .ModalButtonColors "yellow"}} - {{$stylePositive = "yellow"}} - {{end}} - + {{end}}
diff --git a/templates/devtest/fomantic-modal.tmpl b/templates/devtest/fomantic-modal.tmpl index 5cd36721a717..f31cdc1983e6 100644 --- a/templates/devtest/fomantic-modal.tmpl +++ b/templates/devtest/fomantic-modal.tmpl @@ -1,6 +1,15 @@ {{template "base/head" .}}
{{template "base/alert" .}} + + - - - - - - -
{{template "base/footer" .}} diff --git a/templates/devtest/gitea-ui.tmpl b/templates/devtest/gitea-ui.tmpl index bb4fc77a74a8..3b13c13be840 100644 --- a/templates/devtest/gitea-ui.tmpl +++ b/templates/devtest/gitea-ui.tmpl @@ -29,41 +29,13 @@ - - - -
  • Recommended colors:

    - - - - - - - - -
  • -
  • -

    Supported but not recommended:

    -

    Do not use if there is no strong requirement. Do not use grey/black buttons, they don't work well with dark theme.

    - - - - - - - - - - - - - - + +
  • Inline / Plain:

    @@ -198,7 +170,7 @@
    123
  • - +

    Input with SVG

    @@ -271,10 +243,6 @@ button dropdown {{svg "octicon-triangle-down" 14 "dropdown icon"}} -
    @@ -290,10 +258,6 @@ button compact {{svg "octicon-triangle-down" 14 "dropdown icon"}}
    -
    diff --git a/templates/org/team/sidebar.tmpl b/templates/org/team/sidebar.tmpl index 9311a46e38f7..b9e55dd587d1 100644 --- a/templates/org/team/sidebar.tmpl +++ b/templates/org/team/sidebar.tmpl @@ -79,7 +79,7 @@
    {{if .IsOrganizationOwner}} {{end}} diff --git a/templates/repo/code/recently_pushed_new_branches.tmpl b/templates/repo/code/recently_pushed_new_branches.tmpl index 17ae7d119d18..b808f413d352 100644 --- a/templates/repo/code/recently_pushed_new_branches.tmpl +++ b/templates/repo/code/recently_pushed_new_branches.tmpl @@ -5,7 +5,7 @@ {{$branchLink := HTMLFormat `%s` $.RepoLink (PathEscapeSegments .Name) .Name}} {{ctx.Locale.Tr "repo.pulls.recently_pushed_new_branches" $branchLink $timeSince}} - + {{ctx.Locale.Tr "repo.pulls.compare_changes"}} diff --git a/templates/repo/diff/compare.tmpl b/templates/repo/diff/compare.tmpl index d0472577d0ab..f92750119719 100644 --- a/templates/repo/diff/compare.tmpl +++ b/templates/repo/diff/compare.tmpl @@ -184,23 +184,15 @@ {{end}} {{else if and .PageIsComparePull (gt .CommitCount 0)}} {{if .HasPullRequest}} -
    -
    - {{ctx.Locale.Tr "repo.pulls.has_pull_request" (print $.RepoLink "/pulls/" .PullRequest.Issue.Index) $.RepoRelPath .PullRequest.Index}} -

    - {{RenderIssueTitle $.Context .PullRequest.Issue.Title ($.Repository.ComposeMetas ctx)}} - #{{.PullRequest.Issue.Index}} -

    -
    -
    - {{- if .PullRequest.HasMerged -}} - {{svg "octicon-git-merge" 16}} {{ctx.Locale.Tr "repo.pulls.view"}} - {{else if .Issue.IsClosed}} - {{svg "octicon-issue-closed" 16}} {{ctx.Locale.Tr "repo.pulls.view"}} - {{else}} - {{svg "octicon-git-pull-request" 16}} {{ctx.Locale.Tr "repo.pulls.view"}} - {{end}} +
    + {{template "shared/issueicon" .}} +
    + {{RenderIssueTitle $.Context .PullRequest.Issue.Title ($.Repository.ComposeMetas ctx) | RenderCodeBlock}} + #{{.PullRequest.Issue.Index}}
    + + {{ctx.Locale.Tr "repo.pulls.view"}} +
    {{else}} {{if and $.IsSigned (not .Repository.IsArchived)}} diff --git a/templates/repo/issue/view_content/sidebar.tmpl b/templates/repo/issue/view_content/sidebar.tmpl index 7040c2849a2b..bb0bb2cff360 100644 --- a/templates/repo/issue/view_content/sidebar.tmpl +++ b/templates/repo/issue/view_content/sidebar.tmpl @@ -572,7 +572,7 @@ {{end}} -
    diff --git a/templates/repo/settings/lfs_pointers.tmpl b/templates/repo/settings/lfs_pointers.tmpl index a0bb8c46f0d9..758aec6bb031 100644 --- a/templates/repo/settings/lfs_pointers.tmpl +++ b/templates/repo/settings/lfs_pointers.tmpl @@ -37,7 +37,7 @@ - + {{ShortSha .Oid}} diff --git a/templates/repo/settings/webhook/history.tmpl b/templates/repo/settings/webhook/history.tmpl index 8ee1446a16f8..149840b0deac 100644 --- a/templates/repo/settings/webhook/history.tmpl +++ b/templates/repo/settings/webhook/history.tmpl @@ -6,7 +6,7 @@
    - diff --git a/templates/user/settings/applications.tmpl b/templates/user/settings/applications.tmpl index 5e2ffc3bb3ef..8baa07c90b0e 100644 --- a/templates/user/settings/applications.tmpl +++ b/templates/user/settings/applications.tmpl @@ -109,7 +109,7 @@

    {{ctx.Locale.Tr "settings.access_token_deletion_desc"}}

    - {{template "base/modal_actions_confirm" (dict "ModalButtonColors" "yellow")}} + {{template "base/modal_actions_confirm"}}
    {{template "user/settings/layout_footer" .}} diff --git a/web_src/css/base.css b/web_src/css/base.css index 20f361617749..b3f87044e06d 100644 --- a/web_src/css/base.css +++ b/web_src/css/base.css @@ -663,10 +663,6 @@ input:-webkit-autofill:active, font-size: 0.75em; } -.ui.form .ui.button { - font-weight: var(--font-weight-normal); -} - /* popover box shadows */ .ui.dropdown .menu, .ui.upward.dropdown > .menu, @@ -1347,10 +1343,6 @@ table th[data-sortt-desc] .svg { vertical-align: middle; } -.ui.ui.button { - justify-content: center; -} - .ui.dropdown .ui.label .svg { vertical-align: middle; } diff --git a/web_src/css/helpers.css b/web_src/css/helpers.css index 118c058b19a8..cf2e73572c31 100644 --- a/web_src/css/helpers.css +++ b/web_src/css/helpers.css @@ -52,6 +52,9 @@ only use: */ .tw-hidden.tw-hidden { display: none !important; } +/* proposed class from https://github.com/tailwindlabs/tailwindcss/pull/12128 */ +.tw-break-anywhere { overflow-wrap: anywhere !important; } + @media (max-width: 767.98px) { /* double selector so it wins over .tw-flex (old .gt-df) etc */ .not-mobile.not-mobile { diff --git a/web_src/css/modules/button.css b/web_src/css/modules/button.css index faeed8c9a1b5..47f55df7fa1d 100644 --- a/web_src/css/modules/button.css +++ b/web_src/css/modules/button.css @@ -1,11 +1,33 @@ -/* this contains override styles for buttons and related elements */ - -/* these styles changed the Fomantic UI's rules, Fomantic UI expects only "basic" buttons have borders */ -.ui.button, -.ui.button:focus { +/* based on Fomantic UI checkbox module, with just the parts extracted that we use. If you find any + unused rules here after refactoring, please remove them. */ + +.ui.button { + cursor: pointer; + display: inline-block; + min-height: 1em; + outline: none; + vertical-align: baseline; + font-family: var(--fonts-regular); + margin: 0 0.25em 0 0; + padding: 0.78571429em 1.5em; + font-weight: var(--font-weight-normal); + text-align: center; + text-decoration: none; + line-height: 1; + border-radius: 0.28571429rem; + user-select: none; + -webkit-tap-highlight-color: transparent; + justify-content: center; background: var(--color-button); border: 1px solid var(--color-light-border); color: var(--color-text); + white-space: nowrap; +} + +@media (max-width: 767.98px) { + .ui.button { + white-space: normal; + } } .ui.button:hover { @@ -13,10 +35,6 @@ color: var(--color-text); } -.page-content .ui.button { - box-shadow: none !important; -} - .ui.active.button, .ui.button:active, .ui.active.button:active, @@ -25,89 +43,116 @@ color: var(--color-text); } -.delete-button, -.delete-button:hover { - color: var(--color-red); +.ui.buttons .disabled.button:not(.basic), +.ui.disabled.button, +.ui.button:disabled, +.ui.disabled.button:hover, +.ui.disabled.active.button { + cursor: default; + opacity: var(--opacity-disabled) !important; + background-image: none; + pointer-events: none !important; } -/* btn is a plain button without any opinionated styling, it only uses flex for vertical alignment like ".ui.button" in base.css */ - -.btn { - background: transparent; - border-radius: var(--border-radius); +.ui.labeled.button:not(.icon) { + display: inline-flex; + flex-direction: row; + background: none; + padding: 0 !important; border: none; - color: inherit; +} +.ui.labeled.button > .button { margin: 0; - padding: 0; } - -.btn:hover, -.btn:active, -.btn:focus { - background: none; - border: none; +.ui.labeled.button > .label { + display: flex; + align-items: center; + margin: 0 0 0 -1px !important; + font-size: 1em; + border-color: var(--color-light-border); } -a.btn, -a.btn:hover { - color: inherit; +.ui.button > .icon:not(.button) { + height: auto; + opacity: 0.8; } - -/* By default, Fomantic UI doesn't support "bordered" buttons group, but Gitea would like to use it. -And the default buttons always have borders now (not the same as Fomantic UI's default buttons, see above). -It needs some tricks to tweak the left/right borders with active state */ - -.ui.buttons .button { - border-right: none; +.ui.button:not(.icon) > .icon:not(.button):not(.dropdown), +.ui.button:not(.icon) > .icons:not(.button):not(.dropdown) { + margin: 0 0.42857143em 0 -0.21428571em; + vertical-align: baseline; } - -.ui.buttons .button:hover { - border-color: var(--color-secondary-dark-2); +.ui.button:not(.icon) > .icons:not(.button):not(.dropdown) > .icon { + vertical-align: baseline; } - -.ui.buttons .button:hover + .button { - border-left: 1px solid var(--color-secondary-dark-2); +.ui.button:not(.icon) > .right.icon:not(.button):not(.dropdown) { + margin: 0 -0.21428571em 0 0.42857143em; } -/* TODO: these "tw-hidden" selectors are only used by "blame.tmpl" buttons: Raw/Normal View/History/Unescape, need to be refactored to a clear solution later */ -.ui.buttons .button:first-child, -.ui.buttons .button.tw-hidden:first-child + .button { - border-left: 1px solid var(--color-light-border); +.ui.compact.buttons .button, +.ui.compact.button { + padding: 0.58928571em 1.125em; } - -.ui.buttons .button:last-child, -.ui.buttons .button:nth-last-child(2):has(+ .button.tw-hidden) { - border-right: 1px solid var(--color-light-border); +.ui.compact.icon.buttons .button, +.ui.compact.icon.button { + padding: 0.58928571em; +} +.ui.compact.labeled.icon.button { + padding: 0.58928571em 3.69642857em; +} +.ui.compact.labeled.icon.button > .icon { + padding: 0.58928571em 0; } -.ui.buttons .button.active { - border-left: 1px solid var(--color-light-border); - border-right: 1px solid var(--color-light-border); +.ui.buttons .button, +.ui.button { + font-size: 1rem; +} +.ui.mini.buttons .dropdown, +.ui.mini.buttons .dropdown .menu > .item, +.ui.mini.buttons .button, +.ui.ui.ui.ui.mini.button { + font-size: 0.78571429rem; +} +.ui.tiny.buttons .dropdown, +.ui.tiny.buttons .dropdown .menu > .item, +.ui.tiny.buttons .button, +.ui.ui.ui.ui.tiny.button { + font-size: 0.85714286rem; +} +.ui.small.buttons .dropdown, +.ui.small.buttons .dropdown .menu > .item, +.ui.small.buttons .button, +.ui.ui.ui.ui.small.button { + font-size: 0.92857143rem; } -.ui.buttons .button.active + .button { - border-left: none; +.ui.icon.buttons .button, +.ui.icon.button:not(.compact) { + padding: 0.78571429em; +} +.ui.icon.buttons .button > .icon, +.ui.icon.button > .icon { + margin: 0 !important; + vertical-align: top; } .ui.basic.buttons .button, -.ui.basic.button, -.ui.basic.buttons .button:hover, -.ui.basic.button:hover { - box-shadow: none; +.ui.basic.button { + border-radius: 0.28571429rem; + background: none; } - -/* apply the vertical padding of .compact to non-compact buttons when they contain a svg as they - would otherwise appear too large. Seen on "RSS Feed" button on repo releases tab. */ -.ui.small.button:not(.compact):has(.svg) { - padding-top: 0.58928571em; - padding-bottom: 0.58928571em; +.ui.basic.buttons { + border: 1px solid var(--color-secondary); + border-radius: 0.28571429rem; +} +.ui.basic.buttons .button { + border-radius: 0; + border-left: 1px solid var(--color-secondary); } .ui.labeled.button.disabled > .button, .ui.basic.buttons .button, -.ui.basic.button, -.ui.basic.buttons .button:focus, -.ui.basic.button:focus { +.ui.basic.button { color: var(--color-text-light); background: var(--color-button); } @@ -129,23 +174,45 @@ It needs some tricks to tweak the left/right borders with active state */ background: var(--color-active); } -.ui.labeled.button > .label { - border-color: var(--color-light-border); +.ui.labeled.icon.button { + position: relative; + padding-left: 4.07142857em !important; + padding-right: 1.5em !important; } -.ui.labeled.icon.buttons > .button > .icon, .ui.labeled.icon.button > .icon { + position: absolute; + top: 0; + left: 0; + height: 100%; + line-height: 1; + border-radius: 0; + border-top-left-radius: inherit; + border-bottom-left-radius: inherit; + text-align: center; + animation: none; + padding: 0.78571429em 0; + margin: 0; + width: 2.57142857em; background: var(--color-hover); } -/* primary */ +.ui.button.toggle.active { + background-color: var(--color-green); + color: var(--color-white); +} +.ui.button.toggle.active:hover { + background-color: var(--color-green-dark-1); + color: var(--color-white); +} + +.ui.fluid.button { + width: 100%; + display: block; +} -.ui.primary.labels .label, -.ui.ui.ui.primary.label, .ui.primary.button, -.ui.primary.buttons .button, -.ui.primary.button:focus, -.ui.primary.buttons .button:focus { +.ui.primary.buttons .button { background: var(--color-primary); color: var(--color-primary-contrast); } @@ -162,11 +229,10 @@ It needs some tricks to tweak the left/right borders with active state */ } .ui.basic.primary.buttons .button, -.ui.basic.primary.button, -.ui.basic.primary.buttons .button:focus, -.ui.basic.primary.button:focus { +.ui.basic.primary.button { color: var(--color-primary); border-color: var(--color-primary); + background: none; } .ui.basic.primary.buttons .button:hover, @@ -181,55 +247,8 @@ It needs some tricks to tweak the left/right borders with active state */ border-color: var(--color-primary-active); } -/* secondary */ - -.ui.secondary.labels .label, -.ui.ui.ui.secondary.label, -.ui.secondary.button, -.ui.secondary.buttons .button, -.ui.secondary.button:focus, -.ui.secondary.buttons .button:focus { - background: var(--color-secondary-button); -} - -.ui.secondary.button:hover, -.ui.secondary.buttons .button:hover { - background: var(--color-secondary-hover); -} - -.ui.secondary.button:active, -.ui.secondary.buttons .button:active { - background: var(--color-secondary-active); -} - -.ui.basic.secondary.buttons .button, -.ui.basic.secondary.button, -.ui.basic.secondary.button:focus, -.ui.basic.secondary.buttons .button:focus { - color: var(--color-secondary-button); - border-color: var(--color-secondary-button); -} - -.ui.basic.secondary.buttons .button:hover, -.ui.basic.secondary.button:hover { - color: var(--color-secondary-hover); - border-color: var(--color-secondary-hover); -} - -.ui.basic.secondary.buttons .button:active, -.ui.basic.secondary.button:active { - color: var(--color-secondary-active); - border-color: var(--color-secondary-active); -} - -/* red */ - -.ui.red.labels .label, -.ui.ui.ui.red.label, .ui.red.button, -.ui.red.buttons .button, -.ui.red.button:focus, -.ui.red.buttons .button:focus { +.ui.red.buttons .button { background: var(--color-red); } @@ -244,11 +263,10 @@ It needs some tricks to tweak the left/right borders with active state */ } .ui.basic.red.buttons .button, -.ui.basic.red.button, -.ui.basic.red.buttons .button:focus, -.ui.basic.red.button:focus { +.ui.basic.red.button { color: var(--color-red); border-color: var(--color-red); + background: none; } .ui.basic.red.buttons .button:hover, @@ -263,137 +281,8 @@ It needs some tricks to tweak the left/right borders with active state */ border-color: var(--color-red-dark-2); } -/* orange */ - -.ui.orange.labels .label, -.ui.ui.ui.orange.label, -.ui.orange.button, -.ui.orange.buttons .button, -.ui.orange.button:focus, -.ui.orange.buttons .button:focus { - background: var(--color-orange); -} - -.ui.orange.button:hover, -.ui.orange.buttons .button:hover { - background: var(--color-orange-dark-1); -} - -.ui.orange.button:active, -.ui.orange.buttons .button:active { - background: var(--color-orange-dark-2); -} - -.ui.basic.orange.buttons .button, -.ui.basic.orange.button, -.ui.basic.orange.buttons .button:focus, -.ui.basic.orange.button:focus { - color: var(--color-orange); - border-color: var(--color-orange); -} - -.ui.basic.orange.buttons .button:hover, -.ui.basic.orange.button:hover { - color: var(--color-orange-dark-1); - border-color: var(--color-orange-dark-1); -} - -.ui.basic.orange.buttons .button:active, -.ui.basic.orange.button:active { - color: var(--color-orange-dark-2); - border-color: var(--color-orange-dark-2); -} - -/* yellow */ - -.ui.yellow.labels .label, -.ui.ui.ui.yellow.label, -.ui.yellow.button, -.ui.yellow.buttons .button, -.ui.yellow.button:focus, -.ui.yellow.buttons .button:focus { - background: var(--color-yellow); -} - -.ui.yellow.button:hover, -.ui.yellow.buttons .button:hover { - background: var(--color-yellow-dark-1); -} - -.ui.yellow.button:active, -.ui.yellow.buttons .button:active { - background: var(--color-yellow-dark-2); -} - -.ui.basic.yellow.buttons .button, -.ui.basic.yellow.button, -.ui.basic.yellow.buttons .button:focus, -.ui.basic.yellow.button:focus { - color: var(--color-yellow); - border-color: var(--color-yellow); -} - -.ui.basic.yellow.buttons .button:hover, -.ui.basic.yellow.button:hover { - color: var(--color-yellow-dark-1); - border-color: var(--color-yellow-dark-1); -} - -.ui.basic.yellow.buttons .button:active, -.ui.basic.yellow.button:active { - color: var(--color-yellow-dark-2); - border-color: var(--color-yellow-dark-2); -} - -/* olive */ - -.ui.olive.labels .label, -.ui.ui.ui.olive.label, -.ui.olive.button, -.ui.olive.buttons .button, -.ui.olive.button:focus, -.ui.olive.buttons .button:focus { - background: var(--color-olive); -} - -.ui.olive.button:hover, -.ui.olive.buttons .button:hover { - background: var(--color-olive-dark-1); -} - -.ui.olive.button:active, -.ui.olive.buttons .button:active { - background: var(--color-olive-dark-2); -} - -.ui.basic.olive.buttons .button, -.ui.basic.olive.button, -.ui.basic.olive.buttons .button:focus, -.ui.basic.olive.button:focus { - color: var(--color-olive); - border-color: var(--color-olive); -} - -.ui.basic.olive.buttons .button:hover, -.ui.basic.olive.button:hover { - color: var(--color-olive-dark-1); - border-color: var(--color-olive-dark-1); -} - -.ui.basic.olive.buttons .button:active, -.ui.basic.olive.button:active { - color: var(--color-olive-dark-2); - border-color: var(--color-olive-dark-2); -} - -/* green */ - -.ui.green.labels .label, -.ui.ui.ui.green.label, .ui.green.button, -.ui.green.buttons .button, -.ui.green.button:focus, -.ui.green.buttons .button:focus { +.ui.green.buttons .button { background: var(--color-green); } @@ -408,11 +297,10 @@ It needs some tricks to tweak the left/right borders with active state */ } .ui.basic.green.buttons .button, -.ui.basic.green.button, -.ui.basic.green.buttons .button:focus, -.ui.basic.green.button:focus { +.ui.basic.green.button { color: var(--color-green); border-color: var(--color-green); + background: none; } .ui.basic.green.buttons .button:hover, @@ -427,326 +315,93 @@ It needs some tricks to tweak the left/right borders with active state */ border-color: var(--color-green-dark-2); } -/* teal */ - -.ui.teal.labels .label, -.ui.ui.ui.teal.label, -.ui.teal.button, -.ui.teal.buttons .button, -.ui.teal.button:focus, -.ui.teal.buttons .button:focus { - background: var(--color-teal); -} - -.ui.teal.button:hover, -.ui.teal.buttons .button:hover { - background: var(--color-teal-dark-1); +.ui.buttons { + display: inline-flex; + flex-direction: row; + font-size: 0; + vertical-align: baseline; + margin: 0 0.25em 0 0; } -.ui.teal.button:active, -.ui.teal.buttons .button:active { - background: var(--color-teal-dark-2); -} - -.ui.basic.teal.buttons .button, -.ui.basic.teal.button, -.ui.basic.teal.buttons .button:focus, -.ui.basic.teal.button:focus { - color: var(--color-teal); - border-color: var(--color-teal); -} - -.ui.basic.teal.buttons .button:hover, -.ui.basic.teal.button:hover { - color: var(--color-teal-dark-1); - border-color: var(--color-teal-dark-1); -} - -.ui.basic.teal.buttons .button:active, -.ui.basic.teal.button:active { - color: var(--color-teal-dark-2); - border-color: var(--color-teal-dark-2); -} - -/* blue */ - -.ui.blue.labels .label, -.ui.ui.ui.blue.label, -.ui.blue.button, -.ui.blue.buttons .button, -.ui.blue.button:focus, -.ui.blue.buttons .button:focus { - background: var(--color-blue); -} - -.ui.blue.button:hover, -.ui.blue.buttons .button:hover { - background: var(--color-blue-dark-1); -} - -.ui.blue.button:active, -.ui.blue.buttons .button:active { - background: var(--color-blue-dark-2); -} - -.ui.basic.blue.buttons .button, -.ui.basic.blue.button, -.ui.basic.blue.buttons .button:focus, -.ui.basic.blue.button:focus { - color: var(--color-blue); - border-color: var(--color-blue); -} - -.ui.basic.blue.buttons .button:hover, -.ui.basic.blue.button:hover { - color: var(--color-blue-dark-1); - border-color: var(--color-blue-dark-1); -} - -.ui.basic.blue.buttons .button:active, -.ui.basic.blue.button:active { - color: var(--color-blue-dark-2); - border-color: var(--color-blue-dark-2); -} - -/* violet */ - -.ui.violet.labels .label, -.ui.ui.ui.violet.label, -.ui.violet.button, -.ui.violet.buttons .button, -.ui.violet.button:focus, -.ui.violet.buttons .button:focus { - background: var(--color-violet); -} - -.ui.violet.button:hover, -.ui.violet.buttons .button:hover { - background: var(--color-violet-dark-1); -} - -.ui.violet.button:active, -.ui.violet.buttons .button:active { - background: var(--color-violet-dark-2); -} - -.ui.basic.violet.buttons .button, -.ui.basic.violet.button, -.ui.basic.violet.buttons .button:focus, -.ui.basic.violet.button:focus { - color: var(--color-violet); - border-color: var(--color-violet); -} - -.ui.basic.violet.buttons .button:hover, -.ui.basic.violet.button:hover { - color: var(--color-violet-dark-1); - border-color: var(--color-violet-dark-1); -} - -.ui.basic.violet.buttons .button:active, -.ui.basic.violet.button:active { - color: var(--color-violet-dark-2); - border-color: var(--color-violet-dark-2); -} - -/* purple */ - -.ui.purple.labels .label, -.ui.ui.ui.purple.label, -.ui.purple.button, -.ui.purple.buttons .button, -.ui.purple.button:focus, -.ui.purple.buttons .button:focus { - background: var(--color-purple); -} - -.ui.purple.button:hover, -.ui.purple.buttons .button:hover { - background: var(--color-purple-dark-1); -} - -.ui.purple.button:active, -.ui.purple.buttons .button:active { - background: var(--color-purple-dark-2); -} - -.ui.basic.purple.buttons .button, -.ui.basic.purple.button, -.ui.basic.purple.buttons .button:focus, -.ui.basic.purple.button:focus { - color: var(--color-purple); - border-color: var(--color-purple); -} - -.ui.basic.purple.buttons .button:hover, -.ui.basic.purple.button:hover { - color: var(--color-purple-dark-1); - border-color: var(--color-purple-dark-1); -} - -.ui.basic.purple.buttons .button:active, -.ui.basic.purple.button:active { - color: var(--color-purple-dark-2); - border-color: var(--color-purple-dark-2); -} - -/* pink */ - -.ui.pink.labels .label, -.ui.ui.ui.pink.label, -.ui.pink.button, -.ui.pink.buttons .button, -.ui.pink.button:focus, -.ui.pink.buttons .button:focus { - background: var(--color-pink); -} - -.ui.pink.button:hover, -.ui.pink.buttons .button:hover { - background: var(--color-pink-dark-1); -} - -.ui.pink.button:active, -.ui.pink.buttons .button:active { - background: var(--color-pink-dark-2); -} - -.ui.basic.pink.buttons .button, -.ui.basic.pink.button, -.ui.basic.pink.buttons .button:focus, -.ui.basic.pink.button:focus { - color: var(--color-pink); - border-color: var(--color-pink); -} - -.ui.basic.pink.buttons .button:hover, -.ui.basic.pink.button:hover { - color: var(--color-pink-dark-1); - border-color: var(--color-pink-dark-1); -} - -.ui.basic.pink.buttons .button:active, -.ui.basic.pink.button:active { - color: var(--color-pink-dark-2); - border-color: var(--color-pink-dark-2); -} - -/* brown */ - -.ui.brown.labels .label, -.ui.ui.ui.brown.label, -.ui.brown.button, -.ui.brown.buttons .button, -.ui.brown.button:focus, -.ui.brown.buttons .button:focus { - background: var(--color-brown); -} - -.ui.brown.button:hover, -.ui.brown.buttons .button:hover { - background: var(--color-brown-dark-1); -} - -.ui.brown.button:active, -.ui.brown.buttons .button:active { - background: var(--color-brown-dark-2); +.delete-button, +.delete-button:hover { + color: var(--color-red); } -.ui.basic.brown.buttons .button, -.ui.basic.brown.button, -.ui.basic.brown.buttons .button:focus, -.ui.basic.brown.button:focus { - color: var(--color-brown); - border-color: var(--color-brown); -} +/* btn is a plain button without any opinionated styling, it only uses flex for vertical alignment like ".ui.button" in base.css */ -.ui.basic.brown.buttons .button:hover, -.ui.basic.brown.button:hover { - color: var(--color-brown-dark-1); - border-color: var(--color-brown-dark-1); +.btn { + background: transparent; + border-radius: var(--border-radius); + border: none; + color: inherit; + margin: 0; + padding: 0; } -.ui.basic.brown.buttons .button:active, -.ui.basic.brown.button:active { - color: var(--color-brown-dark-2); - border-color: var(--color-brown-dark-2); +.btn:hover, +.btn:active { + background: none; + border: none; } -/* negative */ - -.ui.negative.buttons .button, -.ui.negative.button, -.ui.negative.buttons .button:focus, -.ui.negative.button:focus { - background: var(--color-red); +a.btn, +a.btn:hover { + color: inherit; } -.ui.negative.buttons .button:hover, -.ui.negative.button:hover { - background: var(--color-red-dark-1); -} +/* By default, Fomantic UI doesn't support "bordered" buttons group, but Gitea would like to use it. +And the default buttons always have borders now (not the same as Fomantic UI's default buttons, see above). +It needs some tricks to tweak the left/right borders with active state */ -.ui.negative.buttons .button:active, -.ui.negative.button:active { - background: var(--color-red-dark-2); +.ui.buttons .button { + border-right: none; + flex: 1 0 auto; + border-radius: 0; + margin: 0; } - -.ui.basic.negative.buttons .button, -.ui.basic.negative.button, -.ui.basic.negative.buttons .button:focus, -.ui.basic.negative.button:focus { - color: var(--color-red); - border-color: var(--color-red); +.ui.buttons .button:first-child { + border-left: none; + margin-left: 0; + border-top-left-radius: 0.28571429rem; + border-bottom-left-radius: 0.28571429rem; } - -.ui.basic.negative.buttons .button:hover, -.ui.basic.negative.button:hover { - color: var(--color-red-dark-1); - border-color: var(--color-red-dark-1); +.ui.buttons .button:last-child { + border-top-right-radius: 0.28571429rem; + border-bottom-right-radius: 0.28571429rem; } -.ui.basic.negative.buttons .button:active, -.ui.basic.negative.button:active { - color: var(--color-red-dark-2); - border-color: var(--color-red-dark-2); +.ui.buttons .button:hover { + border-color: var(--color-secondary-dark-2); } -/* positive */ - -.ui.positive.buttons .button, -.ui.positive.button, -.ui.positive.buttons .button:focus, -.ui.positive.button:focus { - background: var(--color-green); +.ui.buttons .button:hover + .button { + border-left: 1px solid var(--color-secondary-dark-2); } -.ui.positive.buttons .button:hover, -.ui.positive.button:hover { - background: var(--color-green-dark-1); +/* TODO: these "tw-hidden" selectors are only used by "blame.tmpl" buttons: Raw/Normal View/History/Unescape, need to be refactored to a clear solution later */ +.ui.buttons .button:first-child, +.ui.buttons .button.tw-hidden:first-child + .button { + border-left: 1px solid var(--color-light-border); } -.ui.positive.buttons .button:active, -.ui.positive.button:active { - background: var(--color-green-dark-2); +.ui.buttons .button:last-child, +.ui.buttons .button:nth-last-child(2):has(+ .button.tw-hidden) { + border-right: 1px solid var(--color-light-border); } -.ui.basic.positive.buttons .button, -.ui.basic.positive.button, -.ui.basic.positive.buttons .button:focus, -.ui.basic.positive.button:focus { - color: var(--color-green); - border-color: var(--color-green); +.ui.buttons .button.active { + border-left: 1px solid var(--color-light-border); + border-right: 1px solid var(--color-light-border); } -.ui.basic.positive.buttons .button:hover, -.ui.basic.positive.button:hover { - color: var(--color-green-dark-1); - border-color: var(--color-green-dark-1); +.ui.buttons .button.active + .button { + border-left: none; } -.ui.basic.positive.buttons .button:active, -.ui.basic.positive.button:active { - color: var(--color-green-dark-2); - border-color: var(--color-green-dark-2); +/* apply the vertical padding of .compact to non-compact buttons when they contain a svg as they + would otherwise appear too large. Seen on "RSS Feed" button on repo releases tab. */ +.ui.small.button:not(.compact):has(.svg) { + padding-top: 0.58928571em; + padding-bottom: 0.58928571em; } diff --git a/web_src/css/modules/modal.css b/web_src/css/modules/modal.css index 54a4ef81ca19..a2acfeaa150e 100644 --- a/web_src/css/modules/modal.css +++ b/web_src/css/modules/modal.css @@ -10,6 +10,10 @@ top: 1.2em; } +.ui.modal > .close.inside { + color: inherit; +} + .ui.modal > .close.icon[height="16"] { top: 0.7em; /* fomantic uses absolute layout, so if we have special icon size, it needs this trick to align vertically */ color: var(--color-text-dark); diff --git a/web_src/css/repo.css b/web_src/css/repo.css index 52f9d5a6ca83..887789115ebb 100644 --- a/web_src/css/repo.css +++ b/web_src/css/repo.css @@ -654,15 +654,15 @@ td .commit-summary { padding: 2px .5rem; } -.repository.view.issue .issue-title .index { +.issue-title .index { color: var(--color-text-light-2); } -.repository.view.issue .issue-title .label { +.issue-title .label { margin-right: 10px; } -.repository.view.issue .issue-title .edit-zone { +.issue-title .edit-zone { margin-top: 10px; } @@ -1164,14 +1164,6 @@ td .commit-summary { font-size: 14px; } -.repository.compare.pull .title .issue-title { - margin-bottom: 0.5rem; -} - -.repository.compare.pull .title .issue-title .index { - color: var(--color-text-light-2); -} - .repository .ui.dropdown.filter > .menu { margin-top: 1px; } diff --git a/web_src/fomantic/build/semantic.css b/web_src/fomantic/build/semantic.css index 8ce9ee24ea8f..7c404bdb30a0 100644 --- a/web_src/fomantic/build/semantic.css +++ b/web_src/fomantic/build/semantic.css @@ -8,2324 +8,6 @@ * http://opensource.org/licenses/MIT * */ -/*! - * # Fomantic-UI - Button - * http://github.com/fomantic/Fomantic-UI/ - * - * - * Released under the MIT license - * http://opensource.org/licenses/MIT - * - */ - -/******************************* - Button -*******************************/ - -.ui.button { - cursor: pointer; - display: inline-block; - min-height: 1em; - outline: none; - border: none; - vertical-align: baseline; - background: #E0E1E2 none; - color: rgba(0, 0, 0, 0.6); - font-family: var(--fonts-regular); - margin: 0 0.25em 0 0; - padding: 0.78571429em 1.5em 0.78571429em; - text-transform: none; - text-shadow: none; - font-weight: 500; - line-height: 1em; - font-style: normal; - text-align: center; - text-decoration: none; - border-radius: 0.28571429rem; - box-shadow: 0 0 0 1px transparent inset, 0 0 0 0 rgba(34, 36, 38, 0.15) inset; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none; - transition: opacity 0.1s ease, background-color 0.1s ease, color 0.1s ease, box-shadow 0.1s ease, background 0.1s ease; - will-change: auto; - -webkit-tap-highlight-color: transparent; -} - -/******************************* - States -*******************************/ - -/*-------------- - Hover ----------------*/ - -.ui.button:hover { - background-color: #CACBCD; - background-image: none; - box-shadow: 0 0 0 1px transparent inset, 0 0 0 0 rgba(34, 36, 38, 0.15) inset; - color: rgba(0, 0, 0, 0.8); -} - -.ui.button:hover .icon { - opacity: 0.85; -} - -/*-------------- - Focus ----------------*/ - -.ui.button:focus { - background-color: #CACBCD; - color: rgba(0, 0, 0, 0.8); - background-image: none; - box-shadow: ''; -} - -.ui.button:focus .icon { - opacity: 0.85; -} - -/*-------------- - Down ----------------*/ - -.ui.button:active, -.ui.active.button:active { - background-color: #BABBBC; - background-image: ''; - color: rgba(0, 0, 0, 0.9); - box-shadow: 0 0 0 1px transparent inset, none; -} - -/*-------------- - Active ----------------*/ - -.ui.active.button { - background-color: #C0C1C2; - background-image: none; - box-shadow: 0 0 0 1px transparent inset; - color: rgba(0, 0, 0, 0.95); -} - -.ui.active.button:hover { - background-color: #C0C1C2; - background-image: none; - color: rgba(0, 0, 0, 0.95); -} - -.ui.active.button:active { - background-color: #C0C1C2; - background-image: none; -} - -/*-------------- - Loading ----------------*/ - -/* Specificity hack */ - -.ui.loading.loading.loading.loading.loading.loading.button { - position: relative; - cursor: default; - text-shadow: none !important; - color: transparent; - opacity: 1; - pointer-events: auto; - transition: all 0s linear, opacity 0.1s ease; -} - -.ui.loading.button:before { - position: absolute; - content: ''; - top: 50%; - left: 50%; - margin: -0.64285714em 0 0 -0.64285714em; - width: 1.28571429em; - height: 1.28571429em; - border-radius: 500rem; - border: 0.2em solid rgba(0, 0, 0, 0.15); -} - -.ui.loading.button:after { - position: absolute; - content: ''; - top: 50%; - left: 50%; - margin: -0.64285714em 0 0 -0.64285714em; - width: 1.28571429em; - height: 1.28571429em; - border-radius: 500rem; - animation: loader 0.6s infinite linear; - border: 0.2em solid currentColor; - color: #FFFFFF; - box-shadow: 0 0 0 1px transparent; -} - -.ui.labeled.icon.loading.button .icon { - background-color: transparent; - box-shadow: none; -} - -.ui.basic.loading.button:not(.inverted):before { - border-color: rgba(0, 0, 0, 0.1); -} - -.ui.basic.loading.button:not(.inverted):after { - border-color: #767676; -} - -/*------------------- - Disabled - --------------------*/ - -.ui.buttons .disabled.button:not(.basic), -.ui.disabled.button, -.ui.button:disabled, -.ui.disabled.button:hover, -.ui.disabled.active.button { - cursor: default; - opacity: var(--opacity-disabled) !important; - background-image: none; - box-shadow: none; - pointer-events: none !important; -} - -/* Basic Group With Disabled */ - -.ui.basic.buttons .ui.disabled.button { - border-color: rgba(34, 36, 38, 0.5); -} - -/******************************* - Types -*******************************/ - -/*------------------- - Labeled Button - --------------------*/ - -.ui.labeled.button:not(.icon) { - display: inline-flex; - flex-direction: row; - background: none; - padding: 0 !important; - border: none; - box-shadow: none; -} - -.ui.labeled.button > .button { - margin: 0; -} - -.ui.labeled.button > .label { - display: flex; - align-items: center; - margin: 0 0 0 -1px !important; - font-size: 1em; - padding: ''; - border-color: rgba(34, 36, 38, 0.15); -} - -/* Tag */ - -.ui.labeled.button > .tag.label:before { - width: 1.85em; - height: 1.85em; -} - -/* Right */ - -.ui.labeled.button:not([class*="left labeled"]) > .button { - border-top-right-radius: 0; - border-bottom-right-radius: 0; -} - -.ui.labeled.button:not([class*="left labeled"]) > .label { - border-top-left-radius: 0; - border-bottom-left-radius: 0; -} - -/* Left Side */ - -.ui[class*="left labeled"].button > .button { - border-top-left-radius: 0; - border-bottom-left-radius: 0; -} - -.ui[class*="left labeled"].button > .label { - border-top-right-radius: 0; - border-bottom-right-radius: 0; -} - -/*-------------- - Icon ----------------*/ - -.ui.button > .icon:not(.button) { - height: auto; - opacity: 0.8; - transition: opacity 0.1s ease; - color: ''; -} - -.ui.button:not(.icon) > .icon:not(.button):not(.dropdown), -.ui.button:not(.icon) > .icons:not(.button):not(.dropdown) { - margin: 0 0.42857143em 0 -0.21428571em; - vertical-align: baseline; -} - -.ui.button:not(.icon) > .icons:not(.button):not(.dropdown) > .icon { - vertical-align: baseline; -} - -.ui.button:not(.icon) > .right.icon:not(.button):not(.dropdown) { - margin: 0 -0.21428571em 0 0.42857143em; -} - -/******************************* - Variations -*******************************/ - -/*------------------- - Floated - --------------------*/ - -.ui[class*="left floated"].buttons, -.ui[class*="left floated"].button { - float: left; - margin-left: 0; - margin-right: 0.25em; -} - -.ui[class*="right floated"].buttons, -.ui[class*="right floated"].button { - float: right; - margin-right: 0; - margin-left: 0.25em; -} - -/*------------------- - Compact - --------------------*/ - -.ui.compact.buttons .button, -.ui.compact.button { - padding: 0.58928571em 1.125em 0.58928571em; -} - -.ui.compact.icon.buttons .button, -.ui.compact.icon.button { - padding: 0.58928571em 0.58928571em 0.58928571em; -} - -.ui.compact.labeled.icon.buttons .button, -.ui.compact.labeled.icon.button { - padding: 0.58928571em 3.69642857em 0.58928571em; -} - -.ui.compact.labeled.icon.buttons .button > .icon, -.ui.compact.labeled.icon.button > .icon { - padding: 0.58928571em 0 0.58928571em 0; -} - -/*------------------- - Sizes ---------------------*/ - -.ui.buttons .button, -.ui.buttons .or, -.ui.button { - font-size: 1rem; -} - -.ui.mini.buttons .dropdown, -.ui.mini.buttons .dropdown .menu > .item, -.ui.mini.buttons .button, -.ui.mini.buttons .or, -.ui.ui.ui.ui.mini.button { - font-size: 0.78571429rem; -} - -.ui.tiny.buttons .dropdown, -.ui.tiny.buttons .dropdown .menu > .item, -.ui.tiny.buttons .button, -.ui.tiny.buttons .or, -.ui.ui.ui.ui.tiny.button { - font-size: 0.85714286rem; -} - -.ui.small.buttons .dropdown, -.ui.small.buttons .dropdown .menu > .item, -.ui.small.buttons .button, -.ui.small.buttons .or, -.ui.ui.ui.ui.small.button { - font-size: 0.92857143rem; -} - -.ui.large.buttons .dropdown, -.ui.large.buttons .dropdown .menu > .item, -.ui.large.buttons .button, -.ui.large.buttons .or, -.ui.ui.ui.ui.large.button { - font-size: 1.14285714rem; -} - -.ui.big.buttons .dropdown, -.ui.big.buttons .dropdown .menu > .item, -.ui.big.buttons .button, -.ui.big.buttons .or, -.ui.ui.ui.ui.big.button { - font-size: 1.28571429rem; -} - -.ui.huge.buttons .dropdown, -.ui.huge.buttons .dropdown .menu > .item, -.ui.huge.buttons .button, -.ui.huge.buttons .or, -.ui.ui.ui.ui.huge.button { - font-size: 1.42857143rem; -} - -.ui.massive.buttons .dropdown, -.ui.massive.buttons .dropdown .menu > .item, -.ui.massive.buttons .button, -.ui.massive.buttons .or, -.ui.ui.ui.ui.massive.button { - font-size: 1.71428571rem; -} - -/*-------------- - Icon Only ----------------*/ - -.ui.icon.buttons .button, -.ui.icon.button:not(.animated):not(.compact) { - padding: 0.78571429em 0.78571429em 0.78571429em; -} - -.ui.animated.icon.button > .content > .icon, -.ui.icon.buttons .button > .icon, -.ui.icon.button > .icon { - opacity: 0.9; - margin: 0 !important; - vertical-align: top; -} - -.ui.animated.button > .content > .icon { - vertical-align: top; -} - -/*------------------- - Basic - --------------------*/ - -.ui.basic.buttons .button, -.ui.basic.button { - background: transparent none; - color: rgba(0, 0, 0, 0.6); - font-weight: normal; - border-radius: 0.28571429rem; - text-transform: none; - text-shadow: none !important; - box-shadow: 0 0 0 1px rgba(34, 36, 38, 0.15) inset; -} - -.ui.basic.buttons { - box-shadow: none; - border: 1px solid rgba(34, 36, 38, 0.15); - border-radius: 0.28571429rem; -} - -.ui.basic.buttons .button { - border-radius: 0; -} - -.ui.basic.buttons .button:hover, -.ui.basic.button:hover { - background: #FFFFFF; - color: rgba(0, 0, 0, 0.8); - box-shadow: 0 0 0 1px rgba(34, 36, 38, 0.35) inset, 0 0 0 0 rgba(34, 36, 38, 0.15) inset; -} - -.ui.basic.buttons .button:focus, -.ui.basic.button:focus { - background: #FFFFFF; - color: rgba(0, 0, 0, 0.8); - box-shadow: 0 0 0 1px rgba(34, 36, 38, 0.35) inset, 0 0 0 0 rgba(34, 36, 38, 0.15) inset; -} - -.ui.basic.buttons .button:active, -.ui.basic.button:active { - background: #F8F8F8; - color: rgba(0, 0, 0, 0.9); - box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.15) inset, 0 1px 4px 0 rgba(34, 36, 38, 0.15) inset; -} - -.ui.basic.buttons .active.button, -.ui.basic.active.button { - background: rgba(0, 0, 0, 0.05); - box-shadow: ''; - color: rgba(0, 0, 0, 0.95); -} - -.ui.basic.buttons .active.button:hover, -.ui.basic.active.button:hover { - background-color: rgba(0, 0, 0, 0.05); -} - -/* Vertical */ - -.ui.basic.buttons .button:hover { - box-shadow: 0 0 0 1px rgba(34, 36, 38, 0.35) inset, 0 0 0 0 rgba(34, 36, 38, 0.15) inset inset; -} - -.ui.basic.buttons .button:active { - box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.15) inset, 0 1px 4px 0 rgba(34, 36, 38, 0.15) inset inset; -} - -.ui.basic.buttons .active.button { - box-shadow: ''; -} - -/* Basic Group */ - -.ui.basic.buttons .button { - border-left: 1px solid rgba(34, 36, 38, 0.15); - box-shadow: none; -} - -.ui.basic.vertical.buttons .button { - border-left: none; - border-left-width: 0; - border-top: 1px solid rgba(34, 36, 38, 0.15); -} - -.ui.basic.vertical.buttons .button:first-child { - border-top-width: 0; -} - -/*-------------- - Labeled Icon - ---------------*/ - -.ui.labeled.icon.buttons .button, -.ui.labeled.icon.button { - position: relative; - padding-left: 4.07142857em !important; - padding-right: 1.5em !important; -} - -/* Left Labeled */ - -.ui.labeled.icon.buttons > .button > .icon, -.ui.labeled.icon.button > .icon { - position: absolute; - top: 0; - left: 0; - height: 100%; - line-height: 1; - border-radius: 0; - border-top-left-radius: inherit; - border-bottom-left-radius: inherit; - text-align: center; - animation: none; - padding: 0.78571429em 0 0.78571429em 0; - margin: 0; - width: 2.57142857em; - background-color: rgba(0, 0, 0, 0.05); - color: ''; - box-shadow: -1px 0 0 0 transparent inset; -} - -/* Right Labeled */ - -.ui[class*="right labeled"].icon.button { - padding-right: 4.07142857em !important; - padding-left: 1.5em !important; -} - -.ui[class*="right labeled"].icon.button > .icon { - left: auto; - right: 0; - border-radius: 0; - border-top-right-radius: inherit; - border-bottom-right-radius: inherit; - box-shadow: 1px 0 0 0 transparent inset; -} - -.ui.labeled.icon.buttons > .button > .icon:before, -.ui.labeled.icon.button > .icon:before, -.ui.labeled.icon.buttons > .button > .icon:after, -.ui.labeled.icon.button > .icon:after { - display: block; - position: relative; - width: 100%; - top: 0; - text-align: center; -} - -.ui.labeled.icon.buttons .button > .icon { - border-radius: 0; -} - -.ui.labeled.icon.buttons .button:first-child > .icon { - border-top-left-radius: 0.28571429rem; - border-bottom-left-radius: 0.28571429rem; -} - -.ui.labeled.icon.buttons .button:last-child > .icon { - border-top-right-radius: 0.28571429rem; - border-bottom-right-radius: 0.28571429rem; -} - -.ui.vertical.labeled.icon.buttons .button:first-child > .icon { - border-radius: 0; - border-top-left-radius: 0.28571429rem; -} - -.ui.vertical.labeled.icon.buttons .button:last-child > .icon { - border-radius: 0; - border-bottom-left-radius: 0.28571429rem; -} - -/* Loading Icon in Labeled Button */ - -.ui.labeled.icon.button > .loading.icon:before { - animation: loader 2s linear infinite; -} - -/*-------------- - Toggle - ---------------*/ - -/* Toggle (Modifies active state to give affordances) */ - -.ui.toggle.buttons .active.button, -.ui.buttons .button.toggle.active, -.ui.button.toggle.active { - background-color: #21BA45; - box-shadow: none; - text-shadow: none; - color: #FFFFFF; -} - -.ui.button.toggle.active:hover { - background-color: #16ab39; - text-shadow: none; - color: #FFFFFF; -} - -/*-------------- - Circular - ---------------*/ - -.ui.circular.button { - border-radius: 10em; -} - -.ui.circular.button > .icon { - width: 1em; - vertical-align: baseline; -} - -/*------------------- - Or Buttons - --------------------*/ - -.ui.buttons .or { - position: relative; - width: 0.3em; - height: 2.57142857em; - z-index: 3; -} - -.ui.buttons .or:before { - position: absolute; - text-align: center; - border-radius: 500rem; - content: 'or'; - top: 50%; - left: 50%; - background-color: #FFFFFF; - text-shadow: none; - margin-top: -0.89285714em; - margin-left: -0.89285714em; - width: 1.78571429em; - height: 1.78571429em; - line-height: 1.78571429em; - color: rgba(0, 0, 0, 0.4); - font-style: normal; - font-weight: 500; - box-shadow: 0 0 0 1px transparent inset; -} - -.ui.buttons .or[data-text]:before { - content: attr(data-text); -} - -/* Fluid Or */ - -.ui.fluid.buttons .or { - width: 0 !important; -} - -.ui.fluid.buttons .or:after { - display: none; -} - -/*------------------- - Fluid - --------------------*/ - -.ui.fluid.buttons, -.ui.fluid.button { - width: 100%; -} - -.ui.fluid.button { - display: block; -} - -.ui.two.buttons { - width: 100%; -} - -.ui.two.buttons > .button { - width: 50%; -} - -.ui.three.buttons { - width: 100%; -} - -.ui.three.buttons > .button { - width: 33.333%; -} - -.ui.four.buttons { - width: 100%; -} - -.ui.four.buttons > .button { - width: 25%; -} - -.ui.five.buttons { - width: 100%; -} - -.ui.five.buttons > .button { - width: 20%; -} - -.ui.six.buttons { - width: 100%; -} - -.ui.six.buttons > .button { - width: 16.666%; -} - -.ui.seven.buttons { - width: 100%; -} - -.ui.seven.buttons > .button { - width: 14.285%; -} - -.ui.eight.buttons { - width: 100%; -} - -.ui.eight.buttons > .button { - width: 12.5%; -} - -.ui.nine.buttons { - width: 100%; -} - -.ui.nine.buttons > .button { - width: 11.11%; -} - -.ui.ten.buttons { - width: 100%; -} - -.ui.ten.buttons > .button { - width: 10%; -} - -.ui.eleven.buttons { - width: 100%; -} - -.ui.eleven.buttons > .button { - width: 9.09%; -} - -.ui.twelve.buttons { - width: 100%; -} - -.ui.twelve.buttons > .button { - width: 8.3333%; -} - -/* Fluid Vertical Buttons */ - -.ui.fluid.vertical.buttons, -.ui.fluid.vertical.buttons > .button { - display: flex; - width: auto; - justify-content: center; -} - -.ui.two.vertical.buttons > .button { - height: 50%; -} - -.ui.three.vertical.buttons > .button { - height: 33.333%; -} - -.ui.four.vertical.buttons > .button { - height: 25%; -} - -.ui.five.vertical.buttons > .button { - height: 20%; -} - -.ui.six.vertical.buttons > .button { - height: 16.666%; -} - -.ui.seven.vertical.buttons > .button { - height: 14.285%; -} - -.ui.eight.vertical.buttons > .button { - height: 12.5%; -} - -.ui.nine.vertical.buttons > .button { - height: 11.11%; -} - -.ui.ten.vertical.buttons > .button { - height: 10%; -} - -.ui.eleven.vertical.buttons > .button { - height: 9.09%; -} - -.ui.twelve.vertical.buttons > .button { - height: 8.3333%; -} - -/*------------------- - Colors ---------------------*/ - -.ui.primary.buttons .button, -.ui.primary.button { - background-color: #2185D0; - color: #FFFFFF; - text-shadow: none; - background-image: none; -} - -.ui.primary.button { - box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset; -} - -.ui.primary.buttons .button:hover, -.ui.primary.button:hover { - background-color: #1678c2; - color: #FFFFFF; - text-shadow: none; -} - -.ui.primary.buttons .button:focus, -.ui.primary.button:focus { - background-color: #0d71bb; - color: #FFFFFF; - text-shadow: none; -} - -.ui.primary.buttons .button:active, -.ui.primary.button:active { - background-color: #1a69a4; - color: #FFFFFF; - text-shadow: none; -} - -.ui.primary.buttons .active.button, -.ui.primary.buttons .active.button:active, -.ui.primary.active.button, -.ui.primary.button .active.button:active { - background-color: #1279c6; - color: #FFFFFF; - text-shadow: none; -} - -/* Basic */ - -.ui.basic.primary.buttons .button, -.ui.basic.primary.button { - background: transparent; - box-shadow: 0 0 0 1px #2185D0 inset; - color: #2185D0; -} - -.ui.basic.primary.buttons .button:hover, -.ui.basic.primary.button:hover { - background: transparent; - box-shadow: 0 0 0 1px #1678c2 inset; - color: #1678c2; -} - -.ui.basic.primary.buttons .button:focus, -.ui.basic.primary.button:focus { - background: transparent; - box-shadow: 0 0 0 1px #0d71bb inset; - color: #1678c2; -} - -.ui.basic.primary.buttons .active.button, -.ui.basic.primary.active.button { - background: transparent; - box-shadow: 0 0 0 1px #1279c6 inset; - color: #1a69a4; -} - -.ui.basic.primary.buttons .button:active, -.ui.basic.primary.button:active { - box-shadow: 0 0 0 1px #1a69a4 inset; - color: #1a69a4; -} - -.ui.buttons:not(.vertical) > .basic.primary.button:not(:first-child) { - margin-left: -1px; -} - -.ui.secondary.buttons .button, -.ui.secondary.button { - background-color: #1B1C1D; - color: #FFFFFF; - text-shadow: none; - background-image: none; -} - -.ui.secondary.button { - box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset; -} - -.ui.secondary.buttons .button:hover, -.ui.secondary.button:hover { - background-color: #27292a; - color: #FFFFFF; - text-shadow: none; -} - -.ui.secondary.buttons .button:focus, -.ui.secondary.button:focus { - background-color: #2e3032; - color: #FFFFFF; - text-shadow: none; -} - -.ui.secondary.buttons .button:active, -.ui.secondary.button:active { - background-color: #343637; - color: #FFFFFF; - text-shadow: none; -} - -.ui.secondary.buttons .active.button, -.ui.secondary.buttons .active.button:active, -.ui.secondary.active.button, -.ui.secondary.button .active.button:active { - background-color: #27292a; - color: #FFFFFF; - text-shadow: none; -} - -/* Basic */ - -.ui.basic.secondary.buttons .button, -.ui.basic.secondary.button { - background: transparent; - box-shadow: 0 0 0 1px #1B1C1D inset; - color: #1B1C1D; -} - -.ui.basic.secondary.buttons .button:hover, -.ui.basic.secondary.button:hover { - background: transparent; - box-shadow: 0 0 0 1px #27292a inset; - color: #27292a; -} - -.ui.basic.secondary.buttons .button:focus, -.ui.basic.secondary.button:focus { - background: transparent; - box-shadow: 0 0 0 1px #2e3032 inset; - color: #27292a; -} - -.ui.basic.secondary.buttons .active.button, -.ui.basic.secondary.active.button { - background: transparent; - box-shadow: 0 0 0 1px #27292a inset; - color: #343637; -} - -.ui.basic.secondary.buttons .button:active, -.ui.basic.secondary.button:active { - box-shadow: 0 0 0 1px #343637 inset; - color: #343637; -} - -.ui.buttons:not(.vertical) > .basic.secondary.button:not(:first-child) { - margin-left: -1px; -} - -.ui.red.buttons .button, -.ui.red.button { - background-color: #DB2828; - color: #FFFFFF; - text-shadow: none; - background-image: none; -} - -.ui.red.button { - box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset; -} - -.ui.red.buttons .button:hover, -.ui.red.button:hover { - background-color: #d01919; - color: #FFFFFF; - text-shadow: none; -} - -.ui.red.buttons .button:focus, -.ui.red.button:focus { - background-color: #ca1010; - color: #FFFFFF; - text-shadow: none; -} - -.ui.red.buttons .button:active, -.ui.red.button:active { - background-color: #b21e1e; - color: #FFFFFF; - text-shadow: none; -} - -.ui.red.buttons .active.button, -.ui.red.buttons .active.button:active, -.ui.red.active.button, -.ui.red.button .active.button:active { - background-color: #d41515; - color: #FFFFFF; - text-shadow: none; -} - -/* Basic */ - -.ui.basic.red.buttons .button, -.ui.basic.red.button { - background: transparent; - box-shadow: 0 0 0 1px #DB2828 inset; - color: #DB2828; -} - -.ui.basic.red.buttons .button:hover, -.ui.basic.red.button:hover { - background: transparent; - box-shadow: 0 0 0 1px #d01919 inset; - color: #d01919; -} - -.ui.basic.red.buttons .button:focus, -.ui.basic.red.button:focus { - background: transparent; - box-shadow: 0 0 0 1px #ca1010 inset; - color: #d01919; -} - -.ui.basic.red.buttons .active.button, -.ui.basic.red.active.button { - background: transparent; - box-shadow: 0 0 0 1px #d41515 inset; - color: #b21e1e; -} - -.ui.basic.red.buttons .button:active, -.ui.basic.red.button:active { - box-shadow: 0 0 0 1px #b21e1e inset; - color: #b21e1e; -} - -.ui.buttons:not(.vertical) > .basic.red.button:not(:first-child) { - margin-left: -1px; -} - -.ui.orange.buttons .button, -.ui.orange.button { - background-color: #F2711C; - color: #FFFFFF; - text-shadow: none; - background-image: none; -} - -.ui.orange.button { - box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset; -} - -.ui.orange.buttons .button:hover, -.ui.orange.button:hover { - background-color: #f26202; - color: #FFFFFF; - text-shadow: none; -} - -.ui.orange.buttons .button:focus, -.ui.orange.button:focus { - background-color: #e55b00; - color: #FFFFFF; - text-shadow: none; -} - -.ui.orange.buttons .button:active, -.ui.orange.button:active { - background-color: #cf590c; - color: #FFFFFF; - text-shadow: none; -} - -.ui.orange.buttons .active.button, -.ui.orange.buttons .active.button:active, -.ui.orange.active.button, -.ui.orange.button .active.button:active { - background-color: #f56100; - color: #FFFFFF; - text-shadow: none; -} - -/* Basic */ - -.ui.basic.orange.buttons .button, -.ui.basic.orange.button { - background: transparent; - box-shadow: 0 0 0 1px #F2711C inset; - color: #F2711C; -} - -.ui.basic.orange.buttons .button:hover, -.ui.basic.orange.button:hover { - background: transparent; - box-shadow: 0 0 0 1px #f26202 inset; - color: #f26202; -} - -.ui.basic.orange.buttons .button:focus, -.ui.basic.orange.button:focus { - background: transparent; - box-shadow: 0 0 0 1px #e55b00 inset; - color: #f26202; -} - -.ui.basic.orange.buttons .active.button, -.ui.basic.orange.active.button { - background: transparent; - box-shadow: 0 0 0 1px #f56100 inset; - color: #cf590c; -} - -.ui.basic.orange.buttons .button:active, -.ui.basic.orange.button:active { - box-shadow: 0 0 0 1px #cf590c inset; - color: #cf590c; -} - -.ui.buttons:not(.vertical) > .basic.orange.button:not(:first-child) { - margin-left: -1px; -} - -.ui.yellow.buttons .button, -.ui.yellow.button { - background-color: #FBBD08; - color: #FFFFFF; - text-shadow: none; - background-image: none; -} - -.ui.yellow.button { - box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset; -} - -.ui.yellow.buttons .button:hover, -.ui.yellow.button:hover { - background-color: #eaae00; - color: #FFFFFF; - text-shadow: none; -} - -.ui.yellow.buttons .button:focus, -.ui.yellow.button:focus { - background-color: #daa300; - color: #FFFFFF; - text-shadow: none; -} - -.ui.yellow.buttons .button:active, -.ui.yellow.button:active { - background-color: #cd9903; - color: #FFFFFF; - text-shadow: none; -} - -.ui.yellow.buttons .active.button, -.ui.yellow.buttons .active.button:active, -.ui.yellow.active.button, -.ui.yellow.button .active.button:active { - background-color: #eaae00; - color: #FFFFFF; - text-shadow: none; -} - -/* Basic */ - -.ui.basic.yellow.buttons .button, -.ui.basic.yellow.button { - background: transparent; - box-shadow: 0 0 0 1px #FBBD08 inset; - color: #FBBD08; -} - -.ui.basic.yellow.buttons .button:hover, -.ui.basic.yellow.button:hover { - background: transparent; - box-shadow: 0 0 0 1px #eaae00 inset; - color: #eaae00; -} - -.ui.basic.yellow.buttons .button:focus, -.ui.basic.yellow.button:focus { - background: transparent; - box-shadow: 0 0 0 1px #daa300 inset; - color: #eaae00; -} - -.ui.basic.yellow.buttons .active.button, -.ui.basic.yellow.active.button { - background: transparent; - box-shadow: 0 0 0 1px #eaae00 inset; - color: #cd9903; -} - -.ui.basic.yellow.buttons .button:active, -.ui.basic.yellow.button:active { - box-shadow: 0 0 0 1px #cd9903 inset; - color: #cd9903; -} - -.ui.buttons:not(.vertical) > .basic.yellow.button:not(:first-child) { - margin-left: -1px; -} - -.ui.olive.buttons .button, -.ui.olive.button { - background-color: #B5CC18; - color: #FFFFFF; - text-shadow: none; - background-image: none; -} - -.ui.olive.button { - box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset; -} - -.ui.olive.buttons .button:hover, -.ui.olive.button:hover { - background-color: #a7bd0d; - color: #FFFFFF; - text-shadow: none; -} - -.ui.olive.buttons .button:focus, -.ui.olive.button:focus { - background-color: #a0b605; - color: #FFFFFF; - text-shadow: none; -} - -.ui.olive.buttons .button:active, -.ui.olive.button:active { - background-color: #8d9e13; - color: #FFFFFF; - text-shadow: none; -} - -.ui.olive.buttons .active.button, -.ui.olive.buttons .active.button:active, -.ui.olive.active.button, -.ui.olive.button .active.button:active { - background-color: #aac109; - color: #FFFFFF; - text-shadow: none; -} - -/* Basic */ - -.ui.basic.olive.buttons .button, -.ui.basic.olive.button { - background: transparent; - box-shadow: 0 0 0 1px #B5CC18 inset; - color: #B5CC18; -} - -.ui.basic.olive.buttons .button:hover, -.ui.basic.olive.button:hover { - background: transparent; - box-shadow: 0 0 0 1px #a7bd0d inset; - color: #a7bd0d; -} - -.ui.basic.olive.buttons .button:focus, -.ui.basic.olive.button:focus { - background: transparent; - box-shadow: 0 0 0 1px #a0b605 inset; - color: #a7bd0d; -} - -.ui.basic.olive.buttons .active.button, -.ui.basic.olive.active.button { - background: transparent; - box-shadow: 0 0 0 1px #aac109 inset; - color: #8d9e13; -} - -.ui.basic.olive.buttons .button:active, -.ui.basic.olive.button:active { - box-shadow: 0 0 0 1px #8d9e13 inset; - color: #8d9e13; -} - -.ui.buttons:not(.vertical) > .basic.olive.button:not(:first-child) { - margin-left: -1px; -} - -.ui.green.buttons .button, -.ui.green.button { - background-color: #21BA45; - color: #FFFFFF; - text-shadow: none; - background-image: none; -} - -.ui.green.button { - box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset; -} - -.ui.green.buttons .button:hover, -.ui.green.button:hover { - background-color: #16ab39; - color: #FFFFFF; - text-shadow: none; -} - -.ui.green.buttons .button:focus, -.ui.green.button:focus { - background-color: #0ea432; - color: #FFFFFF; - text-shadow: none; -} - -.ui.green.buttons .button:active, -.ui.green.button:active { - background-color: #198f35; - color: #FFFFFF; - text-shadow: none; -} - -.ui.green.buttons .active.button, -.ui.green.buttons .active.button:active, -.ui.green.active.button, -.ui.green.button .active.button:active { - background-color: #13ae38; - color: #FFFFFF; - text-shadow: none; -} - -/* Basic */ - -.ui.basic.green.buttons .button, -.ui.basic.green.button { - background: transparent; - box-shadow: 0 0 0 1px #21BA45 inset; - color: #21BA45; -} - -.ui.basic.green.buttons .button:hover, -.ui.basic.green.button:hover { - background: transparent; - box-shadow: 0 0 0 1px #16ab39 inset; - color: #16ab39; -} - -.ui.basic.green.buttons .button:focus, -.ui.basic.green.button:focus { - background: transparent; - box-shadow: 0 0 0 1px #0ea432 inset; - color: #16ab39; -} - -.ui.basic.green.buttons .active.button, -.ui.basic.green.active.button { - background: transparent; - box-shadow: 0 0 0 1px #13ae38 inset; - color: #198f35; -} - -.ui.basic.green.buttons .button:active, -.ui.basic.green.button:active { - box-shadow: 0 0 0 1px #198f35 inset; - color: #198f35; -} - -.ui.buttons:not(.vertical) > .basic.green.button:not(:first-child) { - margin-left: -1px; -} - -.ui.teal.buttons .button, -.ui.teal.button { - background-color: #00B5AD; - color: #FFFFFF; - text-shadow: none; - background-image: none; -} - -.ui.teal.button { - box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset; -} - -.ui.teal.buttons .button:hover, -.ui.teal.button:hover { - background-color: #009c95; - color: #FFFFFF; - text-shadow: none; -} - -.ui.teal.buttons .button:focus, -.ui.teal.button:focus { - background-color: #008c86; - color: #FFFFFF; - text-shadow: none; -} - -.ui.teal.buttons .button:active, -.ui.teal.button:active { - background-color: #00827c; - color: #FFFFFF; - text-shadow: none; -} - -.ui.teal.buttons .active.button, -.ui.teal.buttons .active.button:active, -.ui.teal.active.button, -.ui.teal.button .active.button:active { - background-color: #009c95; - color: #FFFFFF; - text-shadow: none; -} - -/* Basic */ - -.ui.basic.teal.buttons .button, -.ui.basic.teal.button { - background: transparent; - box-shadow: 0 0 0 1px #00B5AD inset; - color: #00B5AD; -} - -.ui.basic.teal.buttons .button:hover, -.ui.basic.teal.button:hover { - background: transparent; - box-shadow: 0 0 0 1px #009c95 inset; - color: #009c95; -} - -.ui.basic.teal.buttons .button:focus, -.ui.basic.teal.button:focus { - background: transparent; - box-shadow: 0 0 0 1px #008c86 inset; - color: #009c95; -} - -.ui.basic.teal.buttons .active.button, -.ui.basic.teal.active.button { - background: transparent; - box-shadow: 0 0 0 1px #009c95 inset; - color: #00827c; -} - -.ui.basic.teal.buttons .button:active, -.ui.basic.teal.button:active { - box-shadow: 0 0 0 1px #00827c inset; - color: #00827c; -} - -.ui.buttons:not(.vertical) > .basic.teal.button:not(:first-child) { - margin-left: -1px; -} - -.ui.blue.buttons .button, -.ui.blue.button { - background-color: #2185D0; - color: #FFFFFF; - text-shadow: none; - background-image: none; -} - -.ui.blue.button { - box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset; -} - -.ui.blue.buttons .button:hover, -.ui.blue.button:hover { - background-color: #1678c2; - color: #FFFFFF; - text-shadow: none; -} - -.ui.blue.buttons .button:focus, -.ui.blue.button:focus { - background-color: #0d71bb; - color: #FFFFFF; - text-shadow: none; -} - -.ui.blue.buttons .button:active, -.ui.blue.button:active { - background-color: #1a69a4; - color: #FFFFFF; - text-shadow: none; -} - -.ui.blue.buttons .active.button, -.ui.blue.buttons .active.button:active, -.ui.blue.active.button, -.ui.blue.button .active.button:active { - background-color: #1279c6; - color: #FFFFFF; - text-shadow: none; -} - -/* Basic */ - -.ui.basic.blue.buttons .button, -.ui.basic.blue.button { - background: transparent; - box-shadow: 0 0 0 1px #2185D0 inset; - color: #2185D0; -} - -.ui.basic.blue.buttons .button:hover, -.ui.basic.blue.button:hover { - background: transparent; - box-shadow: 0 0 0 1px #1678c2 inset; - color: #1678c2; -} - -.ui.basic.blue.buttons .button:focus, -.ui.basic.blue.button:focus { - background: transparent; - box-shadow: 0 0 0 1px #0d71bb inset; - color: #1678c2; -} - -.ui.basic.blue.buttons .active.button, -.ui.basic.blue.active.button { - background: transparent; - box-shadow: 0 0 0 1px #1279c6 inset; - color: #1a69a4; -} - -.ui.basic.blue.buttons .button:active, -.ui.basic.blue.button:active { - box-shadow: 0 0 0 1px #1a69a4 inset; - color: #1a69a4; -} - -.ui.buttons:not(.vertical) > .basic.blue.button:not(:first-child) { - margin-left: -1px; -} - -.ui.violet.buttons .button, -.ui.violet.button { - background-color: #6435C9; - color: #FFFFFF; - text-shadow: none; - background-image: none; -} - -.ui.violet.button { - box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset; -} - -.ui.violet.buttons .button:hover, -.ui.violet.button:hover { - background-color: #5829bb; - color: #FFFFFF; - text-shadow: none; -} - -.ui.violet.buttons .button:focus, -.ui.violet.button:focus { - background-color: #4f20b5; - color: #FFFFFF; - text-shadow: none; -} - -.ui.violet.buttons .button:active, -.ui.violet.button:active { - background-color: #502aa1; - color: #FFFFFF; - text-shadow: none; -} - -.ui.violet.buttons .active.button, -.ui.violet.buttons .active.button:active, -.ui.violet.active.button, -.ui.violet.button .active.button:active { - background-color: #5626bf; - color: #FFFFFF; - text-shadow: none; -} - -/* Basic */ - -.ui.basic.violet.buttons .button, -.ui.basic.violet.button { - background: transparent; - box-shadow: 0 0 0 1px #6435C9 inset; - color: #6435C9; -} - -.ui.basic.violet.buttons .button:hover, -.ui.basic.violet.button:hover { - background: transparent; - box-shadow: 0 0 0 1px #5829bb inset; - color: #5829bb; -} - -.ui.basic.violet.buttons .button:focus, -.ui.basic.violet.button:focus { - background: transparent; - box-shadow: 0 0 0 1px #4f20b5 inset; - color: #5829bb; -} - -.ui.basic.violet.buttons .active.button, -.ui.basic.violet.active.button { - background: transparent; - box-shadow: 0 0 0 1px #5626bf inset; - color: #502aa1; -} - -.ui.basic.violet.buttons .button:active, -.ui.basic.violet.button:active { - box-shadow: 0 0 0 1px #502aa1 inset; - color: #502aa1; -} - -.ui.buttons:not(.vertical) > .basic.violet.button:not(:first-child) { - margin-left: -1px; -} - -.ui.purple.buttons .button, -.ui.purple.button { - background-color: #A333C8; - color: #FFFFFF; - text-shadow: none; - background-image: none; -} - -.ui.purple.button { - box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset; -} - -.ui.purple.buttons .button:hover, -.ui.purple.button:hover { - background-color: #9627ba; - color: #FFFFFF; - text-shadow: none; -} - -.ui.purple.buttons .button:focus, -.ui.purple.button:focus { - background-color: #8f1eb4; - color: #FFFFFF; - text-shadow: none; -} - -.ui.purple.buttons .button:active, -.ui.purple.button:active { - background-color: #82299f; - color: #FFFFFF; - text-shadow: none; -} - -.ui.purple.buttons .active.button, -.ui.purple.buttons .active.button:active, -.ui.purple.active.button, -.ui.purple.button .active.button:active { - background-color: #9724be; - color: #FFFFFF; - text-shadow: none; -} - -/* Basic */ - -.ui.basic.purple.buttons .button, -.ui.basic.purple.button { - background: transparent; - box-shadow: 0 0 0 1px #A333C8 inset; - color: #A333C8; -} - -.ui.basic.purple.buttons .button:hover, -.ui.basic.purple.button:hover { - background: transparent; - box-shadow: 0 0 0 1px #9627ba inset; - color: #9627ba; -} - -.ui.basic.purple.buttons .button:focus, -.ui.basic.purple.button:focus { - background: transparent; - box-shadow: 0 0 0 1px #8f1eb4 inset; - color: #9627ba; -} - -.ui.basic.purple.buttons .active.button, -.ui.basic.purple.active.button { - background: transparent; - box-shadow: 0 0 0 1px #9724be inset; - color: #82299f; -} - -.ui.basic.purple.buttons .button:active, -.ui.basic.purple.button:active { - box-shadow: 0 0 0 1px #82299f inset; - color: #82299f; -} - -.ui.buttons:not(.vertical) > .basic.purple.button:not(:first-child) { - margin-left: -1px; -} - -.ui.pink.buttons .button, -.ui.pink.button { - background-color: #E03997; - color: #FFFFFF; - text-shadow: none; - background-image: none; -} - -.ui.pink.button { - box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset; -} - -.ui.pink.buttons .button:hover, -.ui.pink.button:hover { - background-color: #e61a8d; - color: #FFFFFF; - text-shadow: none; -} - -.ui.pink.buttons .button:focus, -.ui.pink.button:focus { - background-color: #e10f85; - color: #FFFFFF; - text-shadow: none; -} - -.ui.pink.buttons .button:active, -.ui.pink.button:active { - background-color: #c71f7e; - color: #FFFFFF; - text-shadow: none; -} - -.ui.pink.buttons .active.button, -.ui.pink.buttons .active.button:active, -.ui.pink.active.button, -.ui.pink.button .active.button:active { - background-color: #ea158d; - color: #FFFFFF; - text-shadow: none; -} - -/* Basic */ - -.ui.basic.pink.buttons .button, -.ui.basic.pink.button { - background: transparent; - box-shadow: 0 0 0 1px #E03997 inset; - color: #E03997; -} - -.ui.basic.pink.buttons .button:hover, -.ui.basic.pink.button:hover { - background: transparent; - box-shadow: 0 0 0 1px #e61a8d inset; - color: #e61a8d; -} - -.ui.basic.pink.buttons .button:focus, -.ui.basic.pink.button:focus { - background: transparent; - box-shadow: 0 0 0 1px #e10f85 inset; - color: #e61a8d; -} - -.ui.basic.pink.buttons .active.button, -.ui.basic.pink.active.button { - background: transparent; - box-shadow: 0 0 0 1px #ea158d inset; - color: #c71f7e; -} - -.ui.basic.pink.buttons .button:active, -.ui.basic.pink.button:active { - box-shadow: 0 0 0 1px #c71f7e inset; - color: #c71f7e; -} - -.ui.buttons:not(.vertical) > .basic.pink.button:not(:first-child) { - margin-left: -1px; -} - -.ui.brown.buttons .button, -.ui.brown.button { - background-color: #A5673F; - color: #FFFFFF; - text-shadow: none; - background-image: none; -} - -.ui.brown.button { - box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset; -} - -.ui.brown.buttons .button:hover, -.ui.brown.button:hover { - background-color: #975b33; - color: #FFFFFF; - text-shadow: none; -} - -.ui.brown.buttons .button:focus, -.ui.brown.button:focus { - background-color: #90532b; - color: #FFFFFF; - text-shadow: none; -} - -.ui.brown.buttons .button:active, -.ui.brown.button:active { - background-color: #805031; - color: #FFFFFF; - text-shadow: none; -} - -.ui.brown.buttons .active.button, -.ui.brown.buttons .active.button:active, -.ui.brown.active.button, -.ui.brown.button .active.button:active { - background-color: #995a31; - color: #FFFFFF; - text-shadow: none; -} - -/* Basic */ - -.ui.basic.brown.buttons .button, -.ui.basic.brown.button { - background: transparent; - box-shadow: 0 0 0 1px #A5673F inset; - color: #A5673F; -} - -.ui.basic.brown.buttons .button:hover, -.ui.basic.brown.button:hover { - background: transparent; - box-shadow: 0 0 0 1px #975b33 inset; - color: #975b33; -} - -.ui.basic.brown.buttons .button:focus, -.ui.basic.brown.button:focus { - background: transparent; - box-shadow: 0 0 0 1px #90532b inset; - color: #975b33; -} - -.ui.basic.brown.buttons .active.button, -.ui.basic.brown.active.button { - background: transparent; - box-shadow: 0 0 0 1px #995a31 inset; - color: #805031; -} - -.ui.basic.brown.buttons .button:active, -.ui.basic.brown.button:active { - box-shadow: 0 0 0 1px #805031 inset; - color: #805031; -} - -.ui.buttons:not(.vertical) > .basic.brown.button:not(:first-child) { - margin-left: -1px; -} - -.ui.grey.buttons .button, -.ui.grey.button { - background-color: #767676; - color: #FFFFFF; - text-shadow: none; - background-image: none; -} - -.ui.grey.button { - box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset; -} - -.ui.grey.buttons .button:hover, -.ui.grey.button:hover { - background-color: #838383; - color: #FFFFFF; - text-shadow: none; -} - -.ui.grey.buttons .button:focus, -.ui.grey.button:focus { - background-color: #8a8a8a; - color: #FFFFFF; - text-shadow: none; -} - -.ui.grey.buttons .button:active, -.ui.grey.button:active { - background-color: #909090; - color: #FFFFFF; - text-shadow: none; -} - -.ui.grey.buttons .active.button, -.ui.grey.buttons .active.button:active, -.ui.grey.active.button, -.ui.grey.button .active.button:active { - background-color: #696969; - color: #FFFFFF; - text-shadow: none; -} - -/* Basic */ - -.ui.basic.grey.buttons .button, -.ui.basic.grey.button { - background: transparent; - box-shadow: 0 0 0 1px #767676 inset; - color: #767676; -} - -.ui.basic.grey.buttons .button:hover, -.ui.basic.grey.button:hover { - background: transparent; - box-shadow: 0 0 0 1px #838383 inset; - color: #838383; -} - -.ui.basic.grey.buttons .button:focus, -.ui.basic.grey.button:focus { - background: transparent; - box-shadow: 0 0 0 1px #8a8a8a inset; - color: #838383; -} - -.ui.basic.grey.buttons .active.button, -.ui.basic.grey.active.button { - background: transparent; - box-shadow: 0 0 0 1px #696969 inset; - color: #909090; -} - -.ui.basic.grey.buttons .button:active, -.ui.basic.grey.button:active { - box-shadow: 0 0 0 1px #909090 inset; - color: #909090; -} - -.ui.buttons:not(.vertical) > .basic.grey.button:not(:first-child) { - margin-left: -1px; -} - -.ui.black.buttons .button, -.ui.black.button { - background-color: #1B1C1D; - color: #FFFFFF; - text-shadow: none; - background-image: none; -} - -.ui.black.button { - box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset; -} - -.ui.black.buttons .button:hover, -.ui.black.button:hover { - background-color: #27292a; - color: #FFFFFF; - text-shadow: none; -} - -.ui.black.buttons .button:focus, -.ui.black.button:focus { - background-color: #2f3032; - color: #FFFFFF; - text-shadow: none; -} - -.ui.black.buttons .button:active, -.ui.black.button:active { - background-color: #343637; - color: #FFFFFF; - text-shadow: none; -} - -.ui.black.buttons .active.button, -.ui.black.buttons .active.button:active, -.ui.black.active.button, -.ui.black.button .active.button:active { - background-color: #0f0f10; - color: #FFFFFF; - text-shadow: none; -} - -/* Basic */ - -.ui.basic.black.buttons .button, -.ui.basic.black.button { - background: transparent; - box-shadow: 0 0 0 1px #1B1C1D inset; - color: #1B1C1D; -} - -.ui.basic.black.buttons .button:hover, -.ui.basic.black.button:hover { - background: transparent; - box-shadow: 0 0 0 1px #27292a inset; - color: #27292a; -} - -.ui.basic.black.buttons .button:focus, -.ui.basic.black.button:focus { - background: transparent; - box-shadow: 0 0 0 1px #2f3032 inset; - color: #27292a; -} - -.ui.basic.black.buttons .active.button, -.ui.basic.black.active.button { - background: transparent; - box-shadow: 0 0 0 1px #0f0f10 inset; - color: #343637; -} - -.ui.basic.black.buttons .button:active, -.ui.basic.black.button:active { - box-shadow: 0 0 0 1px #343637 inset; - color: #343637; -} - -.ui.buttons:not(.vertical) > .basic.black.button:not(:first-child) { - margin-left: -1px; -} - -/*--------------- - Positive -----------------*/ - -/* Standard */ - -.ui.positive.buttons .button, -.ui.positive.button { - background-color: #21BA45; - color: #FFFFFF; - text-shadow: none; - background-image: none; -} - -.ui.positive.button { - box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset; -} - -.ui.positive.buttons .button:hover, -.ui.positive.button:hover { - background-color: #16ab39; - color: #FFFFFF; - text-shadow: none; -} - -.ui.positive.buttons .button:focus, -.ui.positive.button:focus { - background-color: #0ea432; - color: #FFFFFF; - text-shadow: none; -} - -.ui.positive.buttons .button:active, -.ui.positive.button:active { - background-color: #198f35; - color: #FFFFFF; - text-shadow: none; -} - -.ui.positive.buttons .active.button, -.ui.positive.buttons .active.button:active, -.ui.positive.active.button, -.ui.positive.button .active.button:active { - background-color: #13ae38; - color: #FFFFFF; - text-shadow: none; -} - -/* Basic */ - -.ui.basic.positive.buttons .button, -.ui.basic.positive.button { - background: transparent; - box-shadow: 0 0 0 1px #21BA45 inset; - color: #21BA45; -} - -.ui.basic.positive.buttons .button:hover, -.ui.basic.positive.button:hover { - background: transparent; - box-shadow: 0 0 0 1px #16ab39 inset; - color: #16ab39; -} - -.ui.basic.positive.buttons .button:focus, -.ui.basic.positive.button:focus { - background: transparent; - box-shadow: 0 0 0 1px #0ea432 inset; - color: #16ab39; -} - -.ui.basic.positive.buttons .active.button, -.ui.basic.positive.active.button { - background: transparent; - box-shadow: 0 0 0 1px #13ae38 inset; - color: #198f35; -} - -.ui.basic.positive.buttons .button:active, -.ui.basic.positive.button:active { - box-shadow: 0 0 0 1px #198f35 inset; - color: #198f35; -} - -.ui.buttons:not(.vertical) > .basic.positive.button:not(:first-child) { - margin-left: -1px; -} - -/*--------------- - Negative -----------------*/ - -/* Standard */ - -.ui.negative.buttons .button, -.ui.negative.button { - background-color: #DB2828; - color: #FFFFFF; - text-shadow: none; - background-image: none; -} - -.ui.negative.button { - box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset; -} - -.ui.negative.buttons .button:hover, -.ui.negative.button:hover { - background-color: #d01919; - color: #FFFFFF; - text-shadow: none; -} - -.ui.negative.buttons .button:focus, -.ui.negative.button:focus { - background-color: #ca1010; - color: #FFFFFF; - text-shadow: none; -} - -.ui.negative.buttons .button:active, -.ui.negative.button:active { - background-color: #b21e1e; - color: #FFFFFF; - text-shadow: none; -} - -.ui.negative.buttons .active.button, -.ui.negative.buttons .active.button:active, -.ui.negative.active.button, -.ui.negative.button .active.button:active { - background-color: #d41515; - color: #FFFFFF; - text-shadow: none; -} - -/* Basic */ - -.ui.basic.negative.buttons .button, -.ui.basic.negative.button { - background: transparent; - box-shadow: 0 0 0 1px #DB2828 inset; - color: #DB2828; -} - -.ui.basic.negative.buttons .button:hover, -.ui.basic.negative.button:hover { - background: transparent; - box-shadow: 0 0 0 1px #d01919 inset; - color: #d01919; -} - -.ui.basic.negative.buttons .button:focus, -.ui.basic.negative.button:focus { - background: transparent; - box-shadow: 0 0 0 1px #ca1010 inset; - color: #d01919; -} - -.ui.basic.negative.buttons .active.button, -.ui.basic.negative.active.button { - background: transparent; - box-shadow: 0 0 0 1px #d41515 inset; - color: #b21e1e; -} - -.ui.basic.negative.buttons .button:active, -.ui.basic.negative.button:active { - box-shadow: 0 0 0 1px #b21e1e inset; - color: #b21e1e; -} - -.ui.buttons:not(.vertical) > .basic.negative.button:not(:first-child) { - margin-left: -1px; -} - -/******************************* - Groups - *******************************/ - -.ui.buttons { - display: inline-flex; - flex-direction: row; - font-size: 0; - vertical-align: baseline; - margin: 0 0.25em 0 0; -} - -.ui.buttons:not(.basic):not(.inverted) { - box-shadow: none; -} - -/* Clearfix */ - -.ui.buttons:after { - content: "."; - display: block; - height: 0; - clear: both; - visibility: hidden; -} - -/* Standard Group */ - -.ui.buttons .button { - flex: 1 0 auto; - border-radius: 0; - margin: 0 0 0 0; -} - -.ui.buttons:not(.basic):not(.inverted) > .button:not(.basic):not(.inverted) { - box-shadow: 0 0 0 1px transparent inset, 0 0 0 0 rgba(34, 36, 38, 0.15) inset; -} - -.ui.buttons .button:first-child { - border-left: none; - margin-left: 0; - border-top-left-radius: 0.28571429rem; - border-bottom-left-radius: 0.28571429rem; -} - -.ui.buttons .button:last-child { - border-top-right-radius: 0.28571429rem; - border-bottom-right-radius: 0.28571429rem; -} - -/* Vertical Style */ - -.ui.vertical.buttons { - display: inline-flex; - flex-direction: column; -} - -.ui.vertical.buttons .button { - display: block; - float: none; - width: 100%; - margin: 0 0 0 0; - box-shadow: none; - border-radius: 0; -} - -.ui.vertical.buttons .button:first-child { - border-top-left-radius: 0.28571429rem; - border-top-right-radius: 0.28571429rem; -} - -.ui.vertical.buttons .button:last-child { - margin-bottom: 0; - border-bottom-left-radius: 0.28571429rem; - border-bottom-right-radius: 0.28571429rem; -} - -.ui.vertical.buttons .button:only-child { - border-radius: 0.28571429rem; -} - -/******************************* - Theme Overrides -*******************************/ - -/******************************* - Site Overrides -*******************************/ /*! * # Fomantic-UI - Dimmer * http://github.com/fomantic/Fomantic-UI/ diff --git a/web_src/fomantic/semantic.json b/web_src/fomantic/semantic.json index 5b3a480d5343..489ca7b9b79c 100644 --- a/web_src/fomantic/semantic.json +++ b/web_src/fomantic/semantic.json @@ -22,7 +22,6 @@ "admin": false, "components": [ "api", - "button", "dimmer", "dropdown", "form", diff --git a/web_src/js/components/PullRequestMergeForm.vue b/web_src/js/components/PullRequestMergeForm.vue index bd0901a7b595..9efa8840ac47 100644 --- a/web_src/js/components/PullRequestMergeForm.vue +++ b/web_src/js/components/PullRequestMergeForm.vue @@ -138,7 +138,7 @@ export default {
    -
    +