diff --git a/cmd/admin.go b/cmd/admin.go index a01274b90e..dbd48e5727 100644 --- a/cmd/admin.go +++ b/cmd/admin.go @@ -134,7 +134,7 @@ func runRepoSyncReleases(ctx context.Context, _ *cli.Command) error { } log.Trace(" currentNumReleases is %d, running SyncReleasesWithTags", oldnum) - if err = repo_module.SyncReleasesWithTags(ctx, repo, gitRepo); err != nil { + if _, err = repo_module.SyncReleasesWithTags(ctx, repo, gitRepo); err != nil { log.Warn(" SyncReleasesWithTags: %v", err) gitRepo.Close() continue diff --git a/eslint.config.ts b/eslint.config.ts index 8d5b435397..5815702c89 100644 --- a/eslint.config.ts +++ b/eslint.config.ts @@ -342,7 +342,7 @@ export default defineConfig([ 'import-x/first': [2], 'import-x/group-exports': [0], 'import-x/max-dependencies': [0], - 'import-x/named': [2], + 'import-x/named': [0], 'import-x/namespace': [0], 'import-x/newline-after-import': [0], 'import-x/no-absolute-path': [0], @@ -987,7 +987,7 @@ export default defineConfig([ 'vitest/require-to-throw-message': [0], 'vitest/require-top-level-describe': [0], 'vitest/valid-describe-callback': [2], - 'vitest/valid-expect': [2], + 'vitest/valid-expect': [2, {maxArgs: 2}], 'vitest/valid-title': [2], }, }, diff --git a/go.mod b/go.mod index ffe3332f10..0d47b731ea 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ godebug x509negativeserial=1 require ( code.gitea.io/actions-proto-go v0.4.1 - code.gitea.io/sdk/gitea v0.22.0 + code.gitea.io/sdk/gitea v0.23.2 codeberg.org/gusted/mcaptcha v0.0.0-20220723083913-4f3072e1d570 connectrpc.com/connect v1.19.1 gitea.com/go-chi/binding v0.0.0-20240430071103-39a851e106ed diff --git a/go.sum b/go.sum index 85f392302b..b10e259c91 100644 --- a/go.sum +++ b/go.sum @@ -20,8 +20,8 @@ code.gitea.io/actions-proto-go v0.4.1 h1:l0EYhjsgpUe/1VABo2eK7zcoNX2W44WOnb0MSLr code.gitea.io/actions-proto-go v0.4.1/go.mod h1:mn7Wkqz6JbnTOHQpot3yDeHx+O5C9EGhMEE+htvHBas= code.gitea.io/gitea-vet v0.2.3 h1:gdFmm6WOTM65rE8FUBTRzeQZYzXePKSSB1+r574hWwI= code.gitea.io/gitea-vet v0.2.3/go.mod h1:zcNbT/aJEmivCAhfmkHOlT645KNOf9W2KnkLgFjGGfE= -code.gitea.io/sdk/gitea v0.22.0 h1:HCKq7bX/HQ85Nw7c/HAhWgRye+vBp5nQOE8Md1+9Ef0= -code.gitea.io/sdk/gitea v0.22.0/go.mod h1:yyF5+GhljqvA30sRDreoyHILruNiy4ASufugzYg0VHM= +code.gitea.io/sdk/gitea v0.23.2 h1:iJB1FDmLegwfwjX8gotBDHdPSbk/ZR8V9VmEJaVsJYg= +code.gitea.io/sdk/gitea v0.23.2/go.mod h1:yyF5+GhljqvA30sRDreoyHILruNiy4ASufugzYg0VHM= codeberg.org/gusted/mcaptcha v0.0.0-20220723083913-4f3072e1d570 h1:TXbikPqa7YRtfU9vS6QJBg77pUvbEb6StRdZO8t1bEY= codeberg.org/gusted/mcaptcha v0.0.0-20220723083913-4f3072e1d570/go.mod h1:IIAjsijsd8q1isWX8MACefDEgTQslQ4stk2AeeTt3kM= connectrpc.com/connect v1.19.1 h1:R5M57z05+90EfEvCY1b7hBxDVOUl45PrtXtAV2fOC14= diff --git a/modules/highlight/highlight.go b/modules/highlight/highlight.go index 68f523c6ca..fc8699829c 100644 --- a/modules/highlight/highlight.go +++ b/modules/highlight/highlight.go @@ -98,6 +98,10 @@ func getChromaLexerByLanguage(fileName, lang string) chroma.Lexer { lang = "C++" } } + if lang == "" && util.AsciiEqualFold(ext, ".sql") { + // there is a bug when using MySQL lexer: "--\nSELECT", the second line will be rendered as comment incorrectly + lang = "SQL" + } // lexers.Get is slow if the language name can't be matched directly: it does extra "Match" call to iterate all lexers return lexers.Get(lang) } diff --git a/modules/highlight/highlight_test.go b/modules/highlight/highlight_test.go index f4bdedb2a0..69aff07b04 100644 --- a/modules/highlight/highlight_test.go +++ b/modules/highlight/highlight_test.go @@ -108,6 +108,12 @@ c=2 ), lexerName: "Python", }, + { + name: "test.sql", + code: "--\nSELECT", + want: []template.HTML{"--\n", `SELECT`}, + lexerName: "SQL", + }, } for _, tt := range tests { diff --git a/modules/repository/branch.go b/modules/repository/branch.go index 30aa0a6e85..0a8f7cc464 100644 --- a/modules/repository/branch.go +++ b/modules/repository/branch.go @@ -17,6 +17,13 @@ import ( "code.gitea.io/gitea/modules/timeutil" ) +// SyncResult describes a reference update detected during sync. +type SyncResult struct { + RefName git.RefName + OldCommitID string + NewCommitID string +} + // SyncRepoBranches synchronizes branch table with repository branches func SyncRepoBranches(ctx context.Context, repoID, doerID int64) (int64, error) { repo, err := repo_model.GetRepositoryByID(ctx, repoID) @@ -33,18 +40,19 @@ func SyncRepoBranches(ctx context.Context, repoID, doerID int64) (int64, error) } defer gitRepo.Close() - return SyncRepoBranchesWithRepo(ctx, repo, gitRepo, doerID) + count, _, err := SyncRepoBranchesWithRepo(ctx, repo, gitRepo, doerID) + return count, err } -func SyncRepoBranchesWithRepo(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository, doerID int64) (int64, error) { +func SyncRepoBranchesWithRepo(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository, doerID int64) (int64, []*SyncResult, error) { objFmt, err := gitRepo.GetObjectFormat() if err != nil { - return 0, fmt.Errorf("GetObjectFormat: %w", err) + return 0, nil, fmt.Errorf("GetObjectFormat: %w", err) } if objFmt.Name() != repo.ObjectFormatName { repo.ObjectFormatName = objFmt.Name() if err = repo_model.UpdateRepositoryColsWithAutoTime(ctx, repo, "object_format_name"); err != nil { - return 0, fmt.Errorf("UpdateRepositoryColsWithAutoTime: %w", err) + return 0, nil, fmt.Errorf("UpdateRepositoryColsWithAutoTime: %w", err) } } @@ -52,7 +60,7 @@ func SyncRepoBranchesWithRepo(ctx context.Context, repo *repo_model.Repository, { branches, _, err := gitRepo.GetBranchNames(0, 0) if err != nil { - return 0, err + return 0, nil, err } log.Trace("SyncRepoBranches[%s]: branches[%d]: %v", repo.FullName(), len(branches), branches) for _, branch := range branches { @@ -67,7 +75,7 @@ func SyncRepoBranchesWithRepo(ctx context.Context, repo *repo_model.Repository, RepoID: repo.ID, }) if err != nil { - return 0, err + return 0, nil, err } for _, branch := range branches { dbBranches[branch.Name] = branch @@ -77,11 +85,12 @@ func SyncRepoBranchesWithRepo(ctx context.Context, repo *repo_model.Repository, var toAdd []*git_model.Branch var toUpdate []*git_model.Branch var toRemove []int64 + var syncResults []*SyncResult for branch := range allBranches { dbb := dbBranches[branch] commit, err := gitRepo.GetBranchCommit(branch) if err != nil { - return 0, err + return 0, nil, err } if dbb == nil { toAdd = append(toAdd, &git_model.Branch{ @@ -92,7 +101,12 @@ func SyncRepoBranchesWithRepo(ctx context.Context, repo *repo_model.Repository, PusherID: doerID, CommitTime: timeutil.TimeStamp(commit.Committer.When.Unix()), }) - } else if commit.ID.String() != dbb.CommitID { + syncResults = append(syncResults, &SyncResult{ + RefName: git.RefNameFromBranch(branch), + OldCommitID: "", + NewCommitID: commit.ID.String(), + }) + } else if commit.ID.String() != dbb.CommitID || dbb.IsDeleted { toUpdate = append(toUpdate, &git_model.Branch{ ID: dbb.ID, RepoID: repo.ID, @@ -102,19 +116,29 @@ func SyncRepoBranchesWithRepo(ctx context.Context, repo *repo_model.Repository, PusherID: doerID, CommitTime: timeutil.TimeStamp(commit.Committer.When.Unix()), }) + syncResults = append(syncResults, &SyncResult{ + RefName: git.RefNameFromBranch(branch), + OldCommitID: dbb.CommitID, + NewCommitID: commit.ID.String(), + }) } } for _, dbBranch := range dbBranches { if !allBranches.Contains(dbBranch.Name) && !dbBranch.IsDeleted { toRemove = append(toRemove, dbBranch.ID) + syncResults = append(syncResults, &SyncResult{ + RefName: git.RefNameFromBranch(dbBranch.Name), + OldCommitID: dbBranch.CommitID, + NewCommitID: "", + }) } } log.Trace("SyncRepoBranches[%s]: toAdd: %v, toUpdate: %v, toRemove: %v", repo.FullName(), toAdd, toUpdate, toRemove) if len(toAdd) == 0 && len(toRemove) == 0 && len(toUpdate) == 0 { - return int64(len(allBranches)), nil + return int64(len(allBranches)), syncResults, nil } if err := db.WithTx(ctx, func(ctx context.Context) error { @@ -140,7 +164,7 @@ func SyncRepoBranchesWithRepo(ctx context.Context, repo *repo_model.Repository, return nil }); err != nil { - return 0, err + return 0, nil, err } - return int64(len(allBranches)), nil + return int64(len(allBranches)), syncResults, nil } diff --git a/modules/repository/repo.go b/modules/repository/repo.go index 9013590247..76125f5e61 100644 --- a/modules/repository/repo.go +++ b/modules/repository/repo.go @@ -53,7 +53,8 @@ func SyncRepoTags(ctx context.Context, repoID int64) error { } defer gitRepo.Close() - return SyncReleasesWithTags(ctx, repo, gitRepo) + _, err = SyncReleasesWithTags(ctx, repo, gitRepo) + return err } // StoreMissingLfsObjectsInRepository downloads missing LFS objects @@ -178,13 +179,14 @@ func (shortRelease) TableName() string { // upstream. Hence, after each sync we want the release set to be // identical to the upstream tag set. This is much more efficient for // repositories like https://github.com/vim/vim (with over 13000 tags). -func SyncReleasesWithTags(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository) error { +func SyncReleasesWithTags(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository) ([]*SyncResult, error) { log.Debug("SyncReleasesWithTags: in Repo[%d:%s/%s]", repo.ID, repo.OwnerName, repo.Name) tags, _, err := gitRepo.GetTagInfos(0, 0) if err != nil { - return fmt.Errorf("unable to GetTagInfos in pull-mirror Repo[%d:%s/%s]: %w", repo.ID, repo.OwnerName, repo.Name, err) + return nil, fmt.Errorf("unable to GetTagInfos in pull-mirror Repo[%d:%s/%s]: %w", repo.ID, repo.OwnerName, repo.Name, err) } var added, deleted, updated int + var syncResults []*SyncResult err = db.WithTx(ctx, func(ctx context.Context) error { dbReleases, err := db.Find[shortRelease](ctx, repo_model.FindReleasesOptions{ RepoID: repo.ID, @@ -195,7 +197,45 @@ func SyncReleasesWithTags(ctx context.Context, repo *repo_model.Repository, gitR return fmt.Errorf("unable to FindReleases in pull-mirror Repo[%d:%s/%s]: %w", repo.ID, repo.OwnerName, repo.Name, err) } + dbReleasesByID := make(map[int64]*shortRelease, len(dbReleases)) + dbReleasesByTag := make(map[string]*shortRelease, len(dbReleases)) + for _, release := range dbReleases { + dbReleasesByID[release.ID] = release + dbReleasesByTag[release.TagName] = release + } + inserts, deletes, updates := calcSync(tags, dbReleases) + syncResults = make([]*SyncResult, 0, len(inserts)+len(deletes)+len(updates)) + for _, tag := range inserts { + syncResults = append(syncResults, &SyncResult{ + RefName: git.RefNameFromTag(tag.Name), + OldCommitID: "", + NewCommitID: tag.Object.String(), + }) + } + for _, deleteID := range deletes { + release := dbReleasesByID[deleteID] + if release == nil { + continue + } + syncResults = append(syncResults, &SyncResult{ + RefName: git.RefNameFromTag(release.TagName), + OldCommitID: release.Sha1, + NewCommitID: "", + }) + } + for _, tag := range updates { + release := dbReleasesByTag[tag.Name] + oldSha := "" + if release != nil { + oldSha = release.Sha1 + } + syncResults = append(syncResults, &SyncResult{ + RefName: git.RefNameFromTag(tag.Name), + OldCommitID: oldSha, + NewCommitID: tag.Object.String(), + }) + } // // make release set identical to upstream tags // @@ -238,11 +278,11 @@ func SyncReleasesWithTags(ctx context.Context, repo *repo_model.Repository, gitR return nil }) if err != nil { - return fmt.Errorf("unable to rebuild release table for pull-mirror Repo[%d:%s/%s]: %w", repo.ID, repo.OwnerName, repo.Name, err) + return nil, fmt.Errorf("unable to rebuild release table for pull-mirror Repo[%d:%s/%s]: %w", repo.ID, repo.OwnerName, repo.Name, err) } log.Trace("SyncReleasesWithTags: %d tags added, %d tags deleted, %d tags updated", added, deleted, updated) - return nil + return syncResults, nil } func calcSync(destTags []*git.Tag, dbTags []*shortRelease) ([]*git.Tag, []int64, []*git.Tag) { diff --git a/options/locale/locale_ga-IE.json b/options/locale/locale_ga-IE.json index 8740c5d430..14123c5002 100644 --- a/options/locale/locale_ga-IE.json +++ b/options/locale/locale_ga-IE.json @@ -2124,6 +2124,8 @@ "repo.settings.pulls.ignore_whitespace": "Déan neamhaird de spás bán le haghaidh coinbhleachtaí", "repo.settings.pulls.enable_autodetect_manual_merge": "Cumasaigh cumasc láimhe autodetector (Nóta: I roinnt cásanna speisialta, is féidir míbhreithiúnais tarlú)", "repo.settings.pulls.allow_rebase_update": "Cumasaigh brainse iarratais tarraingthe a nuashonrú trí athbhunú", + "repo.settings.pulls.default_target_branch": "Brainse sprice réamhshocraithe le haghaidh iarratais tarraingthe nua", + "repo.settings.pulls.default_target_branch_default": "Brainse réamhshocraithe (%s)", "repo.settings.pulls.default_delete_branch_after_merge": "Scrios brainse an iarratais tarraingthe tar éis cumasc de réir réamhshocraithe", "repo.settings.pulls.default_allow_edits_from_maintainers": "Ceadaigh eagarthóirí ó chothabhálaí de réir réamhshocraithe", "repo.settings.releases_desc": "Cumasaigh Eisiúintí Stórais", @@ -2436,9 +2438,10 @@ "repo.settings.block_outdated_branch_desc": "Ní bheidh cumasc indéanta nuair a bhíonn ceannbhrainse taobh thiar de bhronnbhrainse.", "repo.settings.block_admin_merge_override": "Ní mór do riarthóirí rialacha cosanta brainse a leanúint", "repo.settings.block_admin_merge_override_desc": "Ní mór do riarthóirí rialacha cosanta brainse a leanúint agus ní féidir leo iad a sheachaint.", - "repo.settings.default_branch_desc": "Roghnaigh brainse stóras réamhshocraithe le haghaidh iarratas tarraingte agus geallann an cód:", + "repo.settings.default_branch_desc": "Roghnaigh brainse réamhshocraithe le haghaidh tiomnuithe cóid.", + "repo.settings.default_target_branch_desc": "Is féidir le hiarratais tarraingthe brainse sprice réamhshocraithe difriúil a úsáid má tá sé socraithe sa rannán Iarratais Tarraingthe de na Socruithe Ardleibhéil Stórála.", "repo.settings.merge_style_desc": "Stíleanna Cumaisc", - "repo.settings.default_merge_style_desc": "Stíl Cumaisc Réamhshocraithe", + "repo.settings.default_merge_style_desc": "Stíl chumasc réamhshocraithe", "repo.settings.choose_branch": "Roghnaigh brainse…", "repo.settings.no_protected_branch": "Níl aon bhrainsí cosanta ann.", "repo.settings.edit_protected_branch": "Cuir in eagar", @@ -2650,7 +2653,7 @@ "repo.branch.restore_success": "Tá brainse \"%s\" curtha ar ais.", "repo.branch.restore_failed": "Theip ar chur ar ais brainse \"%s\".", "repo.branch.protected_deletion_failed": "Tá brainse \"%s\" cosanta. Ní féidir é a scriosadh.", - "repo.branch.default_deletion_failed": "Is é brainse \"%s\" an brainse réamhshocraithe. Ní féidir é a scriosadh.", + "repo.branch.default_deletion_failed": "Is é brainse \"%s\" an brainse sprioc réamhshocraithe nó an brainse sprioc don iarratas tarraingthe. Ní féidir é a scriosadh.", "repo.branch.default_branch_not_exist": "Níl an brainse réamhshocraithe \"%s\" ann.", "repo.branch.restore": "Athchóirigh Brainse \"%s\"", "repo.branch.download": "Brainse Íosluchtaithe \"%s\"", diff --git a/package.json b/package.json index 7b33258759..a01ece0379 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "type": "module", - "packageManager": "pnpm@10.28.2", + "packageManager": "pnpm@10.29.2", "engines": { "node": ">= 22.6.0", "pnpm": ">= 10.0.0" @@ -26,7 +26,7 @@ "chart.js": "4.5.1", "chartjs-adapter-dayjs-4": "1.0.4", "chartjs-plugin-zoom": "2.2.0", - "clippie": "4.1.9", + "clippie": "4.1.10", "compare-versions": "6.1.1", "cropperjs": "1.6.2", "css-loader": "7.1.3", @@ -58,19 +58,19 @@ "tributejs": "5.1.3", "uint8-to-base64": "0.2.1", "vanilla-colorful": "0.7.2", - "vue": "3.5.27", + "vue": "3.5.28", "vue-bar-graph": "2.2.0", "vue-chartjs": "5.3.3", "vue-loader": "17.4.2", - "webpack": "5.104.1", + "webpack": "5.105.0", "webpack-cli": "6.0.1", "wrap-ansi": "9.0.2" }, "devDependencies": { "@eslint-community/eslint-plugin-eslint-comments": "4.6.0", "@eslint/json": "0.14.0", - "@playwright/test": "1.58.1", - "@stylistic/eslint-plugin": "5.7.1", + "@playwright/test": "1.58.2", + "@stylistic/eslint-plugin": "5.8.0", "@stylistic/stylelint-plugin": "5.0.1", "@types/codemirror": "5.60.17", "@types/dropzone": "5.7.9", @@ -83,9 +83,9 @@ "@types/throttle-debounce": "5.0.2", "@types/tinycolor2": "1.4.6", "@types/toastify-js": "1.12.4", - "@typescript-eslint/parser": "8.54.0", - "@vitejs/plugin-vue": "6.0.3", - "@vitest/eslint-plugin": "1.6.6", + "@typescript-eslint/parser": "8.55.0", + "@vitejs/plugin-vue": "6.0.4", + "@vitest/eslint-plugin": "1.6.7", "eslint": "9.39.2", "eslint-import-resolver-typescript": "4.4.4", "eslint-plugin-array-func": "5.1.0", @@ -97,25 +97,25 @@ "eslint-plugin-unicorn": "62.0.0", "eslint-plugin-vue": "10.7.0", "eslint-plugin-vue-scoped-css": "2.12.0", - "eslint-plugin-wc": "3.0.2", - "globals": "17.2.0", - "happy-dom": "20.4.0", + "eslint-plugin-wc": "3.1.0", + "globals": "17.3.0", + "happy-dom": "20.6.0", "jiti": "2.6.1", "markdownlint-cli": "0.47.0", "material-icon-theme": "5.31.0", "nolyfill": "1.0.44", "postcss-html": "1.8.1", - "spectral-cli-bundle": "1.0.3", - "stylelint": "17.1.0", + "spectral-cli-bundle": "1.0.4", + "stylelint": "17.1.1", "stylelint-config-recommended": "18.0.0", "stylelint-declaration-block-no-ignored-properties": "3.0.0", "stylelint-declaration-strict-value": "1.10.11", "stylelint-value-no-unknown-custom-properties": "6.1.1", "svgo": "4.0.0", "typescript": "5.9.3", - "typescript-eslint": "8.54.0", - "updates": "17.0.9", - "vite-string-plugin": "2.0.0", + "typescript-eslint": "8.55.0", + "updates": "17.4.0", + "vite-string-plugin": "2.0.1", "vitest": "4.0.18", "vue-tsc": "3.2.4" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7795f1c6e3..7642d8dc82 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -64,13 +64,13 @@ importers: version: 2.6.2 '@silverwind/vue3-calendar-heatmap': specifier: 2.1.1 - version: 2.1.1(tippy.js@6.3.7)(vue@3.5.27(typescript@5.9.3)) + version: 2.1.1(tippy.js@6.3.7)(vue@3.5.28(typescript@5.9.3)) '@techknowlogick/license-checker-webpack-plugin': specifier: 0.3.0 - version: 0.3.0(webpack@5.104.1) + version: 0.3.0(webpack@5.105.0) add-asset-webpack-plugin: specifier: 3.1.1 - version: 3.1.1(webpack@5.104.1) + version: 3.1.1(webpack@5.105.0) ansi_up: specifier: 6.0.6 version: 6.0.6 @@ -87,8 +87,8 @@ importers: specifier: 2.2.0 version: 2.2.0(chart.js@4.5.1) clippie: - specifier: 4.1.9 - version: 4.1.9 + specifier: 4.1.10 + version: 4.1.10 compare-versions: specifier: 6.1.1 version: 6.1.1 @@ -97,7 +97,7 @@ importers: version: 1.6.2 css-loader: specifier: 7.1.3 - version: 7.1.3(webpack@5.104.1) + version: 7.1.3(webpack@5.105.0) dayjs: specifier: 1.11.19 version: 1.11.19 @@ -109,7 +109,7 @@ importers: version: 2.20.0 esbuild-loader: specifier: 4.4.2 - version: 4.4.2(webpack@5.104.1) + version: 4.4.2(webpack@5.105.0) htmx.org: specifier: 2.0.8 version: 2.0.8 @@ -130,13 +130,13 @@ importers: version: 11.12.2 mini-css-extract-plugin: specifier: 2.10.0 - version: 2.10.0(webpack@5.104.1) + version: 2.10.0(webpack@5.105.0) monaco-editor: specifier: 0.55.1 version: 0.55.1 monaco-editor-webpack-plugin: specifier: 7.1.1 - version: 7.1.1(monaco-editor@0.55.1)(webpack@5.104.1) + version: 7.1.1(monaco-editor@0.55.1)(webpack@5.105.0) online-3d-viewer: specifier: 0.18.0 version: 0.18.0 @@ -151,7 +151,7 @@ importers: version: 8.5.6 postcss-loader: specifier: 8.2.0 - version: 8.2.0(postcss@8.5.6)(typescript@5.9.3)(webpack@5.104.1) + version: 8.2.0(postcss@8.5.6)(typescript@5.9.3)(webpack@5.105.0) sortablejs: specifier: 1.15.6 version: 1.15.6 @@ -183,23 +183,23 @@ importers: specifier: 0.7.2 version: 0.7.2 vue: - specifier: 3.5.27 - version: 3.5.27(typescript@5.9.3) + specifier: 3.5.28 + version: 3.5.28(typescript@5.9.3) vue-bar-graph: specifier: 2.2.0 version: 2.2.0(typescript@5.9.3) vue-chartjs: specifier: 5.3.3 - version: 5.3.3(chart.js@4.5.1)(vue@3.5.27(typescript@5.9.3)) + version: 5.3.3(chart.js@4.5.1)(vue@3.5.28(typescript@5.9.3)) vue-loader: specifier: 17.4.2 - version: 17.4.2(vue@3.5.27(typescript@5.9.3))(webpack@5.104.1) + version: 17.4.2(vue@3.5.28(typescript@5.9.3))(webpack@5.105.0) webpack: - specifier: 5.104.1 - version: 5.104.1(webpack-cli@6.0.1) + specifier: 5.105.0 + version: 5.105.0(webpack-cli@6.0.1) webpack-cli: specifier: 6.0.1 - version: 6.0.1(webpack@5.104.1) + version: 6.0.1(webpack@5.105.0) wrap-ansi: specifier: 9.0.2 version: 9.0.2 @@ -211,14 +211,14 @@ importers: specifier: 0.14.0 version: 0.14.0 '@playwright/test': - specifier: 1.58.1 - version: 1.58.1 + specifier: 1.58.2 + version: 1.58.2 '@stylistic/eslint-plugin': - specifier: 5.7.1 - version: 5.7.1(eslint@9.39.2(jiti@2.6.1)) + specifier: 5.8.0 + version: 5.8.0(eslint@9.39.2(jiti@2.6.1)) '@stylistic/stylelint-plugin': specifier: 5.0.1 - version: 5.0.1(stylelint@17.1.0(typescript@5.9.3)) + version: 5.0.1(stylelint@17.1.1(typescript@5.9.3)) '@types/codemirror': specifier: 5.60.17 version: 5.60.17 @@ -253,20 +253,20 @@ importers: specifier: 1.12.4 version: 1.12.4 '@typescript-eslint/parser': - specifier: 8.54.0 - version: 8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + specifier: 8.55.0 + version: 8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) '@vitejs/plugin-vue': - specifier: 6.0.3 - version: 6.0.3(vite@7.3.1(@types/node@25.1.0)(jiti@2.6.1)(stylus@0.57.0)(terser@5.46.0)(yaml@2.8.2))(vue@3.5.27(typescript@5.9.3)) + specifier: 6.0.4 + version: 6.0.4(vite@7.3.1(@types/node@25.2.2)(jiti@2.6.1)(stylus@0.57.0)(terser@5.46.0)(yaml@2.8.2))(vue@3.5.28(typescript@5.9.3)) '@vitest/eslint-plugin': - specifier: 1.6.6 - version: 1.6.6(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)(vitest@4.0.18(@types/node@25.1.0)(happy-dom@20.4.0)(jiti@2.6.1)(stylus@0.57.0)(terser@5.46.0)(yaml@2.8.2)) + specifier: 1.6.7 + version: 1.6.7(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)(vitest@4.0.18(@types/node@25.2.2)(happy-dom@20.6.0)(jiti@2.6.1)(stylus@0.57.0)(terser@5.46.0)(yaml@2.8.2)) eslint: specifier: 9.39.2 version: 9.39.2(jiti@2.6.1) eslint-import-resolver-typescript: specifier: 4.4.4 - version: 4.4.4(eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.2(jiti@2.6.1)))(eslint-plugin-import@2.32.0)(eslint@9.39.2(jiti@2.6.1)) + version: 4.4.4(eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.2(jiti@2.6.1)))(eslint-plugin-import@2.32.0)(eslint@9.39.2(jiti@2.6.1)) eslint-plugin-array-func: specifier: 5.1.0 version: 5.1.0(eslint@9.39.2(jiti@2.6.1)) @@ -275,7 +275,7 @@ importers: version: 6.0.0(@types/eslint@9.6.1)(eslint-import-resolver-typescript@4.4.4)(eslint@9.39.2(jiti@2.6.1)) eslint-plugin-import-x: specifier: 4.16.1 - version: 4.16.1(@typescript-eslint/utils@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.2(jiti@2.6.1)) + version: 4.16.1(@typescript-eslint/utils@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.2(jiti@2.6.1)) eslint-plugin-playwright: specifier: 2.5.1 version: 2.5.1(eslint@9.39.2(jiti@2.6.1)) @@ -290,19 +290,19 @@ importers: version: 62.0.0(eslint@9.39.2(jiti@2.6.1)) eslint-plugin-vue: specifier: 10.7.0 - version: 10.7.0(@stylistic/eslint-plugin@5.7.1(eslint@9.39.2(jiti@2.6.1)))(@typescript-eslint/parser@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(vue-eslint-parser@10.2.0(eslint@9.39.2(jiti@2.6.1))) + version: 10.7.0(@stylistic/eslint-plugin@5.8.0(eslint@9.39.2(jiti@2.6.1)))(@typescript-eslint/parser@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(vue-eslint-parser@10.2.0(eslint@9.39.2(jiti@2.6.1))) eslint-plugin-vue-scoped-css: specifier: 2.12.0 version: 2.12.0(eslint@9.39.2(jiti@2.6.1))(vue-eslint-parser@10.2.0(eslint@9.39.2(jiti@2.6.1))) eslint-plugin-wc: - specifier: 3.0.2 - version: 3.0.2(eslint@9.39.2(jiti@2.6.1)) + specifier: 3.1.0 + version: 3.1.0(eslint@9.39.2(jiti@2.6.1)) globals: - specifier: 17.2.0 - version: 17.2.0 + specifier: 17.3.0 + version: 17.3.0 happy-dom: - specifier: 20.4.0 - version: 20.4.0 + specifier: 20.6.0 + version: 20.6.0 jiti: specifier: 2.6.1 version: 2.6.1 @@ -319,23 +319,23 @@ importers: specifier: 1.8.1 version: 1.8.1 spectral-cli-bundle: - specifier: 1.0.3 - version: 1.0.3 + specifier: 1.0.4 + version: 1.0.4 stylelint: - specifier: 17.1.0 - version: 17.1.0(typescript@5.9.3) + specifier: 17.1.1 + version: 17.1.1(typescript@5.9.3) stylelint-config-recommended: specifier: 18.0.0 - version: 18.0.0(stylelint@17.1.0(typescript@5.9.3)) + version: 18.0.0(stylelint@17.1.1(typescript@5.9.3)) stylelint-declaration-block-no-ignored-properties: specifier: 3.0.0 - version: 3.0.0(stylelint@17.1.0(typescript@5.9.3)) + version: 3.0.0(stylelint@17.1.1(typescript@5.9.3)) stylelint-declaration-strict-value: specifier: 1.10.11 - version: 1.10.11(stylelint@17.1.0(typescript@5.9.3)) + version: 1.10.11(stylelint@17.1.1(typescript@5.9.3)) stylelint-value-no-unknown-custom-properties: specifier: 6.1.1 - version: 6.1.1(stylelint@17.1.0(typescript@5.9.3)) + version: 6.1.1(stylelint@17.1.1(typescript@5.9.3)) svgo: specifier: 4.0.0 version: 4.0.0 @@ -343,17 +343,17 @@ importers: specifier: 5.9.3 version: 5.9.3 typescript-eslint: - specifier: 8.54.0 - version: 8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + specifier: 8.55.0 + version: 8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) updates: - specifier: 17.0.9 - version: 17.0.9 + specifier: 17.4.0 + version: 17.4.0 vite-string-plugin: - specifier: 2.0.0 - version: 2.0.0(vite@7.3.1(@types/node@25.1.0)(jiti@2.6.1)(stylus@0.57.0)(terser@5.46.0)(yaml@2.8.2)) + specifier: 2.0.1 + version: 2.0.1(vite@7.3.1(@types/node@25.2.2)(jiti@2.6.1)(stylus@0.57.0)(terser@5.46.0)(yaml@2.8.2)) vitest: specifier: 4.0.18 - version: 4.0.18(@types/node@25.1.0)(happy-dom@20.4.0)(jiti@2.6.1)(stylus@0.57.0)(terser@5.46.0)(yaml@2.8.2) + version: 4.0.18(@types/node@25.2.2)(happy-dom@20.6.0)(jiti@2.6.1)(stylus@0.57.0)(terser@5.46.0)(yaml@2.8.2) vue-tsc: specifier: 3.2.4 version: 3.2.4(typescript@5.9.3) @@ -367,8 +367,8 @@ packages: '@antfu/install-pkg@1.1.0': resolution: {integrity: sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==} - '@babel/code-frame@7.28.6': - resolution: {integrity: sha512-JYgintcMjRiCvS8mMECzaEn+m3PfoQiyqukOMCCVQtoJGYJw8j/8LBJEiqkHLkfwCcs74E3pbAUFNg7d9VNJ+Q==} + '@babel/code-frame@7.29.0': + resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} engines: {node: '>=6.9.0'} '@babel/helper-string-parser@7.27.1': @@ -379,8 +379,8 @@ packages: resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} engines: {node: '>=6.9.0'} - '@babel/parser@7.28.6': - resolution: {integrity: sha512-TeR9zWR18BvbfPmGbLampPMW+uW1NZnJlRuuHso8i87QZNq2JRF9i6RgxRqtEq+wQGsS19NNTWr2duhnE49mfQ==} + '@babel/parser@7.29.0': + resolution: {integrity: sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==} engines: {node: '>=6.0.0'} hasBin: true @@ -388,8 +388,8 @@ packages: resolution: {integrity: sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==} engines: {node: '>=6.9.0'} - '@babel/types@7.28.6': - resolution: {integrity: sha512-0ZrskXVEHSWIqZM/sQZ4EV3jZJXRkio/WCxaqKZP1g//CEWEPSfeZFcms4XeKBCHU0ZKnIkdJeU/kF+eRp5lBg==} + '@babel/types@7.29.0': + resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} engines: {node: '>=6.9.0'} '@braintree/sanitize-url@7.1.2': @@ -398,8 +398,8 @@ packages: '@cacheable/memory@2.0.7': resolution: {integrity: sha512-RbxnxAMf89Tp1dLhXMS7ceft/PGsDl1Ip7T20z5nZ+pwIAsQ1p2izPjVG69oCLv/jfQ7HDPHTWK0c9rcAWXN3A==} - '@cacheable/utils@2.3.3': - resolution: {integrity: sha512-JsXDL70gQ+1Vc2W/KUFfkAJzgb4puKwwKehNLuB+HrNKWf91O736kGfxn4KujXCCSuh6mRRL4XEB0PkAFjWS0A==} + '@cacheable/utils@2.3.4': + resolution: {integrity: sha512-knwKUJEYgIfwShABS1BX6JyJJTglAFcEU7EXqzTdiGCXur4voqkiJkdgZIQtWNFhynzDWERcTYv/sETMu3uJWA==} '@chevrotain/cst-dts-gen@11.0.3': resolution: {integrity: sha512-BvIKpRLeS/8UbfxXxgC33xOumsacaeCKAjAeLyOn7Pcp95HiRbrpl14S+9vaZLolnbssPIUuiUd8IvgkRyt6NQ==} @@ -470,8 +470,8 @@ packages: peerDependencies: '@csstools/css-tokenizer': ^4.0.0 - '@csstools/css-syntax-patches-for-csstree@1.0.26': - resolution: {integrity: sha512-6boXK0KkzT5u5xOgF6TKB+CLq9SOpEGmkZw0g5n9/7yg85wab3UzSxB8TxhLJ31L4SGJ6BCFRw/iftTha1CJXA==} + '@csstools/css-syntax-patches-for-csstree@1.0.27': + resolution: {integrity: sha512-sxP33Jwg1bviSUXAV43cVYdmjt2TLnLXNqCWl9xmxHawWVjGz/kEbdkr7F9pxJNBN2Mh+dq0crgItbW6tQvyow==} '@csstools/css-tokenizer@4.0.0': resolution: {integrity: sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==} @@ -509,158 +509,158 @@ packages: '@emnapi/wasi-threads@1.1.0': resolution: {integrity: sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==} - '@esbuild/aix-ppc64@0.27.2': - resolution: {integrity: sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==} + '@esbuild/aix-ppc64@0.27.3': + resolution: {integrity: sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] - '@esbuild/android-arm64@0.27.2': - resolution: {integrity: sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==} + '@esbuild/android-arm64@0.27.3': + resolution: {integrity: sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==} engines: {node: '>=18'} cpu: [arm64] os: [android] - '@esbuild/android-arm@0.27.2': - resolution: {integrity: sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==} + '@esbuild/android-arm@0.27.3': + resolution: {integrity: sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==} engines: {node: '>=18'} cpu: [arm] os: [android] - '@esbuild/android-x64@0.27.2': - resolution: {integrity: sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==} + '@esbuild/android-x64@0.27.3': + resolution: {integrity: sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==} engines: {node: '>=18'} cpu: [x64] os: [android] - '@esbuild/darwin-arm64@0.27.2': - resolution: {integrity: sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==} + '@esbuild/darwin-arm64@0.27.3': + resolution: {integrity: sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] - '@esbuild/darwin-x64@0.27.2': - resolution: {integrity: sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==} + '@esbuild/darwin-x64@0.27.3': + resolution: {integrity: sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==} engines: {node: '>=18'} cpu: [x64] os: [darwin] - '@esbuild/freebsd-arm64@0.27.2': - resolution: {integrity: sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==} + '@esbuild/freebsd-arm64@0.27.3': + resolution: {integrity: sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-x64@0.27.2': - resolution: {integrity: sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==} + '@esbuild/freebsd-x64@0.27.3': + resolution: {integrity: sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] - '@esbuild/linux-arm64@0.27.2': - resolution: {integrity: sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==} + '@esbuild/linux-arm64@0.27.3': + resolution: {integrity: sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==} engines: {node: '>=18'} cpu: [arm64] os: [linux] - '@esbuild/linux-arm@0.27.2': - resolution: {integrity: sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==} + '@esbuild/linux-arm@0.27.3': + resolution: {integrity: sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==} engines: {node: '>=18'} cpu: [arm] os: [linux] - '@esbuild/linux-ia32@0.27.2': - resolution: {integrity: sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==} + '@esbuild/linux-ia32@0.27.3': + resolution: {integrity: sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==} engines: {node: '>=18'} cpu: [ia32] os: [linux] - '@esbuild/linux-loong64@0.27.2': - resolution: {integrity: sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==} + '@esbuild/linux-loong64@0.27.3': + resolution: {integrity: sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==} engines: {node: '>=18'} cpu: [loong64] os: [linux] - '@esbuild/linux-mips64el@0.27.2': - resolution: {integrity: sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==} + '@esbuild/linux-mips64el@0.27.3': + resolution: {integrity: sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] - '@esbuild/linux-ppc64@0.27.2': - resolution: {integrity: sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==} + '@esbuild/linux-ppc64@0.27.3': + resolution: {integrity: sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] - '@esbuild/linux-riscv64@0.27.2': - resolution: {integrity: sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==} + '@esbuild/linux-riscv64@0.27.3': + resolution: {integrity: sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] - '@esbuild/linux-s390x@0.27.2': - resolution: {integrity: sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==} + '@esbuild/linux-s390x@0.27.3': + resolution: {integrity: sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==} engines: {node: '>=18'} cpu: [s390x] os: [linux] - '@esbuild/linux-x64@0.27.2': - resolution: {integrity: sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==} + '@esbuild/linux-x64@0.27.3': + resolution: {integrity: sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==} engines: {node: '>=18'} cpu: [x64] os: [linux] - '@esbuild/netbsd-arm64@0.27.2': - resolution: {integrity: sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==} + '@esbuild/netbsd-arm64@0.27.3': + resolution: {integrity: sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] - '@esbuild/netbsd-x64@0.27.2': - resolution: {integrity: sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==} + '@esbuild/netbsd-x64@0.27.3': + resolution: {integrity: sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] - '@esbuild/openbsd-arm64@0.27.2': - resolution: {integrity: sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==} + '@esbuild/openbsd-arm64@0.27.3': + resolution: {integrity: sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-x64@0.27.2': - resolution: {integrity: sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==} + '@esbuild/openbsd-x64@0.27.3': + resolution: {integrity: sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] - '@esbuild/openharmony-arm64@0.27.2': - resolution: {integrity: sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==} + '@esbuild/openharmony-arm64@0.27.3': + resolution: {integrity: sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==} engines: {node: '>=18'} cpu: [arm64] os: [openharmony] - '@esbuild/sunos-x64@0.27.2': - resolution: {integrity: sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==} + '@esbuild/sunos-x64@0.27.3': + resolution: {integrity: sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==} engines: {node: '>=18'} cpu: [x64] os: [sunos] - '@esbuild/win32-arm64@0.27.2': - resolution: {integrity: sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==} + '@esbuild/win32-arm64@0.27.3': + resolution: {integrity: sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==} engines: {node: '>=18'} cpu: [arm64] os: [win32] - '@esbuild/win32-ia32@0.27.2': - resolution: {integrity: sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==} + '@esbuild/win32-ia32@0.27.3': + resolution: {integrity: sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==} engines: {node: '>=18'} cpu: [ia32] os: [win32] - '@esbuild/win32-x64@0.27.2': - resolution: {integrity: sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==} + '@esbuild/win32-x64@0.27.3': + resolution: {integrity: sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==} engines: {node: '>=18'} cpu: [x64] os: [win32] @@ -770,8 +770,8 @@ packages: resolution: {integrity: sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==} engines: {node: 20 || >=22} - '@isaacs/brace-expansion@5.0.0': - resolution: {integrity: sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==} + '@isaacs/brace-expansion@5.0.1': + resolution: {integrity: sha512-WMz71T1JS624nWj2n2fnYAuPovhv7EUhk69R6i9dsVyzxt5eM3bjwvgk9L+APE1TRscGysAVMANkB0jh0LQZrQ==} engines: {node: 20 || >=22} '@jridgewell/gen-mapping@0.3.13': @@ -898,8 +898,8 @@ packages: resolution: {integrity: sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==} engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} - '@playwright/test@1.58.1': - resolution: {integrity: sha512-6LdVIUERWxQMmUSSQi0I53GgCBYgM2RpGngCPY7hSeju+VrKjq3lvs7HpJoPbDiY5QM5EYRtRX5fvrinnMAz3w==} + '@playwright/test@1.58.2': + resolution: {integrity: sha512-akea+6bHYBBfA9uQqSYmlJXn61cTa+jbO87xVLCWbTqbWadRVmhxlXATaOjOgcBaWU4ePo0wB41KMFv3o35IXA==} engines: {node: '>=18'} hasBin: true @@ -913,8 +913,8 @@ packages: resolution: {integrity: sha512-FqALmHI8D4o6lk/LRWDnhw95z5eO+eAa6ORjVg09YRR7BkcM6oPHU9uyC0gtQG5vpFLvgpeU4+zEAz2H8APHNw==} engines: {node: '>= 10'} - '@rolldown/pluginutils@1.0.0-beta.53': - resolution: {integrity: sha512-vENRlFU4YbrwVqNDZ7fLvy+JR1CRkyr01jhSiDpE1u6py3OMzQfztQU2jxykW3ALNxO4kSlqIDeYyD0Y9RcQeQ==} + '@rolldown/pluginutils@1.0.0-rc.2': + resolution: {integrity: sha512-izyXV/v+cHiRfozX62W9htOAvwMo4/bXKDrQ+vom1L1qRuexPock/7VZDAhnpHCLNejd3NJ6hiab+tO0D44Rgw==} '@rollup/rollup-android-arm-eabi@4.57.1': resolution: {integrity: sha512-A6ehUVSiSaaliTxai040ZpZ2zTevHYbvu/lDoeAteHI8QnaosIzm4qwtezfRg1jOYaUmnzLX1AOD6Z+UJjtifg==} @@ -1092,8 +1092,8 @@ packages: '@standard-schema/spec@1.1.0': resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} - '@stylistic/eslint-plugin@5.7.1': - resolution: {integrity: sha512-zjTUwIsEfT+k9BmXwq1QEFYsb4afBlsI1AXFyWQBgggMzwBFOuu92pGrE5OFx90IOjNl+lUbQoTG7f8S0PkOdg==} + '@stylistic/eslint-plugin@5.8.0': + resolution: {integrity: sha512-WNPVF/FfBAjyi3OA7gok8swRiImNLKI4dmV3iK/GC/0xSJR7eCzBFsw9hLZVgb1+MYNLy7aDsjohxN1hA/FIfQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: '>=9.0.0' @@ -1259,8 +1259,8 @@ packages: '@types/ms@2.1.0': resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} - '@types/node@25.1.0': - resolution: {integrity: sha512-t7frlewr6+cbx+9Ohpl0NOTKXZNV9xHRmNOvql47BFJKcEG1CxtxlPEEe+gR9uhVWM4DwhnvTF110mIL4yP9RA==} + '@types/node@25.2.2': + resolution: {integrity: sha512-BkmoP5/FhRYek5izySdkOneRyXYN35I860MFAGupTdebyE66uZaR+bXLHq8k4DirE5DwQi3NuhvRU1jqTVwUrQ==} '@types/pdfobject@2.2.5': resolution: {integrity: sha512-7gD5tqc/RUDq0PyoLemL0vEHxBYi+zY0WVaFAx/Y0jBsXFgot1vB9No1GhDZGwRGJMCIZbgAb74QG9MTyTNU/g==} @@ -1298,63 +1298,63 @@ packages: '@types/ws@8.18.1': resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} - '@typescript-eslint/eslint-plugin@8.54.0': - resolution: {integrity: sha512-hAAP5io/7csFStuOmR782YmTthKBJ9ND3WVL60hcOjvtGFb+HJxH4O5huAcmcZ9v9G8P+JETiZ/G1B8MALnWZQ==} + '@typescript-eslint/eslint-plugin@8.55.0': + resolution: {integrity: sha512-1y/MVSz0NglV1ijHC8OT49mPJ4qhPYjiK08YUQVbIOyu+5k862LKUHFkpKHWu//zmr7hDR2rhwUm6gnCGNmGBQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^8.54.0 + '@typescript-eslint/parser': ^8.55.0 eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/parser@8.54.0': - resolution: {integrity: sha512-BtE0k6cjwjLZoZixN0t5AKP0kSzlGu7FctRXYuPAm//aaiZhmfq1JwdYpYr1brzEspYyFeF+8XF5j2VK6oalrA==} + '@typescript-eslint/parser@8.55.0': + resolution: {integrity: sha512-4z2nCSBfVIMnbuu8uinj+f0o4qOeggYJLbjpPHka3KH1om7e+H9yLKTYgksTaHcGco+NClhhY2vyO3HsMH1RGw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/project-service@8.54.0': - resolution: {integrity: sha512-YPf+rvJ1s7MyiWM4uTRhE4DvBXrEV+d8oC3P9Y2eT7S+HBS0clybdMIPnhiATi9vZOYDc7OQ1L/i6ga6NFYK/g==} + '@typescript-eslint/project-service@8.55.0': + resolution: {integrity: sha512-zRcVVPFUYWa3kNnjaZGXSu3xkKV1zXy8M4nO/pElzQhFweb7PPtluDLQtKArEOGmjXoRjnUZ29NjOiF0eCDkcQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/scope-manager@8.54.0': - resolution: {integrity: sha512-27rYVQku26j/PbHYcVfRPonmOlVI6gihHtXFbTdB5sb6qA0wdAQAbyXFVarQ5t4HRojIz64IV90YtsjQSSGlQg==} + '@typescript-eslint/scope-manager@8.55.0': + resolution: {integrity: sha512-fVu5Omrd3jeqeQLiB9f1YsuK/iHFOwb04bCtY4BSCLgjNbOD33ZdV6KyEqplHr+IlpgT0QTZ/iJ+wT7hvTx49Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/tsconfig-utils@8.54.0': - resolution: {integrity: sha512-dRgOyT2hPk/JwxNMZDsIXDgyl9axdJI3ogZ2XWhBPsnZUv+hPesa5iuhdYt2gzwA9t8RE5ytOJ6xB0moV0Ujvw==} + '@typescript-eslint/tsconfig-utils@8.55.0': + resolution: {integrity: sha512-1R9cXqY7RQd7WuqSN47PK9EDpgFUK3VqdmbYrvWJZYDd0cavROGn+74ktWBlmJ13NXUQKlZ/iAEQHI/V0kKe0Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/type-utils@8.54.0': - resolution: {integrity: sha512-hiLguxJWHjjwL6xMBwD903ciAwd7DmK30Y9Axs/etOkftC3ZNN9K44IuRD/EB08amu+Zw6W37x9RecLkOo3pMA==} + '@typescript-eslint/type-utils@8.55.0': + resolution: {integrity: sha512-x1iH2unH4qAt6I37I2CGlsNs+B9WGxurP2uyZLRz6UJoZWDBx9cJL1xVN/FiOmHEONEg6RIufdvyT0TEYIgC5g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/types@8.54.0': - resolution: {integrity: sha512-PDUI9R1BVjqu7AUDsRBbKMtwmjWcn4J3le+5LpcFgWULN3LvHC5rkc9gCVxbrsrGmO1jfPybN5s6h4Jy+OnkAA==} + '@typescript-eslint/types@8.55.0': + resolution: {integrity: sha512-ujT0Je8GI5BJWi+/mMoR0wxwVEQaxM+pi30xuMiJETlX80OPovb2p9E8ss87gnSVtYXtJoU9U1Cowcr6w2FE0w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@8.54.0': - resolution: {integrity: sha512-BUwcskRaPvTk6fzVWgDPdUndLjB87KYDrN5EYGetnktoeAvPtO4ONHlAZDnj5VFnUANg0Sjm7j4usBlnoVMHwA==} + '@typescript-eslint/typescript-estree@8.55.0': + resolution: {integrity: sha512-EwrH67bSWdx/3aRQhCoxDaHM+CrZjotc2UCCpEDVqfCE+7OjKAGWNY2HsCSTEVvWH2clYQK8pdeLp42EVs+xQw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/utils@8.54.0': - resolution: {integrity: sha512-9Cnda8GS57AQakvRyG0PTejJNlA2xhvyNtEVIMlDWOOeEyBkYWhGPnfrIAnqxLMTSTo6q8g12XVjjev5l1NvMA==} + '@typescript-eslint/utils@8.55.0': + resolution: {integrity: sha512-BqZEsnPGdYpgyEIkDC1BadNY8oMwckftxBT+C8W0g1iKPdeqKZBtTfnvcq0nf60u7MkjFO8RBvpRGZBPw4L2ow==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/visitor-keys@8.54.0': - resolution: {integrity: sha512-VFlhGSl4opC0bprJiItPQ1RfUhGDIBokcPwaFH4yiBCaNPeld/9VeXbiPO1cLyorQi1G1vL+ecBk1x8o1axORA==} + '@typescript-eslint/visitor-keys@8.55.0': + resolution: {integrity: sha512-AxNRwEie8Nn4eFS1FzDMJWIISMGoXMb037sgCBJ3UR6o0fQTzr2tqN9WT+DkWJPhIdQCfV7T6D387566VtnCJA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@unrs/resolver-binding-android-arm-eabi@1.11.1': @@ -1460,15 +1460,15 @@ packages: cpu: [x64] os: [win32] - '@vitejs/plugin-vue@6.0.3': - resolution: {integrity: sha512-TlGPkLFLVOY3T7fZrwdvKpjprR3s4fxRln0ORDo1VQ7HHyxJwTlrjKU3kpVWTlaAjIEuCTokmjkZnr8Tpc925w==} + '@vitejs/plugin-vue@6.0.4': + resolution: {integrity: sha512-uM5iXipgYIn13UUQCZNdWkYk+sysBeA97d5mHsAoAt1u/wpN3+zxOmsVJWosuzX+IMGRzeYUNytztrYznboIkQ==} engines: {node: ^20.19.0 || >=22.12.0} peerDependencies: vite: ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 vue: ^3.2.25 - '@vitest/eslint-plugin@1.6.6': - resolution: {integrity: sha512-bwgQxQWRtnTVzsUHK824tBmHzjV0iTx3tZaiQIYDjX3SA7TsQS8CuDVqxXrRY3FaOUMgbGavesCxI9MOfFLm7Q==} + '@vitest/eslint-plugin@1.6.7': + resolution: {integrity: sha512-sd2QJirEscSQk3Pywtelbs7z8RQp1gyF5BfeZVtTHE8y3suyzbAA71NuT9z01uTRMHoCf5p6M2t2WYNJ7m5FlA==} engines: {node: '>=18'} peerDependencies: eslint: '>=8.57.0' @@ -1518,37 +1518,37 @@ packages: '@volar/typescript@2.4.27': resolution: {integrity: sha512-eWaYCcl/uAPInSK2Lze6IqVWaBu/itVqR5InXcHXFyles4zO++Mglt3oxdgj75BDcv1Knr9Y93nowS8U3wqhxg==} - '@vue/compiler-core@3.5.27': - resolution: {integrity: sha512-gnSBQjZA+//qDZen+6a2EdHqJ68Z7uybrMf3SPjEGgG4dicklwDVmMC1AeIHxtLVPT7sn6sH1KOO+tS6gwOUeQ==} + '@vue/compiler-core@3.5.28': + resolution: {integrity: sha512-kviccYxTgoE8n6OCw96BNdYlBg2GOWfBuOW4Vqwrt7mSKWKwFVvI8egdTltqRgITGPsTFYtKYfxIG8ptX2PJHQ==} - '@vue/compiler-dom@3.5.27': - resolution: {integrity: sha512-oAFea8dZgCtVVVTEC7fv3T5CbZW9BxpFzGGxC79xakTr6ooeEqmRuvQydIiDAkglZEAd09LgVf1RoDnL54fu5w==} + '@vue/compiler-dom@3.5.28': + resolution: {integrity: sha512-/1ZepxAb159jKR1btkefDP+J2xuWL5V3WtleRmxaT+K2Aqiek/Ab/+Ebrw2pPj0sdHO8ViAyyJWfhXXOP/+LQA==} - '@vue/compiler-sfc@3.5.27': - resolution: {integrity: sha512-sHZu9QyDPeDmN/MRoshhggVOWE5WlGFStKFwu8G52swATgSny27hJRWteKDSUUzUH+wp+bmeNbhJnEAel/auUQ==} + '@vue/compiler-sfc@3.5.28': + resolution: {integrity: sha512-6TnKMiNkd6u6VeVDhZn/07KhEZuBSn43Wd2No5zaP5s3xm8IqFTHBj84HJah4UepSUJTro5SoqqlOY22FKY96g==} - '@vue/compiler-ssr@3.5.27': - resolution: {integrity: sha512-Sj7h+JHt512fV1cTxKlYhg7qxBvack+BGncSpH+8vnN+KN95iPIcqB5rsbblX40XorP+ilO7VIKlkuu3Xq2vjw==} + '@vue/compiler-ssr@3.5.28': + resolution: {integrity: sha512-JCq//9w1qmC6UGLWJX7RXzrGpKkroubey/ZFqTpvEIDJEKGgntuDMqkuWiZvzTzTA5h2qZvFBFHY7fAAa9475g==} '@vue/language-core@3.2.4': resolution: {integrity: sha512-bqBGuSG4KZM45KKTXzGtoCl9cWju5jsaBKaJJe3h5hRAAWpZUuj5G+L+eI01sPIkm4H6setKRlw7E85wLdDNew==} - '@vue/reactivity@3.5.27': - resolution: {integrity: sha512-vvorxn2KXfJ0nBEnj4GYshSgsyMNFnIQah/wczXlsNXt+ijhugmW+PpJ2cNPe4V6jpnBcs0MhCODKllWG+nvoQ==} + '@vue/reactivity@3.5.28': + resolution: {integrity: sha512-gr5hEsxvn+RNyu9/9o1WtdYdwDjg5FgjUSBEkZWqgTKlo/fvwZ2+8W6AfKsc9YN2k/+iHYdS9vZYAhpi10kNaw==} - '@vue/runtime-core@3.5.27': - resolution: {integrity: sha512-fxVuX/fzgzeMPn/CLQecWeDIFNt3gQVhxM0rW02Tvp/YmZfXQgcTXlakq7IMutuZ/+Ogbn+K0oct9J3JZfyk3A==} + '@vue/runtime-core@3.5.28': + resolution: {integrity: sha512-POVHTdbgnrBBIpnbYU4y7pOMNlPn2QVxVzkvEA2pEgvzbelQq4ZOUxbp2oiyo+BOtiYlm8Q44wShHJoBvDPAjQ==} - '@vue/runtime-dom@3.5.27': - resolution: {integrity: sha512-/QnLslQgYqSJ5aUmb5F0z0caZPGHRB8LEAQ1s81vHFM5CBfnun63rxhvE/scVb/j3TbBuoZwkJyiLCkBluMpeg==} + '@vue/runtime-dom@3.5.28': + resolution: {integrity: sha512-4SXxSF8SXYMuhAIkT+eBRqOkWEfPu6nhccrzrkioA6l0boiq7sp18HCOov9qWJA5HML61kW8p/cB4MmBiG9dSA==} - '@vue/server-renderer@3.5.27': - resolution: {integrity: sha512-qOz/5thjeP1vAFc4+BY3Nr6wxyLhpeQgAE/8dDtKo6a6xdk+L4W46HDZgNmLOBUDEkFXV3G7pRiUqxjX0/2zWA==} + '@vue/server-renderer@3.5.28': + resolution: {integrity: sha512-pf+5ECKGj8fX95bNincbzJ6yp6nyzuLDhYZCeFxUNp8EBrQpPpQaLX3nNCp49+UbgbPun3CeVE+5CXVV1Xydfg==} peerDependencies: - vue: 3.5.27 + vue: 3.5.28 - '@vue/shared@3.5.27': - resolution: {integrity: sha512-dXr/3CgqXsJkZ0n9F3I4elY8wM9jMJpP3pvRG52r6m0tu/MsAFIe6JpXVGeNMd/D9F4hQynWT8Rfuj0bdm9kFQ==} + '@vue/shared@3.5.28': + resolution: {integrity: sha512-cfWa1fCGBxrvaHRhvV3Is0MgmrbSCxYTXCSCau2I0a1Xw1N1pHAvkWCiXPRAqjvToILvguNyEwjevUqAuBQWvQ==} '@webassemblyjs/ast@1.14.1': resolution: {integrity: sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==} @@ -1808,8 +1808,8 @@ packages: resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} engines: {node: '>= 6'} - caniuse-lite@1.0.30001766: - resolution: {integrity: sha512-4C0lfJ0/YPjJQHagaE9x2Elb69CIqEPZeG0anQt9SIvIoOH4a4uaRl73IavyO+0qZh6MDLH//DrXThEYKHkmYA==} + caniuse-lite@1.0.30001769: + resolution: {integrity: sha512-BCfFL1sHijQlBGWBMuJyhZUhzo7wer5sVj9hqekB/7xn0Ypy+pER/edCYQm4exbXj4WiySGp40P8UuTh6w1srg==} chai@6.2.2: resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} @@ -1877,8 +1877,8 @@ packages: resolution: {integrity: sha512-GfisEZEJvzKrmGWkvfhgzcz/BllN1USeqD2V6tg14OAOgaCD2Z/PUEuxnAZ/nPvmaHRG7a8y77p1T/IRQ4D1Hw==} engines: {node: '>=4'} - clippie@4.1.9: - resolution: {integrity: sha512-YaNJI8f2bPRVVfdKDUeqSPuQEztyOowee7DIc/DJ48qNJGq/SziipiWN6oWT6q9FR4QN0JzFDpP+fDtkSZyFHw==} + clippie@4.1.10: + resolution: {integrity: sha512-zUjK2fLH8/wju2lks5mH0u8wSRYCOJoHfT1KQ61+aCT5O1ouONnSrnKQ3BTKvIYLUYJarbLZo4FLHyce/SLF2g==} clone-deep@4.0.1: resolution: {integrity: sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==} @@ -1887,8 +1887,8 @@ packages: codemirror-spell-checker@1.1.2: resolution: {integrity: sha512-2Tl6n0v+GJRsC9K3MLCdLaMOmvWL0uukajNJseorZJsslaxZyZMgENocPU8R0DyoTAiKsyqiemSOZo7kjGV0LQ==} - codemirror@5.65.20: - resolution: {integrity: sha512-i5dLDDxwkFCbhjvL2pNjShsojoL3XHyDwsGv1jqETUoW+lzpBKKqNTUWgQwVAOa0tUm4BwekT455ujafi8payA==} + codemirror@5.65.21: + resolution: {integrity: sha512-6teYk0bA0nR3QP0ihGMoxuKzpl5W80FpnHpBJpgy66NK3cZv5b/d/HY8PnRvfSsCG1MTfr92u2WUl+wT0E40mQ==} color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} @@ -1911,8 +1911,8 @@ packages: resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} engines: {node: '>=18'} - commander@14.0.2: - resolution: {integrity: sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ==} + commander@14.0.3: + resolution: {integrity: sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==} engines: {node: '>=20'} commander@2.20.3: @@ -2262,8 +2262,8 @@ packages: easymde@2.20.0: resolution: {integrity: sha512-V1Z5f92TfR42Na852OWnIZMbM7zotWQYTddNaLYZFVKj7APBbyZ3FYJ27gBw2grMW3R6Qdv9J8n5Ij7XRSIgXQ==} - electron-to-chromium@1.5.283: - resolution: {integrity: sha512-3vifjt1HgrGW/h76UEeny+adYApveS9dH2h3p57JYzBSXJIKUJAvtmIytDKjcSCt9xHfrNCFJ7gts6vkhuq++w==} + electron-to-chromium@1.5.286: + resolution: {integrity: sha512-9tfDXhJ4RKFNerfjdCcZfufu49vg620741MNs26a9+bhLThdB+plgMeou98CAaHu/WATj2iHOOHTp1hWtABj2A==} elkjs@0.9.3: resolution: {integrity: sha512-f/ZeWvW/BCXbhGEf1Ujp29EASo/lk1FDnETgNKwJrsVvGZhUWCZyg3xLJjAsxfOmt8KjswHmI5EwCQcPMpOYhQ==} @@ -2281,14 +2281,18 @@ packages: resolution: {integrity: sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==} engines: {node: '>= 4'} - enhanced-resolve@5.18.4: - resolution: {integrity: sha512-LgQMM4WXU3QI+SYgEc2liRgznaD5ojbmY3sb8LxyguVkIg5FxdpTkvk72te2R38/TGKxH634oLxXRGY6d7AP+Q==} + enhanced-resolve@5.19.0: + resolution: {integrity: sha512-phv3E1Xl4tQOShqSte26C7Fl84EwUdZsyOuSSk9qtAGyyQs2s3jJzComh+Abf4g187lUUAvH+H26omrqia2aGg==} engines: {node: '>=10.13.0'} entities@4.5.0: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} engines: {node: '>=0.12'} + entities@6.0.1: + resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} + engines: {node: '>=0.12'} + entities@7.0.1: resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==} engines: {node: '>=0.12'} @@ -2316,8 +2320,8 @@ packages: peerDependencies: webpack: ^4.40.0 || ^5.0.0 - esbuild@0.27.2: - resolution: {integrity: sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==} + esbuild@0.27.3: + resolution: {integrity: sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==} engines: {node: '>=18'} hasBin: true @@ -2515,8 +2519,8 @@ packages: '@typescript-eslint/parser': optional: true - eslint-plugin-wc@3.0.2: - resolution: {integrity: sha512-siwTrxPTw6GU2JmP3faInw8nhi0ZCnKsiSRM3j7EAkZmBTGYdDAToeseLYsvPrc5Urp/vPz+g7Ewh7XcICLxww==} + eslint-plugin-wc@3.1.0: + resolution: {integrity: sha512-spvXHD2/GTTgYXxFB3xlMThnXGUeNJaiCwWuPGzjDOLXnVGLcQpDt0fyiN6yiLoaLs/yhsj+7G1FpBZKeigCSA==} peerDependencies: eslint: '>=8.40.0' @@ -2689,8 +2693,8 @@ packages: resolution: {integrity: sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==} engines: {node: '>=18'} - get-tsconfig@4.13.1: - resolution: {integrity: sha512-EoY1N2xCn44xU6750Sx7OjOIT59FkmstNc3X6y5xpz7D5cBtZRe/3pSlTkDJgqsOk3WwZPkWfonhhUJfttQo3w==} + get-tsconfig@4.13.6: + resolution: {integrity: sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==} glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} @@ -2705,7 +2709,7 @@ packages: glob@7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} - deprecated: Glob versions prior to v9 are no longer supported + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me global-modules@2.0.0: resolution: {integrity: sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==} @@ -2723,8 +2727,8 @@ packages: resolution: {integrity: sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==} engines: {node: '>=18'} - globals@17.2.0: - resolution: {integrity: sha512-tovnCz/fEq+Ripoq+p/gN1u7l6A7wwkoBT9pRCzTHzsD/LvADIzXZdjmRymh5Ztf0DYC3Rwg5cZRYjxzBmzbWg==} + globals@17.3.0: + resolution: {integrity: sha512-yMqGUQVVCkD4tqjOJf3TnrvaaHDMYp4VlUSObbkIiuCPe/ofdMBFIAcBbCSRFWOnos6qRiTVStDwqPLUclaxIw==} engines: {node: '>=18'} globby@16.1.0: @@ -2744,8 +2748,8 @@ packages: resolution: {integrity: sha512-tSQXBXS/MWQOn/RKckawJ61vvsDpCom87JgxiYdGwHdOa0ht0vzUWDlfioofFCRU0L+6NGDt6XzbgoJvZkMeRQ==} engines: {node: '>=0.8.0'} - happy-dom@20.4.0: - resolution: {integrity: sha512-RDeQm3dT9n0A5f/TszjUmNCLEuPnMGv3Tv4BmNINebz/h17PA6LMBcxJ5FrcqltNBMh9jA/8ufgDdBYUdBt+eg==} + happy-dom@20.6.0: + resolution: {integrity: sha512-a+Sz2bPai3rajDuE82Y4B0OxlXJ19ckUjyfWDmeCAs8ZbEbnqtwzV9d4CVhQjWIuOBTOw8rhlxNeaSCHeknXRQ==} engines: {node: '>=20.0.0'} has-flag@4.0.0: @@ -2763,8 +2767,8 @@ packages: resolution: {integrity: sha512-Wn2i1In6XFxl8Az55kkgnFRiAlIAushzh26PTjL2AKtQcEfXrcLa7Hn5QOWGZEf3LU057P9TwwZjFyxfS1VuvQ==} engines: {node: '>=20'} - hookified@1.15.0: - resolution: {integrity: sha512-51w+ZZGt7Zw5q7rM3nC4t3aLn/xvKDETsXqMczndvwyVQhAHfUmUuFBRFcos8Iyebtk7OAE9dL26wFNzZVVOkw==} + hookified@1.15.1: + resolution: {integrity: sha512-MvG/clsADq1GPM2KGo2nyfaWVyn9naPiXrqIe4jYjXNZQt238kWyOGrsyc/DmRAQ+Re6yeo6yX/yoNCG5KAEVg==} html-tags@5.1.0: resolution: {integrity: sha512-n6l5uca7/y5joxZ3LUePhzmBFUJ+U2YWzhMa8XUTecSeSlQiZdF5XAd/Q3/WUl0VsXgUwWi8I7CNIwdI5WN1SQ==} @@ -2943,8 +2947,8 @@ packages: resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} hasBin: true - jsdoc-type-pratt-parser@7.1.0: - resolution: {integrity: sha512-SX7q7XyCwzM/MEDCYz0l8GgGbJAACGFII9+WfNYr5SLEKukHWRy2Jk3iWRe7P+lpYJNs7oQ+OSei4JtKGUjd7A==} + jsdoc-type-pratt-parser@7.1.1: + resolution: {integrity: sha512-/2uqY7x6bsrpi3i9LVU6J89352C0rpMk0as8trXxCtvd4kPk1ke/Eyif6wqfSLvoNJqcDG9Vk4UsXgygzCt2xA==} engines: {node: '>=20.0.0'} jsesc@3.1.0: @@ -3253,6 +3257,10 @@ packages: resolution: {integrity: sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==} engines: {node: 20 || >=22} + minimatch@10.1.2: + resolution: {integrity: sha512-fu656aJ0n2kcXwsnwnv9g24tkU5uSmOlTjd6WyyaKm2Z+h1qmY6bAjrcaIxF/BslFqbZ8UBtbJi7KgQOZD2PTw==} + engines: {node: 20 || >=22} + minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} @@ -3450,13 +3458,13 @@ packages: pkg-types@1.3.1: resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} - playwright-core@1.58.1: - resolution: {integrity: sha512-bcWzOaTxcW+VOOGBCQgnaKToLJ65d6AqfLVKEWvexyS3AS6rbXl+xdpYRMGSRBClPvyj44njOWoxjNdL/H9UNg==} + playwright-core@1.58.2: + resolution: {integrity: sha512-yZkEtftgwS8CsfYo7nm0KE8jsvm6i/PTgVtB8DL726wNf6H2IMsDuxCpJj59KDaxCtSnrWan2AeDqM7JBaultg==} engines: {node: '>=18'} hasBin: true - playwright@1.58.1: - resolution: {integrity: sha512-+2uTZHxSCcxjvGc5C891LrS1/NlxglGxzrC4seZiVjcYVQfUa87wBL6rTDqzGjuoWNjnBzRqKmF6zRYGMvQUaQ==} + playwright@1.58.2: + resolution: {integrity: sha512-vA30H8Nvkq/cPBnNw4Q8TWz1EJyqgpuinBcHET0YVJVFldr8JDNiU9LaWAE1KqSkRYazuaBhTpB5ZzShOezQ6A==} engines: {node: '>=18'} hasBin: true @@ -3713,6 +3721,11 @@ packages: engines: {node: '>=10'} hasBin: true + semver@7.7.4: + resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==} + engines: {node: '>=10'} + hasBin: true + serialize-javascript@6.0.2: resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==} @@ -3812,8 +3825,8 @@ packages: spdx-satisfies@5.0.1: resolution: {integrity: sha512-Nwor6W6gzFp8XX4neaKQ7ChV4wmpSh2sSDemMFSzHxpTw460jxFYeOn+jq4ybnSSw/5sc3pjka9MQPouksQNpw==} - spectral-cli-bundle@1.0.3: - resolution: {integrity: sha512-LUsOK0XKl/C2IhlDwBlXz+7qU2rnGbSlu8nqSFB/K+TbPjjmqoCYjG82YFJCmEHurbthvTJ8WRP735vl+3rY2Q==} + spectral-cli-bundle@1.0.4: + resolution: {integrity: sha512-DfArdHmRj8zMlDclROso5sWQzYzn2FZ616bYPstd/+SWdspP99YuUsWRePR32dd2Vj2ic9kfs2INlL749s9Puw==} engines: {node: '>=20'} hasBin: true @@ -3890,8 +3903,8 @@ packages: peerDependencies: stylelint: '>=16' - stylelint@17.1.0: - resolution: {integrity: sha512-+cUX1FxkkbLX5qJRAPapUv/+v+YU3pGbWu+pHVqTXpiY0mYh3Dxfxa0bLBtVtYgOC8hIWIyX2H/3Y3LWlAevDg==} + stylelint@17.1.1: + resolution: {integrity: sha512-SBHVcLEcRF1M9OkD3oT0hT2PayDNLw2hd+aovmzfNQ2ys4Xd3oS9ZNizILWqhQvW802AqKN/vUTMwJqQYMBlWw==} engines: {node: '>=20.19.0'} hasBin: true @@ -4060,8 +4073,8 @@ packages: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} - typescript-eslint@8.54.0: - resolution: {integrity: sha512-CKsJ+g53QpsNPqbzUsfKVgd3Lny4yKZ1pP4qN3jdMOg/sisIDLGyDMezycquXLE5JsEU0wp3dGNdzig0/fmSVQ==} + typescript-eslint@8.55.0: + resolution: {integrity: sha512-HE4wj+r5lmDVS9gdaN0/+iqNvPZwGfnJ5lZuz7s5vLlg9ODw0bIiiETaios9LvFI1U94/VBXGm3CB2Y5cNFMpw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 @@ -4100,8 +4113,8 @@ packages: peerDependencies: browserslist: '>= 4.21.0' - updates@17.0.9: - resolution: {integrity: sha512-fl+hGvIH+oOBwqFWpkbX2XtZODooOqgr1jcjF/M3/RAVc0P7thuwaNoFpXUSpeJNuKQoyfqm2JFZ2i3ScgG7Lw==} + updates@17.4.0: + resolution: {integrity: sha512-7Lnof1TshAdMw9u/R3Deobcd53sYlaU3ZwULNhOtAVu6DMPEmdi2G7ERnyPZpFThbMLxNX2scCGz3wWTcNjY2Q==} engines: {node: '>=22'} hasBin: true @@ -4118,8 +4131,8 @@ packages: vanilla-colorful@0.7.2: resolution: {integrity: sha512-z2YZusTFC6KnLERx1cgoIRX2CjPRP0W75N+3CC6gbvdX5Ch47rZkEMGO2Xnf+IEmi3RiFLxS18gayMA27iU7Kg==} - vite-string-plugin@2.0.0: - resolution: {integrity: sha512-7p4aOvow1kphhZppt62A6Aj03NhUzOTiBgjHaTWDIdmtiUbjoJeVLarV52UjQNvLFC6BxJgV8r1AAwJfcxwZSQ==} + vite-string-plugin@2.0.1: + resolution: {integrity: sha512-L5B86yQkYrqH5d966w1vI91B0d+0vmICgB6tqjINvtBIGU9qhFY7izqjytED/ApggFC4QTDWNjfF6nWMqY/fQg==} peerDependencies: vite: '*' @@ -4253,8 +4266,8 @@ packages: peerDependencies: typescript: '>=5.0.0' - vue@3.5.27: - resolution: {integrity: sha512-aJ/UtoEyFySPBGarREmN4z6qNKpbEguYHMmXSiOGk69czc+zhs0NF6tEFrY8TZKAl8N/LYAkd4JHVd5E/AsSmw==} + vue@3.5.28: + resolution: {integrity: sha512-BRdrNfeoccSoIZeIhyPBfvWSLFP4q8J3u8Ju8Ug5vu3LdD+yTM13Sg4sKtljxozbnuMu1NB1X5HBHRYUzFocKg==} peerDependencies: typescript: '*' peerDependenciesMeta: @@ -4293,8 +4306,8 @@ packages: resolution: {integrity: sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==} engines: {node: '>=10.13.0'} - webpack@5.104.1: - resolution: {integrity: sha512-Qphch25abbMNtekmEGJmeRUhLDbe+QfiWTiqpKYkpCOWY64v9eyl+KRRLmqOFA2AvKPpc9DC6+u2n76tQLBoaA==} + webpack@5.105.0: + resolution: {integrity: sha512-gX/dMkRQc7QOMzgTe6KsYFM7DxeIONQSui1s0n/0xht36HvrgbxtM1xBlgx596NbpHuQU8P7QpKwrZYwUX48nw==} engines: {node: '>=10.13.0'} hasBin: true peerDependencies: @@ -4386,7 +4399,7 @@ snapshots: package-manager-detector: 1.6.0 tinyexec: 1.0.2 - '@babel/code-frame@7.28.6': + '@babel/code-frame@7.29.0': dependencies: '@babel/helper-validator-identifier': 7.28.5 js-tokens: 4.0.0 @@ -4396,13 +4409,13 @@ snapshots: '@babel/helper-validator-identifier@7.28.5': {} - '@babel/parser@7.28.6': + '@babel/parser@7.29.0': dependencies: - '@babel/types': 7.28.6 + '@babel/types': 7.29.0 '@babel/runtime@7.28.6': {} - '@babel/types@7.28.6': + '@babel/types@7.29.0': dependencies: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.28.5 @@ -4411,12 +4424,12 @@ snapshots: '@cacheable/memory@2.0.7': dependencies: - '@cacheable/utils': 2.3.3 + '@cacheable/utils': 2.3.4 '@keyv/bigmap': 1.3.1(keyv@5.6.0) - hookified: 1.15.0 + hookified: 1.15.1 keyv: 5.6.0 - '@cacheable/utils@2.3.3': + '@cacheable/utils@2.3.4': dependencies: hashery: 1.4.0 keyv: 5.6.0 @@ -4500,7 +4513,7 @@ snapshots: dependencies: '@csstools/css-tokenizer': 4.0.0 - '@csstools/css-syntax-patches-for-csstree@1.0.26': {} + '@csstools/css-syntax-patches-for-csstree@1.0.27': {} '@csstools/css-tokenizer@4.0.0': {} @@ -4535,82 +4548,82 @@ snapshots: tslib: 2.8.1 optional: true - '@esbuild/aix-ppc64@0.27.2': + '@esbuild/aix-ppc64@0.27.3': optional: true - '@esbuild/android-arm64@0.27.2': + '@esbuild/android-arm64@0.27.3': optional: true - '@esbuild/android-arm@0.27.2': + '@esbuild/android-arm@0.27.3': optional: true - '@esbuild/android-x64@0.27.2': + '@esbuild/android-x64@0.27.3': optional: true - '@esbuild/darwin-arm64@0.27.2': + '@esbuild/darwin-arm64@0.27.3': optional: true - '@esbuild/darwin-x64@0.27.2': + '@esbuild/darwin-x64@0.27.3': optional: true - '@esbuild/freebsd-arm64@0.27.2': + '@esbuild/freebsd-arm64@0.27.3': optional: true - '@esbuild/freebsd-x64@0.27.2': + '@esbuild/freebsd-x64@0.27.3': optional: true - '@esbuild/linux-arm64@0.27.2': + '@esbuild/linux-arm64@0.27.3': optional: true - '@esbuild/linux-arm@0.27.2': + '@esbuild/linux-arm@0.27.3': optional: true - '@esbuild/linux-ia32@0.27.2': + '@esbuild/linux-ia32@0.27.3': optional: true - '@esbuild/linux-loong64@0.27.2': + '@esbuild/linux-loong64@0.27.3': optional: true - '@esbuild/linux-mips64el@0.27.2': + '@esbuild/linux-mips64el@0.27.3': optional: true - '@esbuild/linux-ppc64@0.27.2': + '@esbuild/linux-ppc64@0.27.3': optional: true - '@esbuild/linux-riscv64@0.27.2': + '@esbuild/linux-riscv64@0.27.3': optional: true - '@esbuild/linux-s390x@0.27.2': + '@esbuild/linux-s390x@0.27.3': optional: true - '@esbuild/linux-x64@0.27.2': + '@esbuild/linux-x64@0.27.3': optional: true - '@esbuild/netbsd-arm64@0.27.2': + '@esbuild/netbsd-arm64@0.27.3': optional: true - '@esbuild/netbsd-x64@0.27.2': + '@esbuild/netbsd-x64@0.27.3': optional: true - '@esbuild/openbsd-arm64@0.27.2': + '@esbuild/openbsd-arm64@0.27.3': optional: true - '@esbuild/openbsd-x64@0.27.2': + '@esbuild/openbsd-x64@0.27.3': optional: true - '@esbuild/openharmony-arm64@0.27.2': + '@esbuild/openharmony-arm64@0.27.3': optional: true - '@esbuild/sunos-x64@0.27.2': + '@esbuild/sunos-x64@0.27.3': optional: true - '@esbuild/win32-arm64@0.27.2': + '@esbuild/win32-arm64@0.27.3': optional: true - '@esbuild/win32-ia32@0.27.2': + '@esbuild/win32-ia32@0.27.3': optional: true - '@esbuild/win32-x64@0.27.2': + '@esbuild/win32-x64@0.27.3': optional: true '@eslint-community/eslint-plugin-eslint-comments@4.6.0(eslint@9.39.2(jiti@2.6.1))': @@ -4716,7 +4729,7 @@ snapshots: '@isaacs/balanced-match@4.0.1': {} - '@isaacs/brace-expansion@5.0.0': + '@isaacs/brace-expansion@5.0.1': dependencies: '@isaacs/balanced-match': 4.0.1 @@ -4742,7 +4755,7 @@ snapshots: '@keyv/bigmap@1.3.1(keyv@5.6.0)': dependencies: hashery: 1.4.0 - hookified: 1.15.0 + hookified: 1.15.1 keyv: 5.6.0 '@keyv/serialize@1.1.1': {} @@ -4838,9 +4851,9 @@ snapshots: '@pkgr/core@0.2.9': {} - '@playwright/test@1.58.1': + '@playwright/test@1.58.2': dependencies: - playwright: 1.58.1 + playwright: 1.58.2 '@popperjs/core@2.11.8': {} @@ -4850,7 +4863,7 @@ snapshots: '@resvg/resvg-wasm@2.6.2': {} - '@rolldown/pluginutils@1.0.0-beta.53': {} + '@rolldown/pluginutils@1.0.0-rc.2': {} '@rollup/rollup-android-arm-eabi@4.57.1': optional: true @@ -4931,10 +4944,10 @@ snapshots: '@scarf/scarf@1.4.0': {} - '@silverwind/vue3-calendar-heatmap@2.1.1(tippy.js@6.3.7)(vue@3.5.27(typescript@5.9.3))': + '@silverwind/vue3-calendar-heatmap@2.1.1(tippy.js@6.3.7)(vue@3.5.28(typescript@5.9.3))': dependencies: tippy.js: 6.3.7 - vue: 3.5.27(typescript@5.9.3) + vue: 3.5.28(typescript@5.9.3) '@simonwep/pickr@1.9.0': dependencies: @@ -4958,17 +4971,17 @@ snapshots: '@standard-schema/spec@1.1.0': {} - '@stylistic/eslint-plugin@5.7.1(eslint@9.39.2(jiti@2.6.1))': + '@stylistic/eslint-plugin@5.8.0(eslint@9.39.2(jiti@2.6.1))': dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2(jiti@2.6.1)) - '@typescript-eslint/types': 8.54.0 + '@typescript-eslint/types': 8.55.0 eslint: 9.39.2(jiti@2.6.1) eslint-visitor-keys: 4.2.1 espree: 10.4.0 estraverse: 5.3.0 picomatch: 4.0.3 - '@stylistic/stylelint-plugin@5.0.1(stylelint@17.1.0(typescript@5.9.3))': + '@stylistic/stylelint-plugin@5.0.1(stylelint@17.1.1(typescript@5.9.3))': dependencies: '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 @@ -4977,11 +4990,11 @@ snapshots: postcss-selector-parser: 7.1.1 postcss-value-parser: 4.2.0 style-search: 0.1.0 - stylelint: 17.1.0(typescript@5.9.3) + stylelint: 17.1.1(typescript@5.9.3) '@swc/helpers@0.2.14': {} - '@techknowlogick/license-checker-webpack-plugin@0.3.0(webpack@5.104.1)': + '@techknowlogick/license-checker-webpack-plugin@0.3.0(webpack@5.105.0)': dependencies: glob: 7.2.3 lodash: 4.17.23 @@ -4990,7 +5003,7 @@ snapshots: spdx-expression-validate: 2.0.0 spdx-satisfies: 5.0.1 superstruct: 0.10.13 - webpack: 5.104.1(webpack-cli@6.0.1) + webpack: 5.105.0(webpack-cli@6.0.1) webpack-sources: 1.4.3 wrap-ansi: 6.2.0 @@ -5167,7 +5180,7 @@ snapshots: '@types/ms@2.1.0': {} - '@types/node@25.1.0': + '@types/node@25.2.2': dependencies: undici-types: 7.16.0 @@ -5198,16 +5211,16 @@ snapshots: '@types/ws@8.18.1': dependencies: - '@types/node': 25.1.0 + '@types/node': 25.2.2 - '@typescript-eslint/eslint-plugin@8.54.0(@typescript-eslint/parser@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/eslint-plugin@8.55.0(@typescript-eslint/parser@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/scope-manager': 8.54.0 - '@typescript-eslint/type-utils': 8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/utils': 8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.54.0 + '@typescript-eslint/parser': 8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.55.0 + '@typescript-eslint/type-utils': 8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/utils': 8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.55.0 eslint: 9.39.2(jiti@2.6.1) ignore: 7.0.5 natural-compare: 1.4.0 @@ -5216,41 +5229,41 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/parser@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)': dependencies: - '@typescript-eslint/scope-manager': 8.54.0 - '@typescript-eslint/types': 8.54.0 - '@typescript-eslint/typescript-estree': 8.54.0(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.54.0 + '@typescript-eslint/scope-manager': 8.55.0 + '@typescript-eslint/types': 8.55.0 + '@typescript-eslint/typescript-estree': 8.55.0(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.55.0 debug: 4.4.3 eslint: 9.39.2(jiti@2.6.1) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.54.0(typescript@5.9.3)': + '@typescript-eslint/project-service@8.55.0(typescript@5.9.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.54.0(typescript@5.9.3) - '@typescript-eslint/types': 8.54.0 + '@typescript-eslint/tsconfig-utils': 8.55.0(typescript@5.9.3) + '@typescript-eslint/types': 8.55.0 debug: 4.4.3 typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@8.54.0': + '@typescript-eslint/scope-manager@8.55.0': dependencies: - '@typescript-eslint/types': 8.54.0 - '@typescript-eslint/visitor-keys': 8.54.0 + '@typescript-eslint/types': 8.55.0 + '@typescript-eslint/visitor-keys': 8.55.0 - '@typescript-eslint/tsconfig-utils@8.54.0(typescript@5.9.3)': + '@typescript-eslint/tsconfig-utils@8.55.0(typescript@5.9.3)': dependencies: typescript: 5.9.3 - '@typescript-eslint/type-utils@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/type-utils@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)': dependencies: - '@typescript-eslint/types': 8.54.0 - '@typescript-eslint/typescript-estree': 8.54.0(typescript@5.9.3) - '@typescript-eslint/utils': 8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/types': 8.55.0 + '@typescript-eslint/typescript-estree': 8.55.0(typescript@5.9.3) + '@typescript-eslint/utils': 8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) debug: 4.4.3 eslint: 9.39.2(jiti@2.6.1) ts-api-utils: 2.4.0(typescript@5.9.3) @@ -5258,37 +5271,37 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/types@8.54.0': {} + '@typescript-eslint/types@8.55.0': {} - '@typescript-eslint/typescript-estree@8.54.0(typescript@5.9.3)': + '@typescript-eslint/typescript-estree@8.55.0(typescript@5.9.3)': dependencies: - '@typescript-eslint/project-service': 8.54.0(typescript@5.9.3) - '@typescript-eslint/tsconfig-utils': 8.54.0(typescript@5.9.3) - '@typescript-eslint/types': 8.54.0 - '@typescript-eslint/visitor-keys': 8.54.0 + '@typescript-eslint/project-service': 8.55.0(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.55.0(typescript@5.9.3) + '@typescript-eslint/types': 8.55.0 + '@typescript-eslint/visitor-keys': 8.55.0 debug: 4.4.3 minimatch: 9.0.5 - semver: 7.7.3 + semver: 7.7.4 tinyglobby: 0.2.15 ts-api-utils: 2.4.0(typescript@5.9.3) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/utils@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)': dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2(jiti@2.6.1)) - '@typescript-eslint/scope-manager': 8.54.0 - '@typescript-eslint/types': 8.54.0 - '@typescript-eslint/typescript-estree': 8.54.0(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.55.0 + '@typescript-eslint/types': 8.55.0 + '@typescript-eslint/typescript-estree': 8.55.0(typescript@5.9.3) eslint: 9.39.2(jiti@2.6.1) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/visitor-keys@8.54.0': + '@typescript-eslint/visitor-keys@8.55.0': dependencies: - '@typescript-eslint/types': 8.54.0 + '@typescript-eslint/types': 8.55.0 eslint-visitor-keys: 4.2.1 '@unrs/resolver-binding-android-arm-eabi@1.11.1': @@ -5350,20 +5363,20 @@ snapshots: '@unrs/resolver-binding-win32-x64-msvc@1.11.1': optional: true - '@vitejs/plugin-vue@6.0.3(vite@7.3.1(@types/node@25.1.0)(jiti@2.6.1)(stylus@0.57.0)(terser@5.46.0)(yaml@2.8.2))(vue@3.5.27(typescript@5.9.3))': + '@vitejs/plugin-vue@6.0.4(vite@7.3.1(@types/node@25.2.2)(jiti@2.6.1)(stylus@0.57.0)(terser@5.46.0)(yaml@2.8.2))(vue@3.5.28(typescript@5.9.3))': dependencies: - '@rolldown/pluginutils': 1.0.0-beta.53 - vite: 7.3.1(@types/node@25.1.0)(jiti@2.6.1)(stylus@0.57.0)(terser@5.46.0)(yaml@2.8.2) - vue: 3.5.27(typescript@5.9.3) + '@rolldown/pluginutils': 1.0.0-rc.2 + vite: 7.3.1(@types/node@25.2.2)(jiti@2.6.1)(stylus@0.57.0)(terser@5.46.0)(yaml@2.8.2) + vue: 3.5.28(typescript@5.9.3) - '@vitest/eslint-plugin@1.6.6(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)(vitest@4.0.18(@types/node@25.1.0)(happy-dom@20.4.0)(jiti@2.6.1)(stylus@0.57.0)(terser@5.46.0)(yaml@2.8.2))': + '@vitest/eslint-plugin@1.6.7(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)(vitest@4.0.18(@types/node@25.2.2)(happy-dom@20.6.0)(jiti@2.6.1)(stylus@0.57.0)(terser@5.46.0)(yaml@2.8.2))': dependencies: - '@typescript-eslint/scope-manager': 8.54.0 - '@typescript-eslint/utils': 8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.55.0 + '@typescript-eslint/utils': 8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) eslint: 9.39.2(jiti@2.6.1) optionalDependencies: typescript: 5.9.3 - vitest: 4.0.18(@types/node@25.1.0)(happy-dom@20.4.0)(jiti@2.6.1)(stylus@0.57.0)(terser@5.46.0)(yaml@2.8.2) + vitest: 4.0.18(@types/node@25.2.2)(happy-dom@20.6.0)(jiti@2.6.1)(stylus@0.57.0)(terser@5.46.0)(yaml@2.8.2) transitivePeerDependencies: - supports-color @@ -5376,13 +5389,13 @@ snapshots: chai: 6.2.2 tinyrainbow: 3.0.3 - '@vitest/mocker@4.0.18(vite@7.3.1(@types/node@25.1.0)(jiti@2.6.1)(stylus@0.57.0)(terser@5.46.0)(yaml@2.8.2))': + '@vitest/mocker@4.0.18(vite@7.3.1(@types/node@25.2.2)(jiti@2.6.1)(stylus@0.57.0)(terser@5.46.0)(yaml@2.8.2))': dependencies: '@vitest/spy': 4.0.18 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 7.3.1(@types/node@25.1.0)(jiti@2.6.1)(stylus@0.57.0)(terser@5.46.0)(yaml@2.8.2) + vite: 7.3.1(@types/node@25.2.2)(jiti@2.6.1)(stylus@0.57.0)(terser@5.46.0)(yaml@2.8.2) '@vitest/pretty-format@4.0.18': dependencies: @@ -5418,69 +5431,69 @@ snapshots: path-browserify: 1.0.1 vscode-uri: 3.1.0 - '@vue/compiler-core@3.5.27': + '@vue/compiler-core@3.5.28': dependencies: - '@babel/parser': 7.28.6 - '@vue/shared': 3.5.27 + '@babel/parser': 7.29.0 + '@vue/shared': 3.5.28 entities: 7.0.1 estree-walker: 2.0.2 source-map-js: 1.2.1 - '@vue/compiler-dom@3.5.27': + '@vue/compiler-dom@3.5.28': dependencies: - '@vue/compiler-core': 3.5.27 - '@vue/shared': 3.5.27 + '@vue/compiler-core': 3.5.28 + '@vue/shared': 3.5.28 - '@vue/compiler-sfc@3.5.27': + '@vue/compiler-sfc@3.5.28': dependencies: - '@babel/parser': 7.28.6 - '@vue/compiler-core': 3.5.27 - '@vue/compiler-dom': 3.5.27 - '@vue/compiler-ssr': 3.5.27 - '@vue/shared': 3.5.27 + '@babel/parser': 7.29.0 + '@vue/compiler-core': 3.5.28 + '@vue/compiler-dom': 3.5.28 + '@vue/compiler-ssr': 3.5.28 + '@vue/shared': 3.5.28 estree-walker: 2.0.2 magic-string: 0.30.21 postcss: 8.5.6 source-map-js: 1.2.1 - '@vue/compiler-ssr@3.5.27': + '@vue/compiler-ssr@3.5.28': dependencies: - '@vue/compiler-dom': 3.5.27 - '@vue/shared': 3.5.27 + '@vue/compiler-dom': 3.5.28 + '@vue/shared': 3.5.28 '@vue/language-core@3.2.4': dependencies: '@volar/language-core': 2.4.27 - '@vue/compiler-dom': 3.5.27 - '@vue/shared': 3.5.27 + '@vue/compiler-dom': 3.5.28 + '@vue/shared': 3.5.28 alien-signals: 3.1.2 muggle-string: 0.4.1 path-browserify: 1.0.1 picomatch: 4.0.3 - '@vue/reactivity@3.5.27': + '@vue/reactivity@3.5.28': dependencies: - '@vue/shared': 3.5.27 + '@vue/shared': 3.5.28 - '@vue/runtime-core@3.5.27': + '@vue/runtime-core@3.5.28': dependencies: - '@vue/reactivity': 3.5.27 - '@vue/shared': 3.5.27 + '@vue/reactivity': 3.5.28 + '@vue/shared': 3.5.28 - '@vue/runtime-dom@3.5.27': + '@vue/runtime-dom@3.5.28': dependencies: - '@vue/reactivity': 3.5.27 - '@vue/runtime-core': 3.5.27 - '@vue/shared': 3.5.27 + '@vue/reactivity': 3.5.28 + '@vue/runtime-core': 3.5.28 + '@vue/shared': 3.5.28 csstype: 3.2.3 - '@vue/server-renderer@3.5.27(vue@3.5.27(typescript@5.9.3))': + '@vue/server-renderer@3.5.28(vue@3.5.28(typescript@5.9.3))': dependencies: - '@vue/compiler-ssr': 3.5.27 - '@vue/shared': 3.5.27 - vue: 3.5.27(typescript@5.9.3) + '@vue/compiler-ssr': 3.5.28 + '@vue/shared': 3.5.28 + vue: 3.5.28(typescript@5.9.3) - '@vue/shared@3.5.27': {} + '@vue/shared@3.5.28': {} '@webassemblyjs/ast@1.14.1': dependencies: @@ -5558,20 +5571,20 @@ snapshots: '@webassemblyjs/ast': 1.14.1 '@xtuc/long': 4.2.2 - '@webpack-cli/configtest@3.0.1(webpack-cli@6.0.1)(webpack@5.104.1)': + '@webpack-cli/configtest@3.0.1(webpack-cli@6.0.1)(webpack@5.105.0)': dependencies: - webpack: 5.104.1(webpack-cli@6.0.1) - webpack-cli: 6.0.1(webpack@5.104.1) + webpack: 5.105.0(webpack-cli@6.0.1) + webpack-cli: 6.0.1(webpack@5.105.0) - '@webpack-cli/info@3.0.1(webpack-cli@6.0.1)(webpack@5.104.1)': + '@webpack-cli/info@3.0.1(webpack-cli@6.0.1)(webpack@5.105.0)': dependencies: - webpack: 5.104.1(webpack-cli@6.0.1) - webpack-cli: 6.0.1(webpack@5.104.1) + webpack: 5.105.0(webpack-cli@6.0.1) + webpack-cli: 6.0.1(webpack@5.105.0) - '@webpack-cli/serve@3.0.1(webpack-cli@6.0.1)(webpack@5.104.1)': + '@webpack-cli/serve@3.0.1(webpack-cli@6.0.1)(webpack@5.105.0)': dependencies: - webpack: 5.104.1(webpack-cli@6.0.1) - webpack-cli: 6.0.1(webpack@5.104.1) + webpack: 5.105.0(webpack-cli@6.0.1) + webpack-cli: 6.0.1(webpack@5.105.0) '@xtuc/ieee754@1.2.0': {} @@ -5587,9 +5600,9 @@ snapshots: acorn@8.15.0: {} - add-asset-webpack-plugin@3.1.1(webpack@5.104.1): + add-asset-webpack-plugin@3.1.1(webpack@5.105.0): optionalDependencies: - webpack: 5.104.1(webpack-cli@6.0.1) + webpack: 5.105.0(webpack-cli@6.0.1) ajv-formats@2.1.1(ajv@8.17.1): optionalDependencies: @@ -5691,8 +5704,8 @@ snapshots: browserslist@4.28.1: dependencies: baseline-browser-mapping: 2.9.19 - caniuse-lite: 1.0.30001766 - electron-to-chromium: 1.5.283 + caniuse-lite: 1.0.30001769 + electron-to-chromium: 1.5.286 node-releases: 2.0.27 update-browserslist-db: 1.2.3(browserslist@4.28.1) @@ -5712,8 +5725,8 @@ snapshots: cacheable@2.3.2: dependencies: '@cacheable/memory': 2.0.7 - '@cacheable/utils': 2.3.3 - hookified: 1.15.0 + '@cacheable/utils': 2.3.4 + hookified: 1.15.1 keyv: 5.6.0 qified: 0.6.0 @@ -5721,7 +5734,7 @@ snapshots: camelcase-css@2.0.1: {} - caniuse-lite@1.0.30001766: {} + caniuse-lite@1.0.30001769: {} chai@6.2.2: {} @@ -5791,7 +5804,7 @@ snapshots: dependencies: escape-string-regexp: 1.0.5 - clippie@4.1.9: {} + clippie@4.1.10: {} clone-deep@4.0.1: dependencies: @@ -5803,7 +5816,7 @@ snapshots: dependencies: typo-js: 1.3.1 - codemirror@5.65.20: {} + codemirror@5.65.21: {} color-convert@2.0.1: dependencies: @@ -5819,7 +5832,7 @@ snapshots: commander@12.1.0: {} - commander@14.0.2: {} + commander@14.0.3: {} commander@2.20.3: {} @@ -5870,7 +5883,7 @@ snapshots: css-functions-list@3.2.3: {} - css-loader@7.1.3(webpack@5.104.1): + css-loader@7.1.3(webpack@5.105.0): dependencies: icss-utils: 5.1.0(postcss@8.5.6) postcss: 8.5.6 @@ -5879,9 +5892,9 @@ snapshots: postcss-modules-scope: 3.2.1(postcss@8.5.6) postcss-modules-values: 4.0.0(postcss@8.5.6) postcss-value-parser: 4.2.0 - semver: 7.7.3 + semver: 7.7.4 optionalDependencies: - webpack: 5.104.1(webpack-cli@6.0.1) + webpack: 5.105.0(webpack-cli@6.0.1) css-select@5.2.2: dependencies: @@ -6183,11 +6196,11 @@ snapshots: dependencies: '@types/codemirror': 5.60.17 '@types/marked': 4.3.2 - codemirror: 5.65.20 + codemirror: 5.65.21 codemirror-spell-checker: 1.1.2 marked: 4.3.0 - electron-to-chromium@1.5.283: {} + electron-to-chromium@1.5.286: {} elkjs@0.9.3: {} @@ -6199,13 +6212,15 @@ snapshots: emojis-list@3.0.0: {} - enhanced-resolve@5.18.4: + enhanced-resolve@5.19.0: dependencies: graceful-fs: 4.2.11 tapable: 2.3.0 entities@4.5.0: {} + entities@6.0.1: {} + entities@7.0.1: {} env-paths@2.2.1: {} @@ -6220,42 +6235,42 @@ snapshots: es-module-lexer@2.0.0: {} - esbuild-loader@4.4.2(webpack@5.104.1): + esbuild-loader@4.4.2(webpack@5.105.0): dependencies: - esbuild: 0.27.2 - get-tsconfig: 4.13.1 + esbuild: 0.27.3 + get-tsconfig: 4.13.6 loader-utils: 2.0.4 - webpack: 5.104.1(webpack-cli@6.0.1) + webpack: 5.105.0(webpack-cli@6.0.1) webpack-sources: 1.4.3 - esbuild@0.27.2: + esbuild@0.27.3: optionalDependencies: - '@esbuild/aix-ppc64': 0.27.2 - '@esbuild/android-arm': 0.27.2 - '@esbuild/android-arm64': 0.27.2 - '@esbuild/android-x64': 0.27.2 - '@esbuild/darwin-arm64': 0.27.2 - '@esbuild/darwin-x64': 0.27.2 - '@esbuild/freebsd-arm64': 0.27.2 - '@esbuild/freebsd-x64': 0.27.2 - '@esbuild/linux-arm': 0.27.2 - '@esbuild/linux-arm64': 0.27.2 - '@esbuild/linux-ia32': 0.27.2 - '@esbuild/linux-loong64': 0.27.2 - '@esbuild/linux-mips64el': 0.27.2 - '@esbuild/linux-ppc64': 0.27.2 - '@esbuild/linux-riscv64': 0.27.2 - '@esbuild/linux-s390x': 0.27.2 - '@esbuild/linux-x64': 0.27.2 - '@esbuild/netbsd-arm64': 0.27.2 - '@esbuild/netbsd-x64': 0.27.2 - '@esbuild/openbsd-arm64': 0.27.2 - '@esbuild/openbsd-x64': 0.27.2 - '@esbuild/openharmony-arm64': 0.27.2 - '@esbuild/sunos-x64': 0.27.2 - '@esbuild/win32-arm64': 0.27.2 - '@esbuild/win32-ia32': 0.27.2 - '@esbuild/win32-x64': 0.27.2 + '@esbuild/aix-ppc64': 0.27.3 + '@esbuild/android-arm': 0.27.3 + '@esbuild/android-arm64': 0.27.3 + '@esbuild/android-x64': 0.27.3 + '@esbuild/darwin-arm64': 0.27.3 + '@esbuild/darwin-x64': 0.27.3 + '@esbuild/freebsd-arm64': 0.27.3 + '@esbuild/freebsd-x64': 0.27.3 + '@esbuild/linux-arm': 0.27.3 + '@esbuild/linux-arm64': 0.27.3 + '@esbuild/linux-ia32': 0.27.3 + '@esbuild/linux-loong64': 0.27.3 + '@esbuild/linux-mips64el': 0.27.3 + '@esbuild/linux-ppc64': 0.27.3 + '@esbuild/linux-riscv64': 0.27.3 + '@esbuild/linux-s390x': 0.27.3 + '@esbuild/linux-x64': 0.27.3 + '@esbuild/netbsd-arm64': 0.27.3 + '@esbuild/netbsd-x64': 0.27.3 + '@esbuild/openbsd-arm64': 0.27.3 + '@esbuild/openbsd-x64': 0.27.3 + '@esbuild/openharmony-arm64': 0.27.3 + '@esbuild/sunos-x64': 0.27.3 + '@esbuild/win32-arm64': 0.27.3 + '@esbuild/win32-ia32': 0.27.3 + '@esbuild/win32-x64': 0.27.3 escalade@3.2.0: {} @@ -6266,7 +6281,7 @@ snapshots: eslint-compat-utils@0.6.5(eslint@9.39.2(jiti@2.6.1)): dependencies: eslint: 9.39.2(jiti@2.6.1) - semver: 7.7.3 + semver: 7.7.4 eslint-config-prettier@10.1.8(eslint@9.39.2(jiti@2.6.1)): dependencies: @@ -6274,7 +6289,7 @@ snapshots: eslint-import-context@0.1.9(unrs-resolver@1.11.1): dependencies: - get-tsconfig: 4.13.1 + get-tsconfig: 4.13.6 stable-hash-x: 0.2.0 optionalDependencies: unrs-resolver: 1.11.1 @@ -6287,30 +6302,30 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@4.4.4(eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.2(jiti@2.6.1)))(eslint-plugin-import@2.32.0)(eslint@9.39.2(jiti@2.6.1)): + eslint-import-resolver-typescript@4.4.4(eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.2(jiti@2.6.1)))(eslint-plugin-import@2.32.0)(eslint@9.39.2(jiti@2.6.1)): dependencies: debug: 4.4.3 eslint: 9.39.2(jiti@2.6.1) eslint-import-context: 0.1.9(unrs-resolver@1.11.1) - get-tsconfig: 4.13.1 + get-tsconfig: 4.13.6 is-bun-module: 2.0.0 stable-hash-x: 0.2.0 tinyglobby: 0.2.15 unrs-resolver: 1.11.1 optionalDependencies: - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@4.4.4)(eslint@9.39.2(jiti@2.6.1)) - eslint-plugin-import-x: 4.16.1(@typescript-eslint/utils@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.2(jiti@2.6.1)) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@4.4.4)(eslint@9.39.2(jiti@2.6.1)) + eslint-plugin-import-x: 4.16.1(@typescript-eslint/utils@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.2(jiti@2.6.1)) transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.1(@typescript-eslint/parser@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@4.4.4)(eslint@9.39.2(jiti@2.6.1)): + eslint-module-utils@2.12.1(@typescript-eslint/parser@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@4.4.4)(eslint@9.39.2(jiti@2.6.1)): dependencies: debug: 3.2.7 optionalDependencies: - '@typescript-eslint/parser': 8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/parser': 8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) eslint: 9.39.2(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 4.4.4(eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.2(jiti@2.6.1)))(eslint-plugin-import@2.32.0)(eslint@9.39.2(jiti@2.6.1)) + eslint-import-resolver-typescript: 4.4.4(eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.2(jiti@2.6.1)))(eslint-plugin-import@2.32.0)(eslint@9.39.2(jiti@2.6.1)) transitivePeerDependencies: - supports-color @@ -6343,8 +6358,8 @@ snapshots: '@eslint/eslintrc': 3.3.3 '@eslint/js': 9.39.2 '@github/browserslist-config': 1.0.0 - '@typescript-eslint/eslint-plugin': 8.54.0(@typescript-eslint/parser@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/parser': 8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/eslint-plugin': 8.55.0(@typescript-eslint/parser@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/parser': 8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) aria-query: 5.3.2 eslint: 9.39.2(jiti@2.6.1) eslint-config-prettier: 10.1.8(eslint@9.39.2(jiti@2.6.1)) @@ -6352,7 +6367,7 @@ snapshots: eslint-plugin-eslint-comments: 3.2.0(eslint@9.39.2(jiti@2.6.1)) eslint-plugin-filenames: 1.3.2(eslint@9.39.2(jiti@2.6.1)) eslint-plugin-i18n-text: 1.0.1(eslint@9.39.2(jiti@2.6.1)) - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@4.4.4)(eslint@9.39.2(jiti@2.6.1)) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@4.4.4)(eslint@9.39.2(jiti@2.6.1)) eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.2(jiti@2.6.1)) eslint-plugin-no-only-tests: 3.3.0 eslint-plugin-prettier: 5.5.5(@types/eslint@9.6.1)(eslint-config-prettier@10.1.8(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1))(prettier@3.8.1) @@ -6362,7 +6377,7 @@ snapshots: prettier: 3.8.1 svg-element-attributes: 1.3.1 typescript: 5.9.3 - typescript-eslint: 8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + typescript-eslint: 8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) transitivePeerDependencies: - '@types/eslint' - eslint-import-resolver-typescript @@ -6373,25 +6388,25 @@ snapshots: dependencies: eslint: 9.39.2(jiti@2.6.1) - eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.2(jiti@2.6.1)): + eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.2(jiti@2.6.1)): dependencies: - '@typescript-eslint/types': 8.54.0 + '@typescript-eslint/types': 8.55.0 comment-parser: 1.4.5 debug: 4.4.3 eslint: 9.39.2(jiti@2.6.1) eslint-import-context: 0.1.9(unrs-resolver@1.11.1) is-glob: 4.0.3 - minimatch: 10.1.1 - semver: 7.7.3 + minimatch: 10.1.2 + semver: 7.7.4 stable-hash-x: 0.2.0 unrs-resolver: 1.11.1 optionalDependencies: - '@typescript-eslint/utils': 8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/utils': 8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) eslint-import-resolver-node: 0.3.9 transitivePeerDependencies: - supports-color - eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@4.4.4)(eslint@9.39.2(jiti@2.6.1)): + eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@4.4.4)(eslint@9.39.2(jiti@2.6.1)): dependencies: '@rtsao/scc': 1.1.0 array-includes: '@nolyfill/array-includes@1.0.44' @@ -6402,7 +6417,7 @@ snapshots: doctrine: 2.1.0 eslint: 9.39.2(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@4.4.4)(eslint@9.39.2(jiti@2.6.1)) + eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@4.4.4)(eslint@9.39.2(jiti@2.6.1)) hasown: '@nolyfill/hasown@1.0.44' is-core-module: '@nolyfill/is-core-module@1.0.39' is-glob: 4.0.3 @@ -6414,7 +6429,7 @@ snapshots: string.prototype.trimend: '@nolyfill/string.prototype.trimend@1.0.44' tsconfig-paths: 3.15.0 optionalDependencies: - '@typescript-eslint/parser': 8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/parser': 8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack @@ -6462,7 +6477,7 @@ snapshots: '@eslint-community/regexpp': 4.12.2 comment-parser: 1.4.5 eslint: 9.39.2(jiti@2.6.1) - jsdoc-type-pratt-parser: 7.1.0 + jsdoc-type-pratt-parser: 7.1.1 refa: 0.12.1 regexp-ast-analysis: 0.7.1 scslre: 0.3.0 @@ -6500,7 +6515,7 @@ snapshots: pluralize: 8.0.0 regexp-tree: 0.1.27 regjsparser: 0.13.0 - semver: 7.7.3 + semver: 7.7.4 strip-indent: 4.1.1 eslint-plugin-vue-scoped-css@2.12.0(eslint@9.39.2(jiti@2.6.1))(vue-eslint-parser@10.2.0(eslint@9.39.2(jiti@2.6.1))): @@ -6518,21 +6533,21 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-plugin-vue@10.7.0(@stylistic/eslint-plugin@5.7.1(eslint@9.39.2(jiti@2.6.1)))(@typescript-eslint/parser@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(vue-eslint-parser@10.2.0(eslint@9.39.2(jiti@2.6.1))): + eslint-plugin-vue@10.7.0(@stylistic/eslint-plugin@5.8.0(eslint@9.39.2(jiti@2.6.1)))(@typescript-eslint/parser@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(vue-eslint-parser@10.2.0(eslint@9.39.2(jiti@2.6.1))): dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2(jiti@2.6.1)) eslint: 9.39.2(jiti@2.6.1) natural-compare: 1.4.0 nth-check: 2.1.1 postcss-selector-parser: 7.1.1 - semver: 7.7.3 + semver: 7.7.4 vue-eslint-parser: 10.2.0(eslint@9.39.2(jiti@2.6.1)) xml-name-validator: 4.0.0 optionalDependencies: - '@stylistic/eslint-plugin': 5.7.1(eslint@9.39.2(jiti@2.6.1)) - '@typescript-eslint/parser': 8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + '@stylistic/eslint-plugin': 5.8.0(eslint@9.39.2(jiti@2.6.1)) + '@typescript-eslint/parser': 8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) - eslint-plugin-wc@3.0.2(eslint@9.39.2(jiti@2.6.1)): + eslint-plugin-wc@3.1.0(eslint@9.39.2(jiti@2.6.1)): dependencies: eslint: 9.39.2(jiti@2.6.1) is-valid-element-name: 1.0.0 @@ -6696,7 +6711,7 @@ snapshots: dependencies: cacheable: 2.3.2 flatted: 3.3.3 - hookified: 1.15.0 + hookified: 1.15.1 flat@5.0.2: {} @@ -6714,7 +6729,7 @@ snapshots: get-east-asian-width@1.4.0: {} - get-tsconfig@4.13.1: + get-tsconfig@4.13.6: dependencies: resolve-pkg-maps: 1.0.0 @@ -6751,7 +6766,7 @@ snapshots: globals@16.5.0: {} - globals@17.2.0: {} + globals@17.3.0: {} globby@16.1.0: dependencies: @@ -6770,12 +6785,12 @@ snapshots: hammerjs@2.0.8: {} - happy-dom@20.4.0: + happy-dom@20.6.0: dependencies: - '@types/node': 25.1.0 + '@types/node': 25.2.2 '@types/whatwg-mimetype': 3.0.2 '@types/ws': 8.18.1 - entities: 4.5.0 + entities: 6.0.1 whatwg-mimetype: 3.0.0 ws: 8.19.0 transitivePeerDependencies: @@ -6790,9 +6805,9 @@ snapshots: hashery@1.4.0: dependencies: - hookified: 1.15.0 + hookified: 1.15.1 - hookified@1.15.0: {} + hookified@1.15.1: {} html-tags@5.1.0: {} @@ -6875,7 +6890,7 @@ snapshots: is-bun-module@2.0.0: dependencies: - semver: 7.7.3 + semver: 7.7.4 is-decimal@2.0.1: {} @@ -6911,7 +6926,7 @@ snapshots: jest-worker@27.5.1: dependencies: - '@types/node': 25.1.0 + '@types/node': 25.2.2 merge-stream: 2.0.0 supports-color: 8.1.1 @@ -6931,7 +6946,7 @@ snapshots: dependencies: argparse: 2.0.1 - jsdoc-type-pratt-parser@7.1.0: {} + jsdoc-type-pratt-parser@7.1.1: {} jsesc@3.1.0: {} @@ -7070,7 +7085,7 @@ snapshots: markdownlint-cli@0.47.0: dependencies: - commander: 14.0.2 + commander: 14.0.3 deep-extend: 0.6.0 ignore: 7.0.5 js-yaml: 4.1.1 @@ -7078,7 +7093,7 @@ snapshots: jsonpointer: 5.0.1 markdown-it: 14.1.0 markdownlint: 0.40.0 - minimatch: 10.1.1 + minimatch: 10.1.2 run-con: 1.3.2 smol-toml: 1.5.2 tinyglobby: 0.2.15 @@ -7332,15 +7347,19 @@ snapshots: dependencies: mime-db: 1.52.0 - mini-css-extract-plugin@2.10.0(webpack@5.104.1): + mini-css-extract-plugin@2.10.0(webpack@5.105.0): dependencies: schema-utils: 4.3.3 tapable: 2.3.0 - webpack: 5.104.1(webpack-cli@6.0.1) + webpack: 5.105.0(webpack-cli@6.0.1) minimatch@10.1.1: dependencies: - '@isaacs/brace-expansion': 5.0.0 + '@isaacs/brace-expansion': 5.0.1 + + minimatch@10.1.2: + dependencies: + '@isaacs/brace-expansion': 5.0.1 minimatch@3.1.2: dependencies: @@ -7359,11 +7378,11 @@ snapshots: pkg-types: 1.3.1 ufo: 1.6.3 - monaco-editor-webpack-plugin@7.1.1(monaco-editor@0.55.1)(webpack@5.104.1): + monaco-editor-webpack-plugin@7.1.1(monaco-editor@0.55.1)(webpack@5.105.0): dependencies: loader-utils: 2.0.4 monaco-editor: 0.55.1 - webpack: 5.104.1(webpack-cli@6.0.1) + webpack: 5.105.0(webpack-cli@6.0.1) monaco-editor@0.55.1: dependencies: @@ -7471,7 +7490,7 @@ snapshots: parse-json@5.2.0: dependencies: - '@babel/code-frame': 7.28.6 + '@babel/code-frame': 7.29.0 error-ex: 1.3.4 json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 @@ -7514,11 +7533,11 @@ snapshots: mlly: 1.8.0 pathe: 2.0.3 - playwright-core@1.58.1: {} + playwright-core@1.58.2: {} - playwright@1.58.1: + playwright@1.58.2: dependencies: - playwright-core: 1.58.1 + playwright-core: 1.58.2 optionalDependencies: fsevents: 2.3.2 @@ -7557,14 +7576,14 @@ snapshots: optionalDependencies: postcss: 8.5.6 - postcss-loader@8.2.0(postcss@8.5.6)(typescript@5.9.3)(webpack@5.104.1): + postcss-loader@8.2.0(postcss@8.5.6)(typescript@5.9.3)(webpack@5.105.0): dependencies: cosmiconfig: 9.0.0(typescript@5.9.3) jiti: 2.6.1 postcss: 8.5.6 - semver: 7.7.3 + semver: 7.7.4 optionalDependencies: - webpack: 5.104.1(webpack-cli@6.0.1) + webpack: 5.105.0(webpack-cli@6.0.1) transitivePeerDependencies: - typescript @@ -7648,7 +7667,7 @@ snapshots: qified@0.6.0: dependencies: - hookified: 1.15.0 + hookified: 1.15.1 queue-microtask@1.2.3: {} @@ -7779,6 +7798,8 @@ snapshots: semver@7.7.3: {} + semver@7.7.4: {} + serialize-javascript@6.0.2: dependencies: randombytes: 2.1.0 @@ -7872,7 +7893,7 @@ snapshots: spdx-expression-parse: 3.0.1 spdx-ranges: 2.1.1 - spectral-cli-bundle@1.0.3: + spectral-cli-bundle@1.0.4: optionalDependencies: fsevents: 2.3.3 @@ -7920,28 +7941,28 @@ snapshots: style-search@0.1.0: {} - stylelint-config-recommended@18.0.0(stylelint@17.1.0(typescript@5.9.3)): + stylelint-config-recommended@18.0.0(stylelint@17.1.1(typescript@5.9.3)): dependencies: - stylelint: 17.1.0(typescript@5.9.3) + stylelint: 17.1.1(typescript@5.9.3) - stylelint-declaration-block-no-ignored-properties@3.0.0(stylelint@17.1.0(typescript@5.9.3)): + stylelint-declaration-block-no-ignored-properties@3.0.0(stylelint@17.1.1(typescript@5.9.3)): dependencies: - stylelint: 17.1.0(typescript@5.9.3) + stylelint: 17.1.1(typescript@5.9.3) - stylelint-declaration-strict-value@1.10.11(stylelint@17.1.0(typescript@5.9.3)): + stylelint-declaration-strict-value@1.10.11(stylelint@17.1.1(typescript@5.9.3)): dependencies: - stylelint: 17.1.0(typescript@5.9.3) + stylelint: 17.1.1(typescript@5.9.3) - stylelint-value-no-unknown-custom-properties@6.1.1(stylelint@17.1.0(typescript@5.9.3)): + stylelint-value-no-unknown-custom-properties@6.1.1(stylelint@17.1.1(typescript@5.9.3)): dependencies: postcss-value-parser: 4.2.0 resolve: 1.22.11 - stylelint: 17.1.0(typescript@5.9.3) + stylelint: 17.1.1(typescript@5.9.3) - stylelint@17.1.0(typescript@5.9.3): + stylelint@17.1.1(typescript@5.9.3): dependencies: '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) - '@csstools/css-syntax-patches-for-csstree': 1.0.26 + '@csstools/css-syntax-patches-for-csstree': 1.0.27 '@csstools/css-tokenizer': 4.0.0 '@csstools/media-query-list-parser': 5.0.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) '@csstools/selector-resolve-nested': 4.0.0(postcss-selector-parser@7.1.1) @@ -8095,14 +8116,14 @@ snapshots: tapable@2.3.0: {} - terser-webpack-plugin@5.3.16(webpack@5.104.1): + terser-webpack-plugin@5.3.16(webpack@5.105.0): dependencies: '@jridgewell/trace-mapping': 0.3.31 jest-worker: 27.5.1 schema-utils: 4.3.3 serialize-javascript: 6.0.2 terser: 5.46.0 - webpack: 5.104.1(webpack-cli@6.0.1) + webpack: 5.105.0(webpack-cli@6.0.1) terser@5.46.0: dependencies: @@ -8172,12 +8193,12 @@ snapshots: dependencies: prelude-ls: 1.2.1 - typescript-eslint@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3): + typescript-eslint@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3): dependencies: - '@typescript-eslint/eslint-plugin': 8.54.0(@typescript-eslint/parser@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/parser': 8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/typescript-estree': 8.54.0(typescript@5.9.3) - '@typescript-eslint/utils': 8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/eslint-plugin': 8.55.0(@typescript-eslint/parser@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/parser': 8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/typescript-estree': 8.55.0(typescript@5.9.3) + '@typescript-eslint/utils': 8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) eslint: 9.39.2(jiti@2.6.1) typescript: 5.9.3 transitivePeerDependencies: @@ -8227,7 +8248,7 @@ snapshots: escalade: 3.2.0 picocolors: 1.1.1 - updates@17.0.9: {} + updates@17.4.0: {} uri-js@4.4.1: dependencies: @@ -8239,30 +8260,30 @@ snapshots: vanilla-colorful@0.7.2: {} - vite-string-plugin@2.0.0(vite@7.3.1(@types/node@25.1.0)(jiti@2.6.1)(stylus@0.57.0)(terser@5.46.0)(yaml@2.8.2)): + vite-string-plugin@2.0.1(vite@7.3.1(@types/node@25.2.2)(jiti@2.6.1)(stylus@0.57.0)(terser@5.46.0)(yaml@2.8.2)): dependencies: - vite: 7.3.1(@types/node@25.1.0)(jiti@2.6.1)(stylus@0.57.0)(terser@5.46.0)(yaml@2.8.2) + vite: 7.3.1(@types/node@25.2.2)(jiti@2.6.1)(stylus@0.57.0)(terser@5.46.0)(yaml@2.8.2) - vite@7.3.1(@types/node@25.1.0)(jiti@2.6.1)(stylus@0.57.0)(terser@5.46.0)(yaml@2.8.2): + vite@7.3.1(@types/node@25.2.2)(jiti@2.6.1)(stylus@0.57.0)(terser@5.46.0)(yaml@2.8.2): dependencies: - esbuild: 0.27.2 + esbuild: 0.27.3 fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 postcss: 8.5.6 rollup: 4.57.1 tinyglobby: 0.2.15 optionalDependencies: - '@types/node': 25.1.0 + '@types/node': 25.2.2 fsevents: 2.3.3 jiti: 2.6.1 stylus: 0.57.0 terser: 5.46.0 yaml: 2.8.2 - vitest@4.0.18(@types/node@25.1.0)(happy-dom@20.4.0)(jiti@2.6.1)(stylus@0.57.0)(terser@5.46.0)(yaml@2.8.2): + vitest@4.0.18(@types/node@25.2.2)(happy-dom@20.6.0)(jiti@2.6.1)(stylus@0.57.0)(terser@5.46.0)(yaml@2.8.2): dependencies: '@vitest/expect': 4.0.18 - '@vitest/mocker': 4.0.18(vite@7.3.1(@types/node@25.1.0)(jiti@2.6.1)(stylus@0.57.0)(terser@5.46.0)(yaml@2.8.2)) + '@vitest/mocker': 4.0.18(vite@7.3.1(@types/node@25.2.2)(jiti@2.6.1)(stylus@0.57.0)(terser@5.46.0)(yaml@2.8.2)) '@vitest/pretty-format': 4.0.18 '@vitest/runner': 4.0.18 '@vitest/snapshot': 4.0.18 @@ -8279,11 +8300,11 @@ snapshots: tinyexec: 1.0.2 tinyglobby: 0.2.15 tinyrainbow: 3.0.3 - vite: 7.3.1(@types/node@25.1.0)(jiti@2.6.1)(stylus@0.57.0)(terser@5.46.0)(yaml@2.8.2) + vite: 7.3.1(@types/node@25.2.2)(jiti@2.6.1)(stylus@0.57.0)(terser@5.46.0)(yaml@2.8.2) why-is-node-running: 2.3.0 optionalDependencies: - '@types/node': 25.1.0 - happy-dom: 20.4.0 + '@types/node': 25.2.2 + happy-dom: 20.6.0 transitivePeerDependencies: - jiti - less @@ -8318,14 +8339,14 @@ snapshots: vue-bar-graph@2.2.0(typescript@5.9.3): dependencies: - vue: 3.5.27(typescript@5.9.3) + vue: 3.5.28(typescript@5.9.3) transitivePeerDependencies: - typescript - vue-chartjs@5.3.3(chart.js@4.5.1)(vue@3.5.27(typescript@5.9.3)): + vue-chartjs@5.3.3(chart.js@4.5.1)(vue@3.5.28(typescript@5.9.3)): dependencies: chart.js: 4.5.1 - vue: 3.5.27(typescript@5.9.3) + vue: 3.5.28(typescript@5.9.3) vue-eslint-parser@10.2.0(eslint@9.39.2(jiti@2.6.1)): dependencies: @@ -8335,18 +8356,18 @@ snapshots: eslint-visitor-keys: 4.2.1 espree: 10.4.0 esquery: 1.7.0 - semver: 7.7.3 + semver: 7.7.4 transitivePeerDependencies: - supports-color - vue-loader@17.4.2(vue@3.5.27(typescript@5.9.3))(webpack@5.104.1): + vue-loader@17.4.2(vue@3.5.28(typescript@5.9.3))(webpack@5.105.0): dependencies: chalk: 4.1.2 hash-sum: 2.0.0 watchpack: 2.5.1 - webpack: 5.104.1(webpack-cli@6.0.1) + webpack: 5.105.0(webpack-cli@6.0.1) optionalDependencies: - vue: 3.5.27(typescript@5.9.3) + vue: 3.5.28(typescript@5.9.3) vue-tsc@3.2.4(typescript@5.9.3): dependencies: @@ -8354,13 +8375,13 @@ snapshots: '@vue/language-core': 3.2.4 typescript: 5.9.3 - vue@3.5.27(typescript@5.9.3): + vue@3.5.28(typescript@5.9.3): dependencies: - '@vue/compiler-dom': 3.5.27 - '@vue/compiler-sfc': 3.5.27 - '@vue/runtime-dom': 3.5.27 - '@vue/server-renderer': 3.5.27(vue@3.5.27(typescript@5.9.3)) - '@vue/shared': 3.5.27 + '@vue/compiler-dom': 3.5.28 + '@vue/compiler-sfc': 3.5.28 + '@vue/runtime-dom': 3.5.28 + '@vue/server-renderer': 3.5.28(vue@3.5.28(typescript@5.9.3)) + '@vue/shared': 3.5.28 optionalDependencies: typescript: 5.9.3 @@ -8371,12 +8392,12 @@ snapshots: webidl-conversions@3.0.1: {} - webpack-cli@6.0.1(webpack@5.104.1): + webpack-cli@6.0.1(webpack@5.105.0): dependencies: '@discoveryjs/json-ext': 0.6.3 - '@webpack-cli/configtest': 3.0.1(webpack-cli@6.0.1)(webpack@5.104.1) - '@webpack-cli/info': 3.0.1(webpack-cli@6.0.1)(webpack@5.104.1) - '@webpack-cli/serve': 3.0.1(webpack-cli@6.0.1)(webpack@5.104.1) + '@webpack-cli/configtest': 3.0.1(webpack-cli@6.0.1)(webpack@5.105.0) + '@webpack-cli/info': 3.0.1(webpack-cli@6.0.1)(webpack@5.105.0) + '@webpack-cli/serve': 3.0.1(webpack-cli@6.0.1)(webpack@5.105.0) colorette: 2.0.20 commander: 12.1.0 cross-spawn: 7.0.6 @@ -8385,7 +8406,7 @@ snapshots: import-local: 3.2.0 interpret: 3.1.1 rechoir: 0.8.0 - webpack: 5.104.1(webpack-cli@6.0.1) + webpack: 5.105.0(webpack-cli@6.0.1) webpack-merge: 6.0.1 webpack-merge@6.0.1: @@ -8401,7 +8422,7 @@ snapshots: webpack-sources@3.3.3: {} - webpack@5.104.1(webpack-cli@6.0.1): + webpack@5.105.0(webpack-cli@6.0.1): dependencies: '@types/eslint-scope': 3.7.7 '@types/estree': 1.0.8 @@ -8413,7 +8434,7 @@ snapshots: acorn-import-phases: 1.0.4(acorn@8.15.0) browserslist: 4.28.1 chrome-trace-event: 1.0.4 - enhanced-resolve: 5.18.4 + enhanced-resolve: 5.19.0 es-module-lexer: 2.0.0 eslint-scope: 5.1.1 events: 3.3.0 @@ -8425,11 +8446,11 @@ snapshots: neo-async: 2.6.2 schema-utils: 4.3.3 tapable: 2.3.0 - terser-webpack-plugin: 5.3.16(webpack@5.104.1) + terser-webpack-plugin: 5.3.16(webpack@5.105.0) watchpack: 2.5.1 webpack-sources: 3.3.3 optionalDependencies: - webpack-cli: 6.0.1(webpack@5.104.1) + webpack-cli: 6.0.1(webpack@5.105.0) transitivePeerDependencies: - '@swc/core' - esbuild diff --git a/routers/web/feed/convert.go b/routers/web/feed/convert.go index 7c59132841..a5c379e01a 100644 --- a/routers/web/feed/convert.go +++ b/routers/web/feed/convert.go @@ -158,16 +158,16 @@ func feedActionsToFeedItems(ctx *context.Context, actions activities_model.Actio if link.Href == "#" { link.Href = srcLink } - titleExtra = ctx.Locale.Tr("action.mirror_sync_push", act.GetRepoAbsoluteLink(ctx), srcLink, act.GetBranch(), act.ShortRepoPath(ctx)) + titleExtra = ctx.Locale.Tr("action.mirror_sync_push", act.GetRepoAbsoluteLink(ctx), srcLink, act.RefName, act.ShortRepoPath(ctx)) case activities_model.ActionMirrorSyncCreate: srcLink := toSrcLink(ctx, act) if link.Href == "#" { link.Href = srcLink } - titleExtra = ctx.Locale.Tr("action.mirror_sync_create", act.GetRepoAbsoluteLink(ctx), srcLink, act.GetBranch(), act.ShortRepoPath(ctx)) + titleExtra = ctx.Locale.Tr("action.mirror_sync_create", act.GetRepoAbsoluteLink(ctx), srcLink, act.RefName, act.ShortRepoPath(ctx)) case activities_model.ActionMirrorSyncDelete: link.Href = act.GetRepoAbsoluteLink(ctx) - titleExtra = ctx.Locale.Tr("action.mirror_sync_delete", act.GetRepoAbsoluteLink(ctx), act.GetBranch(), act.ShortRepoPath(ctx)) + titleExtra = ctx.Locale.Tr("action.mirror_sync_delete", act.GetRepoAbsoluteLink(ctx), act.RefName, act.ShortRepoPath(ctx)) case activities_model.ActionApprovePullRequest: pullLink := toPullLink(ctx, act) titleExtra = ctx.Locale.Tr("action.approve_pull_request", pullLink, act.GetIssueInfos()[0], act.ShortRepoPath(ctx)) diff --git a/routers/web/repo/compare.go b/routers/web/repo/compare.go index 65c203d510..1e2486f5f1 100644 --- a/routers/web/repo/compare.go +++ b/routers/web/repo/compare.go @@ -4,7 +4,6 @@ package repo import ( - "bufio" gocontext "context" "encoding/csv" "errors" @@ -744,13 +743,16 @@ func attachHiddenCommentIDs(section *gitdiff.DiffSection, lineComments map[int64 // ExcerptBlob render blob excerpt contents func ExcerptBlob(ctx *context.Context) { commitID := ctx.PathParam("sha") - lastLeft := ctx.FormInt("last_left") - lastRight := ctx.FormInt("last_right") - idxLeft := ctx.FormInt("left") - idxRight := ctx.FormInt("right") - leftHunkSize := ctx.FormInt("left_hunk_size") - rightHunkSize := ctx.FormInt("right_hunk_size") - direction := ctx.FormString("direction") + opts := gitdiff.BlobExcerptOptions{ + LastLeft: ctx.FormInt("last_left"), + LastRight: ctx.FormInt("last_right"), + LeftIndex: ctx.FormInt("left"), + RightIndex: ctx.FormInt("right"), + LeftHunkSize: ctx.FormInt("left_hunk_size"), + RightHunkSize: ctx.FormInt("right_hunk_size"), + Direction: ctx.FormString("direction"), + Language: ctx.FormString("filelang"), + } filePath := ctx.FormString("path") gitRepo := ctx.Repo.GitRepo @@ -770,61 +772,27 @@ func ExcerptBlob(ctx *context.Context) { diffBlobExcerptData.BaseLink = ctx.Repo.RepoLink + "/wiki/blob_excerpt" } - chunkSize := gitdiff.BlobExcerptChunkSize commit, err := gitRepo.GetCommit(commitID) if err != nil { - ctx.HTTPError(http.StatusInternalServerError, "GetCommit") + ctx.ServerError("GetCommit", err) return } - section := &gitdiff.DiffSection{ - FileName: filePath, - } - if direction == "up" && (idxLeft-lastLeft) > chunkSize { - idxLeft -= chunkSize - idxRight -= chunkSize - leftHunkSize += chunkSize - rightHunkSize += chunkSize - section.Lines, err = getExcerptLines(commit, filePath, idxLeft-1, idxRight-1, chunkSize) - } else if direction == "down" && (idxLeft-lastLeft) > chunkSize { - section.Lines, err = getExcerptLines(commit, filePath, lastLeft, lastRight, chunkSize) - lastLeft += chunkSize - lastRight += chunkSize - } else { - offset := -1 - if direction == "down" { - offset = 0 - } - section.Lines, err = getExcerptLines(commit, filePath, lastLeft, lastRight, idxRight-lastRight+offset) - leftHunkSize = 0 - rightHunkSize = 0 - idxLeft = lastLeft - idxRight = lastRight - } + blob, err := commit.Tree.GetBlobByPath(filePath) if err != nil { - ctx.HTTPError(http.StatusInternalServerError, "getExcerptLines") + ctx.ServerError("GetBlobByPath", err) return } - - newLineSection := &gitdiff.DiffLine{ - Type: gitdiff.DiffLineSection, - SectionInfo: &gitdiff.DiffLineSectionInfo{ - Path: filePath, - LastLeftIdx: lastLeft, - LastRightIdx: lastRight, - LeftIdx: idxLeft, - RightIdx: idxRight, - LeftHunkSize: leftHunkSize, - RightHunkSize: rightHunkSize, - }, + reader, err := blob.DataAsync() + if err != nil { + ctx.ServerError("DataAsync", err) + return } - if newLineSection.GetExpandDirection() != "" { - newLineSection.Content = fmt.Sprintf("@@ -%d,%d +%d,%d @@\n", idxLeft, leftHunkSize, idxRight, rightHunkSize) - switch direction { - case "up": - section.Lines = append([]*gitdiff.DiffLine{newLineSection}, section.Lines...) - case "down": - section.Lines = append(section.Lines, newLineSection) - } + defer reader.Close() + + section, err := gitdiff.BuildBlobExcerptDiffSection(filePath, reader, opts) + if err != nil { + ctx.ServerError("BuildBlobExcerptDiffSection", err) + return } diffBlobExcerptData.PullIssueIndex = ctx.FormInt64("pull_issue_index") @@ -865,37 +833,3 @@ func ExcerptBlob(ctx *context.Context) { ctx.HTML(http.StatusOK, tplBlobExcerpt) } - -func getExcerptLines(commit *git.Commit, filePath string, idxLeft, idxRight, chunkSize int) ([]*gitdiff.DiffLine, error) { - blob, err := commit.Tree.GetBlobByPath(filePath) - if err != nil { - return nil, err - } - reader, err := blob.DataAsync() - if err != nil { - return nil, err - } - defer reader.Close() - scanner := bufio.NewScanner(reader) - var diffLines []*gitdiff.DiffLine - for line := 0; line < idxRight+chunkSize; line++ { - if ok := scanner.Scan(); !ok { - break - } - if line < idxRight { - continue - } - lineText := scanner.Text() - diffLine := &gitdiff.DiffLine{ - LeftIdx: idxLeft + (line - idxRight) + 1, - RightIdx: line + 1, - Type: gitdiff.DiffLinePlain, - Content: " " + lineText, - } - diffLines = append(diffLines, diffLine) - } - if err = scanner.Err(); err != nil { - return nil, fmt.Errorf("getExcerptLines scan: %w", err) - } - return diffLines, nil -} diff --git a/routers/web/repo/editor_preview.go b/routers/web/repo/editor_preview.go index 14be5b72b6..ec1f41a013 100644 --- a/routers/web/repo/editor_preview.go +++ b/routers/web/repo/editor_preview.go @@ -6,12 +6,13 @@ package repo import ( "net/http" + "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/services/context" files_service "code.gitea.io/gitea/services/repository/files" ) func DiffPreviewPost(ctx *context.Context) { - content := ctx.FormString("content") + newContent := ctx.FormString("content") treePath := files_service.CleanGitTreePath(ctx.Repo.TreePath) if treePath == "" { ctx.HTTPError(http.StatusBadRequest, "file name to diff is invalid") @@ -27,7 +28,12 @@ func DiffPreviewPost(ctx *context.Context) { return } - diff, err := files_service.GetDiffPreview(ctx, ctx.Repo.Repository, ctx.Repo.BranchName, treePath, content) + oldContent, err := entry.Blob().GetBlobContent(setting.UI.MaxDisplayFileSize) + if err != nil { + ctx.ServerError("GetBlobContent", err) + return + } + diff, err := files_service.GetDiffPreview(ctx, ctx.Repo.Repository, ctx.Repo.BranchName, treePath, oldContent, newContent) if err != nil { ctx.ServerError("GetDiffPreview", err) return diff --git a/services/gitdiff/gitdiff.go b/services/gitdiff/gitdiff.go index 3728f50d21..a62177c45c 100644 --- a/services/gitdiff/gitdiff.go +++ b/services/gitdiff/gitdiff.go @@ -81,6 +81,8 @@ type DiffLine struct { // DiffLineSectionInfo represents diff line section meta data type DiffLineSectionInfo struct { + language *diffVarMutable[string] + Path string // These line "idx" are 1-based line numbers @@ -165,16 +167,19 @@ func (d *DiffLine) GetLineTypeMarker() string { } func (d *DiffLine) getBlobExcerptQuery() string { - query := fmt.Sprintf( + language := "" + if d.SectionInfo.language != nil { // for normal cases, it can't be nil, this check is only for some tests + language = d.SectionInfo.language.value + } + return fmt.Sprintf( "last_left=%d&last_right=%d&"+ "left=%d&right=%d&"+ "left_hunk_size=%d&right_hunk_size=%d&"+ - "path=%s", + "path=%s&filelang=%s", d.SectionInfo.LastLeftIdx, d.SectionInfo.LastRightIdx, d.SectionInfo.LeftIdx, d.SectionInfo.RightIdx, d.SectionInfo.LeftHunkSize, d.SectionInfo.RightHunkSize, - url.QueryEscape(d.SectionInfo.Path)) - return query + url.QueryEscape(d.SectionInfo.Path), url.QueryEscape(language)) } func (d *DiffLine) GetExpandDirection() string { @@ -266,11 +271,12 @@ func FillHiddenCommentIDsForDiffLine(line *DiffLine, lineComments map[int64][]*i line.SectionInfo.HiddenCommentIDs = hiddenCommentIDs } -func getDiffLineSectionInfo(treePath, line string, lastLeftIdx, lastRightIdx int) *DiffLineSectionInfo { +func newDiffLineSectionInfo(curFile *DiffFile, line string, lastLeftIdx, lastRightIdx int) *DiffLineSectionInfo { leftLine, leftHunk, rightLine, rightHunk := git.ParseDiffHunkString(line) return &DiffLineSectionInfo{ - Path: treePath, + Path: curFile.Name, + language: &curFile.language, LastLeftIdx: lastLeftIdx, LastRightIdx: lastRightIdx, LeftIdx: leftLine, @@ -290,7 +296,10 @@ func getLineContent(content string, locale translation.Locale) DiffInline { // DiffSection represents a section of a DiffFile. type DiffSection struct { - file *DiffFile + language *diffVarMutable[string] + highlightedLeftLines *diffVarMutable[map[int]template.HTML] + highlightedRightLines *diffVarMutable[map[int]template.HTML] + FileName string Lines []*DiffLine } @@ -339,9 +348,9 @@ func (diffSection *DiffSection) getDiffLineForRender(diffLineType DiffLineType, var fileLanguage string var highlightedLeftLines, highlightedRightLines map[int]template.HTML // when a "diff section" is manually prepared by ExcerptBlob, it doesn't have "file" information - if diffSection.file != nil { - fileLanguage = diffSection.file.Language - highlightedLeftLines, highlightedRightLines = diffSection.file.highlightedLeftLines, diffSection.file.highlightedRightLines + if diffSection.language != nil { + fileLanguage = diffSection.language.value + highlightedLeftLines, highlightedRightLines = diffSection.highlightedLeftLines.value, diffSection.highlightedRightLines.value } var lineHTML template.HTML @@ -392,6 +401,11 @@ func (diffSection *DiffSection) GetComputedInlineDiffFor(diffLine *DiffLine, loc } } +// diffVarMutable is a wrapper to make a variable mutable to be shared across structs +type diffVarMutable[T any] struct { + value T +} + // DiffFile represents a file diff. type DiffFile struct { // only used internally to parse Ambiguous filenames @@ -418,7 +432,6 @@ type DiffFile struct { IsIncompleteLineTooLong bool // will be filled by the extra loop in GitDiffForRender - Language string IsGenerated bool IsVendored bool SubmoduleDiffInfo *SubmoduleDiffInfo // IsSubmodule==true, then there must be a SubmoduleDiffInfo @@ -430,9 +443,10 @@ type DiffFile struct { IsViewed bool // User specific HasChangedSinceLastReview bool // User specific - // for render purpose only, will be filled by the extra loop in GitDiffForRender - highlightedLeftLines map[int]template.HTML - highlightedRightLines map[int]template.HTML + // for render purpose only, will be filled by the extra loop in GitDiffForRender, the maps of lines are 0-based + language diffVarMutable[string] + highlightedLeftLines diffVarMutable[map[int]template.HTML] + highlightedRightLines diffVarMutable[map[int]template.HTML] } // GetType returns type of diff file. @@ -469,6 +483,7 @@ func (diffFile *DiffFile) GetTailSectionAndLimitedContent(leftCommit, rightCommi Type: DiffLineSection, Content: " ", SectionInfo: &DiffLineSectionInfo{ + language: &diffFile.language, Path: diffFile.Name, LastLeftIdx: lastLine.LeftIdx, LastRightIdx: lastLine.RightIdx, @@ -907,6 +922,14 @@ func skipToNextDiffHead(input *bufio.Reader) (line string, err error) { return line, err } +func newDiffSectionForDiffFile(curFile *DiffFile) *DiffSection { + return &DiffSection{ + language: &curFile.language, + highlightedLeftLines: &curFile.highlightedLeftLines, + highlightedRightLines: &curFile.highlightedRightLines, + } +} + func parseHunks(ctx context.Context, curFile *DiffFile, maxLines, maxLineCharacters int, input *bufio.Reader) (lineBytes []byte, isFragment bool, err error) { sb := strings.Builder{} @@ -964,12 +987,12 @@ func parseHunks(ctx context.Context, curFile *DiffFile, maxLines, maxLineCharact line := sb.String() // Create a new section to represent this hunk - curSection = &DiffSection{file: curFile} + curSection = newDiffSectionForDiffFile(curFile) lastLeftIdx = -1 curFile.Sections = append(curFile.Sections, curSection) // FIXME: the "-1" can't be right, these "line idx" are all 1-based, maybe there are other bugs that covers this bug. - lineSectionInfo := getDiffLineSectionInfo(curFile.Name, line, leftLine-1, rightLine-1) + lineSectionInfo := newDiffLineSectionInfo(curFile, line, leftLine-1, rightLine-1) diffLine := &DiffLine{ Type: DiffLineSection, Content: line, @@ -1004,7 +1027,7 @@ func parseHunks(ctx context.Context, curFile *DiffFile, maxLines, maxLineCharact rightLine++ if curSection == nil { // Create a new section to represent this hunk - curSection = &DiffSection{file: curFile} + curSection = newDiffSectionForDiffFile(curFile) curFile.Sections = append(curFile.Sections, curSection) lastLeftIdx = -1 } @@ -1037,7 +1060,7 @@ func parseHunks(ctx context.Context, curFile *DiffFile, maxLines, maxLineCharact } if curSection == nil { // Create a new section to represent this hunk - curSection = &DiffSection{file: curFile} + curSection = newDiffSectionForDiffFile(curFile) curFile.Sections = append(curFile.Sections, curSection) lastLeftIdx = -1 } @@ -1064,7 +1087,7 @@ func parseHunks(ctx context.Context, curFile *DiffFile, maxLines, maxLineCharact lastLeftIdx = -1 if curSection == nil { // Create a new section to represent this hunk - curSection = &DiffSection{file: curFile} + curSection = newDiffSectionForDiffFile(curFile) curFile.Sections = append(curFile.Sections, curSection) } curSection.Lines = append(curSection.Lines, diffLine) @@ -1309,7 +1332,7 @@ func GetDiffForRender(ctx context.Context, repoLink string, gitRepo *git.Reposit isVendored, isGenerated = attrs.GetVendored(), attrs.GetGenerated() language := attrs.GetLanguage() if language.Has() { - diffFile.Language = language.Value() + diffFile.language.value = language.Value() } attrDiff = attrs.Get(attribute.Diff).ToString() } @@ -1335,11 +1358,11 @@ func GetDiffForRender(ctx context.Context, repoLink string, gitRepo *git.Reposit shouldFullFileHighlight := !setting.Git.DisableDiffHighlight && attrDiff.Value() == "" if shouldFullFileHighlight { - if limitedContent.LeftContent != nil && limitedContent.LeftContent.buf.Len() < MaxDiffHighlightEntireFileSize { - diffFile.highlightedLeftLines = highlightCodeLines(diffFile, true /* left */, limitedContent.LeftContent.buf.Bytes()) + if limitedContent.LeftContent != nil { + diffFile.highlightedLeftLines.value = highlightCodeLinesForDiffFile(diffFile, true /* left */, limitedContent.LeftContent.buf.Bytes()) } - if limitedContent.RightContent != nil && limitedContent.RightContent.buf.Len() < MaxDiffHighlightEntireFileSize { - diffFile.highlightedRightLines = highlightCodeLines(diffFile, false /* right */, limitedContent.RightContent.buf.Bytes()) + if limitedContent.RightContent != nil { + diffFile.highlightedRightLines.value = highlightCodeLinesForDiffFile(diffFile, false /* right */, limitedContent.RightContent.buf.Bytes()) } } } @@ -1347,13 +1370,26 @@ func GetDiffForRender(ctx context.Context, repoLink string, gitRepo *git.Reposit return diff, nil } -func highlightCodeLines(diffFile *DiffFile, isLeft bool, rawContent []byte) map[int]template.HTML { +func FillDiffFileHighlightLinesByContent(diffFile *DiffFile, left, right []byte) { + diffFile.highlightedLeftLines.value = highlightCodeLinesForDiffFile(diffFile, true /* left */, left) + diffFile.highlightedRightLines.value = highlightCodeLinesForDiffFile(diffFile, false /* right */, right) +} + +func highlightCodeLinesForDiffFile(diffFile *DiffFile, isLeft bool, rawContent []byte) map[int]template.HTML { + return highlightCodeLines(diffFile.Name, diffFile.language.value, diffFile.Sections, isLeft, rawContent) +} + +func highlightCodeLines(name, lang string, sections []*DiffSection, isLeft bool, rawContent []byte) map[int]template.HTML { + if setting.Git.DisableDiffHighlight || len(rawContent) > MaxDiffHighlightEntireFileSize { + return nil + } + content := util.UnsafeBytesToString(charset.ToUTF8(rawContent, charset.ConvertOpts{})) - highlightedNewContent, _ := highlight.RenderCodeFast(diffFile.Name, diffFile.Language, content) + highlightedNewContent, _ := highlight.RenderCodeFast(name, lang, content) unsafeLines := highlight.UnsafeSplitHighlightedLines(highlightedNewContent) lines := make(map[int]template.HTML, len(unsafeLines)) // only save the highlighted lines we need, but not the whole file, to save memory - for _, sec := range diffFile.Sections { + for _, sec := range sections { for _, ln := range sec.Lines { lineIdx := ln.LeftIdx if !isLeft { diff --git a/services/gitdiff/gitdiff_excerpt.go b/services/gitdiff/gitdiff_excerpt.go new file mode 100644 index 0000000000..be66d8e2af --- /dev/null +++ b/services/gitdiff/gitdiff_excerpt.go @@ -0,0 +1,121 @@ +// Copyright 2026 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package gitdiff + +import ( + "bufio" + "bytes" + "fmt" + "html/template" + "io" + + "code.gitea.io/gitea/modules/setting" +) + +type BlobExcerptOptions struct { + LastLeft int + LastRight int + LeftIndex int + RightIndex int + LeftHunkSize int + RightHunkSize int + Direction string + Language string +} + +func fillExcerptLines(section *DiffSection, filePath string, reader io.Reader, lang string, idxLeft, idxRight, chunkSize int) error { + buf := &bytes.Buffer{} + scanner := bufio.NewScanner(reader) + var diffLines []*DiffLine + for line := 0; line < idxRight+chunkSize; line++ { + if ok := scanner.Scan(); !ok { + break + } + lineText := scanner.Text() + if buf.Len()+len(lineText) < int(setting.UI.MaxDisplayFileSize) { + buf.WriteString(lineText) + buf.WriteByte('\n') + } + if line < idxRight { + continue + } + diffLine := &DiffLine{ + LeftIdx: idxLeft + (line - idxRight) + 1, + RightIdx: line + 1, + Type: DiffLinePlain, + Content: " " + lineText, + } + diffLines = append(diffLines, diffLine) + } + if err := scanner.Err(); err != nil { + return fmt.Errorf("fillExcerptLines scan: %w", err) + } + section.Lines = diffLines + // DiffLinePlain always uses right lines + section.highlightedRightLines.value = highlightCodeLines(filePath, lang, []*DiffSection{section}, false /* right */, buf.Bytes()) + return nil +} + +func BuildBlobExcerptDiffSection(filePath string, reader io.Reader, opts BlobExcerptOptions) (*DiffSection, error) { + lastLeft, lastRight, idxLeft, idxRight := opts.LastLeft, opts.LastRight, opts.LeftIndex, opts.RightIndex + leftHunkSize, rightHunkSize, direction := opts.LeftHunkSize, opts.RightHunkSize, opts.Direction + language := opts.Language + + chunkSize := BlobExcerptChunkSize + section := &DiffSection{ + language: &diffVarMutable[string]{value: language}, + highlightedLeftLines: &diffVarMutable[map[int]template.HTML]{}, + highlightedRightLines: &diffVarMutable[map[int]template.HTML]{}, + FileName: filePath, + } + var err error + if direction == "up" && (idxLeft-lastLeft) > chunkSize { + idxLeft -= chunkSize + idxRight -= chunkSize + leftHunkSize += chunkSize + rightHunkSize += chunkSize + err = fillExcerptLines(section, filePath, reader, language, idxLeft-1, idxRight-1, chunkSize) + } else if direction == "down" && (idxLeft-lastLeft) > chunkSize { + err = fillExcerptLines(section, filePath, reader, language, lastLeft, lastRight, chunkSize) + lastLeft += chunkSize + lastRight += chunkSize + } else { + offset := -1 + if direction == "down" { + offset = 0 + } + err = fillExcerptLines(section, filePath, reader, language, lastLeft, lastRight, idxRight-lastRight+offset) + leftHunkSize = 0 + rightHunkSize = 0 + idxLeft = lastLeft + idxRight = lastRight + } + if err != nil { + return nil, err + } + + newLineSection := &DiffLine{ + Type: DiffLineSection, + SectionInfo: &DiffLineSectionInfo{ + language: &diffVarMutable[string]{value: opts.Language}, + Path: filePath, + LastLeftIdx: lastLeft, + LastRightIdx: lastRight, + LeftIdx: idxLeft, + RightIdx: idxRight, + LeftHunkSize: leftHunkSize, + RightHunkSize: rightHunkSize, + }, + } + if newLineSection.GetExpandDirection() != "" { + newLineSection.Content = fmt.Sprintf("@@ -%d,%d +%d,%d @@\n", idxLeft, leftHunkSize, idxRight, rightHunkSize) + switch direction { + case "up": + section.Lines = append([]*DiffLine{newLineSection}, section.Lines...) + case "down": + section.Lines = append(section.Lines, newLineSection) + } + } + return section, nil +} diff --git a/services/gitdiff/gitdiff_excerpt_test.go b/services/gitdiff/gitdiff_excerpt_test.go new file mode 100644 index 0000000000..cb71e66462 --- /dev/null +++ b/services/gitdiff/gitdiff_excerpt_test.go @@ -0,0 +1,39 @@ +// Copyright 2026 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package gitdiff + +import ( + "bytes" + "strconv" + "testing" + + "code.gitea.io/gitea/modules/translation" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestBuildBlobExcerptDiffSection(t *testing.T) { + data := &bytes.Buffer{} + for i := range 100 { + data.WriteString("a = " + strconv.Itoa(i+1) + "\n") + } + + locale := translation.MockLocale{} + lineMiddle := 50 + diffSection, err := BuildBlobExcerptDiffSection("a.py", bytes.NewReader(data.Bytes()), BlobExcerptOptions{ + LeftIndex: lineMiddle, + RightIndex: lineMiddle, + LeftHunkSize: 10, + RightHunkSize: 10, + Direction: "up", + }) + require.NoError(t, err) + assert.Len(t, diffSection.highlightedRightLines.value, BlobExcerptChunkSize) + assert.NotEmpty(t, diffSection.highlightedRightLines.value[lineMiddle-BlobExcerptChunkSize-1]) + assert.NotEmpty(t, diffSection.highlightedRightLines.value[lineMiddle-2]) // 0-based + + diffInline := diffSection.GetComputedInlineDiffFor(diffSection.Lines[1], locale) + assert.Equal(t, `a = 30`+"\n", string(diffInline.Content)) +} diff --git a/services/gitdiff/gitdiff_test.go b/services/gitdiff/gitdiff_test.go index a94dad8b63..62b17c223c 100644 --- a/services/gitdiff/gitdiff_test.go +++ b/services/gitdiff/gitdiff_test.go @@ -1111,22 +1111,20 @@ func TestDiffLine_GetExpandDirection(t *testing.T) { func TestHighlightCodeLines(t *testing.T) { t.Run("CharsetDetecting", func(t *testing.T) { diffFile := &DiffFile{ - Name: "a.c", - Language: "c", + Name: "a.c", Sections: []*DiffSection{ { Lines: []*DiffLine{{LeftIdx: 1}}, }, }, } - ret := highlightCodeLines(diffFile, true, []byte("// abc\xcc def\xcd")) // ISO-8859-1 bytes + ret := highlightCodeLinesForDiffFile(diffFile, true, []byte("// abc\xcc def\xcd")) // ISO-8859-1 bytes assert.Equal(t, "// abcÌ defÍ\n", string(ret[0])) }) t.Run("LeftLines", func(t *testing.T) { diffFile := &DiffFile{ - Name: "a.c", - Language: "c", + Name: "a.c", Sections: []*DiffSection{ { Lines: []*DiffLine{ @@ -1138,7 +1136,7 @@ func TestHighlightCodeLines(t *testing.T) { }, } const nl = "\n" - ret := highlightCodeLines(diffFile, true, []byte("a\nb\n")) + ret := highlightCodeLinesForDiffFile(diffFile, true, []byte("a\nb\n")) assert.Equal(t, map[int]template.HTML{ 0: `a` + nl, 1: `b`, diff --git a/services/gitdiff/highlightdiff.go b/services/gitdiff/highlightdiff.go index 11d5e6dd5a..af8215de5a 100644 --- a/services/gitdiff/highlightdiff.go +++ b/services/gitdiff/highlightdiff.go @@ -23,7 +23,7 @@ func extractDiffTokenRemainingFullTag(s string) (token, after string, valid bool // keep in mind: even if we'd like to relax this check, // we should never ignore "&" because it is for HTML entity and can't be safely used in the diff algorithm, // because diff between "<" and ">" will generate broken result. - isSymbolChar := 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || '0' <= c && c <= '9' || c == '_' || c == '-' + isSymbolChar := 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || '0' <= c && c <= '9' || c == '_' || c == '-' || c == '.' if !isSymbolChar { return "", s, false } @@ -40,7 +40,7 @@ func extractDiffTokenRemainingFullTag(s string) (token, after string, valid bool // Returned token: // * full tag with content: "<content>", it is used to optimize diff results to highlight the whole changed symbol -// * opening/close tag: "" or "" +// * opening/closing tag: "" or "" // * HTML entity: "<" func extractDiffToken(s string) (before, token, after string, valid bool) { for pos1 := 0; pos1 < len(s); pos1++ { @@ -123,6 +123,25 @@ func (hcd *highlightCodeDiff) collectUsedRunes(code template.HTML) { } } +func (hcd *highlightCodeDiff) diffEqualPartIsSpaceOnly(s string) bool { + for _, r := range s { + if r >= hcd.placeholderBegin { + recovered := hcd.placeholderTokenMap[r] + if strings.HasPrefix(recovered, "<<") { + return false // a full tag with content, it can't be space-only + } else if strings.HasPrefix(recovered, "<") { + continue // a single opening/closing tag, skip the tag and continue to check the content + } + return false // otherwise, it must be an HTML entity, it can't be space-only + } + isSpace := r == ' ' || r == '\t' || r == '\n' || r == '\r' + if !isSpace { + return false + } + } + return true +} + func (hcd *highlightCodeDiff) diffLineWithHighlight(lineType DiffLineType, codeA, codeB template.HTML) template.HTML { hcd.collectUsedRunes(codeA) hcd.collectUsedRunes(codeB) @@ -142,7 +161,21 @@ func (hcd *highlightCodeDiff) diffLineWithHighlight(lineType DiffLineType, codeA removedCodePrefix := hcd.registerTokenAsPlaceholder(``) removedCodeSuffix := hcd.registerTokenAsPlaceholder(``) - if removedCodeSuffix != 0 { + equalPartSpaceOnly := true + for _, diff := range diffs { + if diff.Type != diffmatchpatch.DiffEqual { + continue + } + if equalPartSpaceOnly = hcd.diffEqualPartIsSpaceOnly(diff.Text); !equalPartSpaceOnly { + break + } + } + + // only add "added"/"removed" tags when needed: + // * non-space contents appear in the DiffEqual parts (not a full-line add/del) + // * placeholder map still works (not exhausted, can get removedCodeSuffix) + addDiffTags := !equalPartSpaceOnly && removedCodeSuffix != 0 + if addDiffTags { for _, diff := range diffs { switch { case diff.Type == diffmatchpatch.DiffEqual: @@ -158,7 +191,7 @@ func (hcd *highlightCodeDiff) diffLineWithHighlight(lineType DiffLineType, codeA } } } else { - // placeholder map space is exhausted + // the caller will still add added/removed backgrounds for the whole line for _, diff := range diffs { take := diff.Type == diffmatchpatch.DiffEqual || (diff.Type == diffmatchpatch.DiffInsert && lineType == DiffLineAdd) || (diff.Type == diffmatchpatch.DiffDelete && lineType == DiffLineDel) if take { @@ -186,14 +219,7 @@ func (hcd *highlightCodeDiff) convertToPlaceholders(htmlContent template.HTML) s var tagStack []string res := strings.Builder{} - htmlCode := strings.TrimSpace(string(htmlContent)) - - // the standard chroma highlight HTML is ` ... ` - // the line wrapper tags should be removed before diff - if strings.HasPrefix(htmlCode, `") - } - + htmlCode := string(htmlContent) var beforeToken, token string var valid bool for { @@ -204,10 +230,16 @@ func (hcd *highlightCodeDiff) convertToPlaceholders(htmlContent template.HTML) s // write the content before the token into result string, and consume the token in the string res.WriteString(beforeToken) + // the standard chroma highlight HTML is ` ... ` + // the line wrapper tags should be removed before diff + if strings.HasPrefix(token, `" for "" diff --git a/services/gitdiff/highlightdiff_test.go b/services/gitdiff/highlightdiff_test.go index 573177f401..89e320f2c0 100644 --- a/services/gitdiff/highlightdiff_test.go +++ b/services/gitdiff/highlightdiff_test.go @@ -14,25 +14,57 @@ import ( "github.com/stretchr/testify/assert" ) -func TestDiffWithHighlight(t *testing.T) { - t.Run("DiffLineAddDel", func(t *testing.T) { +func BenchmarkHighlightDiff(b *testing.B) { + for b.Loop() { + // still fast enough: BenchmarkHighlightDiff-12 1000000 1027 ns/op + // TODO: the real bottleneck is that "diffLineWithHighlight" is called twice when rendering "added" and "removed" lines by the caller + // Ideally the caller should cache the diff result, and then use the diff result to render "added" and "removed" lines separately hcd := newHighlightCodeDiff() codeA := template.HTML(`x foo y`) codeB := template.HTML(`x bar y`) - outDel := hcd.diffLineWithHighlight(DiffLineDel, codeA, codeB) - assert.Equal(t, `x foo y`, string(outDel)) - outAdd := hcd.diffLineWithHighlight(DiffLineAdd, codeA, codeB) - assert.Equal(t, `x bar y`, string(outAdd)) + hcd.diffLineWithHighlight(DiffLineDel, codeA, codeB) + } +} + +func TestDiffWithHighlight(t *testing.T) { + t.Run("DiffLineAddDel", func(t *testing.T) { + t.Run("WithDiffTags", func(t *testing.T) { + hcd := newHighlightCodeDiff() + codeA := template.HTML(`x foo y`) + codeB := template.HTML(`x bar y`) + outDel := hcd.diffLineWithHighlight(DiffLineDel, codeA, codeB) + assert.Equal(t, `x foo y`, string(outDel)) + outAdd := hcd.diffLineWithHighlight(DiffLineAdd, codeA, codeB) + assert.Equal(t, `x bar y`, string(outAdd)) + }) + t.Run("NoRedundantTags", func(t *testing.T) { + // the equal parts only contain spaces, in this case, don't use "added/removed" tags + // because the diff lines already have a background color to indicate the change + hcd := newHighlightCodeDiff() + codeA := template.HTML(" \tfoo ") + codeB := template.HTML(" bar \n") + outDel := hcd.diffLineWithHighlight(DiffLineDel, codeA, codeB) + assert.Equal(t, string(codeA), string(outDel)) + outAdd := hcd.diffLineWithHighlight(DiffLineAdd, codeA, codeB) + assert.Equal(t, string(codeB), string(outAdd)) + }) }) t.Run("CleanUp", func(t *testing.T) { hcd := newHighlightCodeDiff() - codeA := template.HTML(`this is a comment`) - codeB := template.HTML(`this is updated comment`) + codeA := template.HTML(` this is a comment`) + codeB := template.HTML(` this is updated comment`) outDel := hcd.diffLineWithHighlight(DiffLineDel, codeA, codeB) - assert.Equal(t, `this is a comment`, string(outDel)) + assert.Equal(t, ` this is a comment`, string(outDel)) outAdd := hcd.diffLineWithHighlight(DiffLineAdd, codeA, codeB) - assert.Equal(t, `this is updated comment`, string(outAdd)) + assert.Equal(t, ` this is updated comment`, string(outAdd)) + + codeA = `line1` + "\n" + `line2` + codeB = `line1` + "\n" + `line!` + outDel = hcd.diffLineWithHighlight(DiffLineDel, codeA, codeB) + assert.Equal(t, `line1`+"\n"+`line2`, string(outDel)) + outAdd = hcd.diffLineWithHighlight(DiffLineAdd, codeA, codeB) + assert.Equal(t, `line1`+"\n"+`line!`, string(outAdd)) }) t.Run("OpenCloseTags", func(t *testing.T) { diff --git a/services/migrations/gitea_downloader.go b/services/migrations/gitea_downloader.go index 5d48d2f003..242873e551 100644 --- a/services/migrations/gitea_downloader.go +++ b/services/migrations/gitea_downloader.go @@ -345,25 +345,43 @@ func (g *GiteaDownloader) GetReleases(ctx context.Context) ([]*base.Release, err return releases, nil } -func (g *GiteaDownloader) getIssueReactions(index int64) ([]*base.Reaction, error) { - var reactions []*base.Reaction +func (g *GiteaDownloader) getIssueReactions(ctx context.Context, index int64) ([]*base.Reaction, error) { if err := g.client.CheckServerVersionConstraint(">=1.11"); err != nil { log.Info("GiteaDownloader: instance to old, skip getIssueReactions") - return reactions, nil - } - rl, _, err := g.client.GetIssueReactions(g.repoOwner, g.repoName, index) - if err != nil { - return nil, err + return nil, nil } - for _, reaction := range rl { - reactions = append(reactions, &base.Reaction{ - UserID: reaction.User.ID, - UserName: reaction.User.UserName, - Content: reaction.Reaction, - }) + allReactions := make([]*base.Reaction, 0, g.maxPerPage) + + for i := 1; ; i++ { + // make sure gitea can shutdown gracefully + select { + case <-ctx.Done(): + return nil, nil + default: + } + + reactions, _, err := g.client.ListIssueReactions(g.repoOwner, g.repoName, index, gitea_sdk.ListIssueReactionsOptions{ListOptions: gitea_sdk.ListOptions{ + PageSize: g.maxPerPage, + Page: i, + }}) + if err != nil { + return nil, err + } + + for _, reaction := range reactions { + allReactions = append(allReactions, &base.Reaction{ + UserID: reaction.User.ID, + UserName: reaction.User.UserName, + Content: reaction.Reaction, + }) + } + + if !g.pagination || len(reactions) < g.maxPerPage { + break + } } - return reactions, nil + return allReactions, nil } func (g *GiteaDownloader) getCommentReactions(commentID int64) ([]*base.Reaction, error) { @@ -388,7 +406,7 @@ func (g *GiteaDownloader) getCommentReactions(commentID int64) ([]*base.Reaction } // GetIssues returns issues according start and limit -func (g *GiteaDownloader) GetIssues(_ context.Context, page, perPage int) ([]*base.Issue, bool, error) { +func (g *GiteaDownloader) GetIssues(ctx context.Context, page, perPage int) ([]*base.Issue, bool, error) { if perPage > g.maxPerPage { perPage = g.maxPerPage } @@ -413,7 +431,7 @@ func (g *GiteaDownloader) GetIssues(_ context.Context, page, perPage int) ([]*ba milestone = issue.Milestone.Title } - reactions, err := g.getIssueReactions(issue.Index) + reactions, err := g.getIssueReactions(ctx, issue.Index) if err != nil { WarnAndNotice("Unable to load reactions during migrating issue #%d in %s. Error: %v", issue.Index, g, err) } @@ -497,7 +515,7 @@ func (g *GiteaDownloader) GetComments(ctx context.Context, commentable base.Comm } // GetPullRequests returns pull requests according page and perPage -func (g *GiteaDownloader) GetPullRequests(_ context.Context, page, perPage int) ([]*base.PullRequest, bool, error) { +func (g *GiteaDownloader) GetPullRequests(ctx context.Context, page, perPage int) ([]*base.PullRequest, bool, error) { if perPage > g.maxPerPage { perPage = g.maxPerPage } @@ -546,7 +564,7 @@ func (g *GiteaDownloader) GetPullRequests(_ context.Context, page, perPage int) mergeCommitSHA = *pr.MergedCommitID } - reactions, err := g.getIssueReactions(pr.Index) + reactions, err := g.getIssueReactions(ctx, pr.Index) if err != nil { WarnAndNotice("Unable to load reactions during migrating pull #%d in %s. Error: %v", pr.Index, g, err) } diff --git a/services/migrations/gitea_uploader.go b/services/migrations/gitea_uploader.go index 6f5b9bb33a..af9a0e0eaf 100644 --- a/services/migrations/gitea_uploader.go +++ b/services/migrations/gitea_uploader.go @@ -364,11 +364,12 @@ func (g *GiteaLocalUploader) CreateReleases(ctx context.Context, releases ...*ba // SyncTags syncs releases with tags in the database func (g *GiteaLocalUploader) SyncTags(ctx context.Context) error { - return repo_module.SyncReleasesWithTags(ctx, g.repo, g.gitRepo) + _, err := repo_module.SyncReleasesWithTags(ctx, g.repo, g.gitRepo) + return err } func (g *GiteaLocalUploader) SyncBranches(ctx context.Context) error { - _, err := repo_module.SyncRepoBranchesWithRepo(ctx, g.repo, g.gitRepo, g.doer.ID) + _, _, err := repo_module.SyncRepoBranchesWithRepo(ctx, g.repo, g.gitRepo, g.doer.ID) return err } diff --git a/services/mirror/mirror_pull.go b/services/mirror/mirror_pull.go index 3a1ed2b7d9..576dcb95f6 100644 --- a/services/mirror/mirror_pull.go +++ b/services/mirror/mirror_pull.go @@ -29,9 +29,6 @@ import ( repo_service "code.gitea.io/gitea/services/repository" ) -// gitShortEmptySha Git short empty SHA -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.ParseGitURL(addr) @@ -72,127 +69,6 @@ func UpdateAddress(ctx context.Context, m *repo_model.Mirror, addr string) error return repo_model.UpdateRepositoryColsNoAutoTime(ctx, m.Repo, "original_url") } -// mirrorSyncResult contains information of a updated reference. -// If the oldCommitID is "0000000", it means a new reference, the value of newCommitID is empty. -// If the newCommitID is "0000000", it means the reference is deleted, the value of oldCommitID is empty. -type mirrorSyncResult struct { - refName git.RefName - oldCommitID string - newCommitID string -} - -// parseRemoteUpdateOutput detects create, update and delete operations of references from upstream. -// possible output example: -/* -// * [new tag] v0.1.8 -> v0.1.8 -// * [new branch] master -> origin/master -// * [new ref] refs/pull/2/head -> refs/pull/2/head" -// - [deleted] (none) -> origin/test // delete a branch -// - [deleted] (none) -> 1 // delete a tag -// 957a993..a87ba5f test -> origin/test -// + f895a1e...957a993 test -> origin/test (forced update) -*/ -// TODO: return whether it's a force update -func parseRemoteUpdateOutput(output, remoteName string) []*mirrorSyncResult { - results := make([]*mirrorSyncResult, 0, 3) - lines := strings.Split(output, "\n") - for i := range lines { - // Make sure reference name is presented before continue - idx := strings.Index(lines[i], "-> ") - if idx == -1 { - continue - } - - refName := strings.TrimSpace(lines[i][idx+3:]) - - switch { - case strings.HasPrefix(lines[i], " * [new tag]"): // new tag - results = append(results, &mirrorSyncResult{ - refName: git.RefNameFromTag(refName), - oldCommitID: gitShortEmptySha, - }) - case strings.HasPrefix(lines[i], " * [new branch]"): // new branch - refName = strings.TrimPrefix(refName, remoteName+"/") - results = append(results, &mirrorSyncResult{ - refName: git.RefNameFromBranch(refName), - oldCommitID: gitShortEmptySha, - }) - case strings.HasPrefix(lines[i], " * [new ref]"): // new reference - results = append(results, &mirrorSyncResult{ - refName: git.RefName(refName), - oldCommitID: gitShortEmptySha, - }) - case strings.HasPrefix(lines[i], " - "): // Delete reference - isTag := !strings.HasPrefix(refName, remoteName+"/") - var refFullName git.RefName - if strings.HasPrefix(refName, "refs/") { - refFullName = git.RefName(refName) - } else if isTag { - refFullName = git.RefNameFromTag(refName) - } else { - refFullName = git.RefNameFromBranch(strings.TrimPrefix(refName, remoteName+"/")) - } - results = append(results, &mirrorSyncResult{ - refName: refFullName, - newCommitID: gitShortEmptySha, - }) - case strings.HasPrefix(lines[i], " + "): // Force update - if idx := strings.Index(refName, " "); idx > -1 { - refName = refName[:idx] - } - delimIdx := strings.Index(lines[i][3:], " ") - if delimIdx == -1 { - log.Error("SHA delimiter not found: %q", lines[i]) - continue - } - shas := strings.Split(lines[i][3:delimIdx+3], "...") - if len(shas) != 2 { - log.Error("Expect two SHAs but not what found: %q", lines[i]) - continue - } - var refFullName git.RefName - if strings.HasPrefix(refName, "refs/") { - refFullName = git.RefName(refName) - } else { - refFullName = git.RefNameFromBranch(strings.TrimPrefix(refName, remoteName+"/")) - } - - results = append(results, &mirrorSyncResult{ - refName: refFullName, - oldCommitID: shas[0], - newCommitID: shas[1], - }) - case strings.HasPrefix(lines[i], " "): // New commits of a reference - delimIdx := strings.Index(lines[i][3:], " ") - if delimIdx == -1 { - log.Error("SHA delimiter not found: %q", lines[i]) - continue - } - shas := strings.Split(lines[i][3:delimIdx+3], "..") - if len(shas) != 2 { - log.Error("Expect two SHAs but not what found: %q", lines[i]) - continue - } - var refFullName git.RefName - if strings.HasPrefix(refName, "refs/") { - refFullName = git.RefName(refName) - } else { - refFullName = git.RefNameFromBranch(strings.TrimPrefix(refName, remoteName+"/")) - } - - results = append(results, &mirrorSyncResult{ - refName: refFullName, - oldCommitID: shas[0], - newCommitID: shas[1], - }) - - default: - log.Warn("parseRemoteUpdateOutput: unexpected update line %q", lines[i]) - } - } - return results -} - func pruneBrokenReferences(ctx context.Context, m *repo_model.Mirror, gitRepo gitrepo.Repository, timeout time.Duration) error { cmd := gitcmd.NewCommand("remote", "prune").AddDynamicArguments(m.GetRemoteName()).WithTimeout(timeout) stdout, _, pruneErr := gitrepo.RunCmdString(ctx, gitRepo, cmd) @@ -229,7 +105,7 @@ func checkRecoverableSyncError(stderrMessage string) bool { } // runSync returns true if sync finished without error. -func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bool) { +func runSync(ctx context.Context, m *repo_model.Mirror) ([]*repo_module.SyncResult, bool) { log.Trace("SyncMirrors [repo: %-v]: running git remote update...", m.Repo) remoteURL, remoteErr := gitrepo.GitRemoteGetURL(ctx, m.Repo, m.GetRemoteName()) @@ -250,7 +126,6 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo } var err error - var fetchOutput string // it is from fetch's stderr fetchStdout, fetchStderr, err := gitrepo.RunCmdString(ctx, m.Repo, cmdFetch()) if err != nil { // sanitize the output, since it may contain the remote address, which may contain a password @@ -284,8 +159,6 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo return nil, false } } - fetchOutput = fetchStderr // the result of "git fetch" is in stderr - if err := gitrepo.WriteCommitGraph(ctx, m.Repo); err != nil { log.Error("SyncMirrors [repo: %-v]: %v", m.Repo, err) } @@ -306,14 +179,17 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo } log.Trace("SyncMirrors [repo: %-v]: syncing branches...", m.Repo) - if _, err = repo_module.SyncRepoBranchesWithRepo(ctx, m.Repo, gitRepo, 0); err != nil { + _, results, err := repo_module.SyncRepoBranchesWithRepo(ctx, m.Repo, gitRepo, 0) + if err != nil { log.Error("SyncMirrors [repo: %-v]: failed to synchronize branches: %v", m.Repo, err) } log.Trace("SyncMirrors [repo: %-v]: syncing releases with tags...", m.Repo) - if err = repo_module.SyncReleasesWithTags(ctx, m.Repo, gitRepo); err != nil { + tagResults, err := repo_module.SyncReleasesWithTags(ctx, m.Repo, gitRepo) + if err != nil { log.Error("SyncMirrors [repo: %-v]: failed to synchronize tags to releases: %v", m.Repo, err) } + results = append(results, tagResults...) gitRepo.Close() log.Trace("SyncMirrors [repo: %-v]: updating size of repository", m.Repo) @@ -381,7 +257,7 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo } m.UpdatedUnix = timeutil.TimeStampNow() - return parseRemoteUpdateOutput(fetchOutput, m.GetRemoteName()), true + return results, true } func getRepoPullMirrorLockKey(repoID int64) string { @@ -450,42 +326,42 @@ func SyncPullMirror(ctx context.Context, repoID int64) bool { for _, result := range results { // Discard GitHub pull requests, i.e. refs/pull/* - if result.refName.IsPull() { + if result.RefName.IsPull() { continue } // Create reference - if result.oldCommitID == gitShortEmptySha { - commitID, err := gitRepo.GetRefCommitID(result.refName.String()) + if result.OldCommitID == "" { + commitID, err := gitRepo.GetRefCommitID(result.RefName.String()) if err != nil { - log.Error("SyncMirrors [repo: %-v]: unable to GetRefCommitID [ref_name: %s]: %v", m.Repo, result.refName, err) + log.Error("SyncMirrors [repo: %-v]: unable to GetRefCommitID [ref_name: %s]: %v", m.Repo, result.RefName, err) continue } objectFormat := git.ObjectFormatFromName(m.Repo.ObjectFormatName) notify_service.SyncPushCommits(ctx, m.Repo.MustOwner(ctx), m.Repo, &repo_module.PushUpdateOptions{ - RefFullName: result.refName, + RefFullName: result.RefName, OldCommitID: objectFormat.EmptyObjectID().String(), NewCommitID: commitID, }, repo_module.NewPushCommits()) - notify_service.SyncCreateRef(ctx, m.Repo.MustOwner(ctx), m.Repo, result.refName, commitID) + notify_service.SyncCreateRef(ctx, m.Repo.MustOwner(ctx), m.Repo, result.RefName, commitID) continue } // Delete reference - if result.newCommitID == gitShortEmptySha { - notify_service.SyncDeleteRef(ctx, m.Repo.MustOwner(ctx), m.Repo, result.refName) + if result.NewCommitID == "" { + notify_service.SyncDeleteRef(ctx, m.Repo.MustOwner(ctx), m.Repo, result.RefName) continue } // Push commits - oldCommitID, err := gitrepo.GetFullCommitID(ctx, repo, result.oldCommitID) + oldCommitID, err := gitrepo.GetFullCommitID(ctx, repo, result.OldCommitID) if err != nil { - log.Error("SyncMirrors [repo: %-v]: unable to get GetFullCommitID[%s]: %v", m.Repo, result.oldCommitID, err) + log.Error("SyncMirrors [repo: %-v]: unable to get GetFullCommitID[%s]: %v", m.Repo, result.OldCommitID, err) continue } - newCommitID, err := gitrepo.GetFullCommitID(ctx, repo, result.newCommitID) + newCommitID, err := gitrepo.GetFullCommitID(ctx, repo, result.NewCommitID) if err != nil { - log.Error("SyncMirrors [repo: %-v]: unable to get GetFullCommitID [%s]: %v", m.Repo, result.newCommitID, err) + log.Error("SyncMirrors [repo: %-v]: unable to get GetFullCommitID [%s]: %v", m.Repo, result.NewCommitID, err) continue } commits, err := gitRepo.CommitsBetweenIDs(newCommitID, oldCommitID) @@ -509,7 +385,7 @@ func SyncPullMirror(ctx context.Context, repoID int64) bool { theCommits.CompareURL = m.Repo.ComposeCompareURL(oldCommitID, newCommitID) notify_service.SyncPushCommits(ctx, m.Repo.MustOwner(ctx), m.Repo, &repo_module.PushUpdateOptions{ - RefFullName: result.refName, + RefFullName: result.RefName, OldCommitID: oldCommitID, NewCommitID: newCommitID, }, theCommits) @@ -548,7 +424,7 @@ func SyncPullMirror(ctx context.Context, repoID int64) bool { return true } -func checkAndUpdateEmptyRepository(ctx context.Context, m *repo_model.Mirror, results []*mirrorSyncResult) bool { +func checkAndUpdateEmptyRepository(ctx context.Context, m *repo_model.Mirror, results []*repo_module.SyncResult) bool { if !m.Repo.IsEmpty { return true } @@ -562,11 +438,11 @@ func checkAndUpdateEmptyRepository(ctx context.Context, m *repo_model.Mirror, re } firstName := "" for _, result := range results { - if !result.refName.IsBranch() { + if !result.RefName.IsBranch() { continue } - name := result.refName.BranchName() + name := result.RefName.BranchName() if len(firstName) == 0 { firstName = name } diff --git a/services/mirror/mirror_pull_test.go b/services/mirror/mirror_pull_test.go index 97859be5b0..58b4da51a2 100644 --- a/services/mirror/mirror_pull_test.go +++ b/services/mirror/mirror_pull_test.go @@ -9,62 +9,6 @@ import ( "github.com/stretchr/testify/assert" ) -func Test_parseRemoteUpdateOutput(t *testing.T) { - output := ` - * [new tag] v0.1.8 -> v0.1.8 - * [new branch] master -> origin/master - - [deleted] (none) -> origin/test1 - - [deleted] (none) -> tag1 - + f895a1e...957a993 test2 -> origin/test2 (forced update) - 957a993..a87ba5f test3 -> origin/test3 - * [new ref] refs/pull/26595/head -> refs/pull/26595/head - * [new ref] refs/pull/26595/merge -> refs/pull/26595/merge - e0639e38fb..6db2410489 refs/pull/25873/head -> refs/pull/25873/head - + 1c97ebc746...976d27d52f refs/pull/25873/merge -> refs/pull/25873/merge (forced update) -` - results := parseRemoteUpdateOutput(output, "origin") - assert.Len(t, results, 10) - assert.Equal(t, "refs/tags/v0.1.8", results[0].refName.String()) - assert.Equal(t, gitShortEmptySha, results[0].oldCommitID) - assert.Empty(t, results[0].newCommitID) - - assert.Equal(t, "refs/heads/master", results[1].refName.String()) - assert.Equal(t, gitShortEmptySha, results[1].oldCommitID) - assert.Empty(t, results[1].newCommitID) - - assert.Equal(t, "refs/heads/test1", results[2].refName.String()) - assert.Empty(t, results[2].oldCommitID) - assert.Equal(t, gitShortEmptySha, results[2].newCommitID) - - assert.Equal(t, "refs/tags/tag1", results[3].refName.String()) - assert.Empty(t, results[3].oldCommitID) - assert.Equal(t, gitShortEmptySha, results[3].newCommitID) - - assert.Equal(t, "refs/heads/test2", results[4].refName.String()) - assert.Equal(t, "f895a1e", results[4].oldCommitID) - assert.Equal(t, "957a993", results[4].newCommitID) - - assert.Equal(t, "refs/heads/test3", results[5].refName.String()) - assert.Equal(t, "957a993", results[5].oldCommitID) - assert.Equal(t, "a87ba5f", results[5].newCommitID) - - assert.Equal(t, "refs/pull/26595/head", results[6].refName.String()) - assert.Equal(t, gitShortEmptySha, results[6].oldCommitID) - assert.Empty(t, results[6].newCommitID) - - assert.Equal(t, "refs/pull/26595/merge", results[7].refName.String()) - assert.Equal(t, gitShortEmptySha, results[7].oldCommitID) - assert.Empty(t, results[7].newCommitID) - - assert.Equal(t, "refs/pull/25873/head", results[8].refName.String()) - assert.Equal(t, "e0639e38fb", results[8].oldCommitID) - assert.Equal(t, "6db2410489", results[8].newCommitID) - - assert.Equal(t, "refs/pull/25873/merge", results[9].refName.String()) - assert.Equal(t, "1c97ebc746", results[9].oldCommitID) - assert.Equal(t, "976d27d52f", results[9].newCommitID) -} - func Test_checkRecoverableSyncError(t *testing.T) { cases := []struct { recoverable bool diff --git a/services/repository/adopt.go b/services/repository/adopt.go index 13218193d5..64e7f3f02b 100644 --- a/services/repository/adopt.go +++ b/services/repository/adopt.go @@ -147,11 +147,11 @@ func adoptRepository(ctx context.Context, repo *repo_model.Repository, defaultBr } defer gitRepo.Close() - if _, err = repo_module.SyncRepoBranchesWithRepo(ctx, repo, gitRepo, 0); err != nil { + if _, _, err = repo_module.SyncRepoBranchesWithRepo(ctx, repo, gitRepo, 0); err != nil { return fmt.Errorf("SyncRepoBranchesWithRepo: %w", err) } - if err = repo_module.SyncReleasesWithTags(ctx, repo, gitRepo); err != nil { + if _, err = repo_module.SyncReleasesWithTags(ctx, repo, gitRepo); err != nil { return fmt.Errorf("SyncReleasesWithTags: %w", err) } diff --git a/services/repository/files/diff.go b/services/repository/files/diff.go index 50d01f9d7c..aa4b55a307 100644 --- a/services/repository/files/diff.go +++ b/services/repository/files/diff.go @@ -12,7 +12,7 @@ import ( ) // GetDiffPreview produces and returns diff result of a file which is not yet committed. -func GetDiffPreview(ctx context.Context, repo *repo_model.Repository, branch, treePath, content string) (*gitdiff.Diff, error) { +func GetDiffPreview(ctx context.Context, repo *repo_model.Repository, branch, treePath, oldContent, newContent string) (*gitdiff.Diff, error) { if branch == "" { branch = repo.DefaultBranch } @@ -29,7 +29,7 @@ func GetDiffPreview(ctx context.Context, repo *repo_model.Repository, branch, tr } // Add the object to the database - objectHash, err := t.HashObjectAndWrite(ctx, strings.NewReader(content)) + objectHash, err := t.HashObjectAndWrite(ctx, strings.NewReader(newContent)) if err != nil { return nil, err } @@ -38,5 +38,5 @@ func GetDiffPreview(ctx context.Context, repo *repo_model.Repository, branch, tr if err := t.AddObjectToIndex(ctx, "100644", objectHash, treePath); err != nil { return nil, err } - return t.DiffIndex(ctx) + return t.DiffIndex(ctx, oldContent, newContent) } diff --git a/services/repository/files/diff_test.go b/services/repository/files/diff_test.go index ae702e4189..5295879621 100644 --- a/services/repository/files/diff_test.go +++ b/services/repository/files/diff_test.go @@ -27,8 +27,30 @@ func TestGetDiffPreview(t *testing.T) { branch := ctx.Repo.Repository.DefaultBranch treePath := "README.md" + oldContent := "# repo1\n\nDescription for repo1" content := "# repo1\n\nDescription for repo1\nthis is a new line" + t.Run("Errors", func(t *testing.T) { + t.Run("empty repo", func(t *testing.T) { + diff, err := GetDiffPreview(ctx, &repo_model.Repository{}, branch, treePath, oldContent, content) + assert.Nil(t, diff) + assert.EqualError(t, err, "repository does not exist [id: 0, uid: 0, owner_name: , name: ]") + }) + + t.Run("bad branch", func(t *testing.T) { + badBranch := "bad_branch" + diff, err := GetDiffPreview(ctx, ctx.Repo.Repository, badBranch, treePath, oldContent, content) + assert.Nil(t, diff) + assert.EqualError(t, err, "branch does not exist [name: "+badBranch+"]") + }) + + t.Run("empty treePath", func(t *testing.T) { + diff, err := GetDiffPreview(ctx, ctx.Repo.Repository, branch, "", oldContent, content) + assert.Nil(t, diff) + assert.EqualError(t, err, "path is invalid [path: ]") + }) + }) + expectedDiff := &gitdiff.Diff{ Files: []*gitdiff.DiffFile{ { @@ -112,56 +134,22 @@ func TestGetDiffPreview(t *testing.T) { } t.Run("with given branch", func(t *testing.T) { - diff, err := GetDiffPreview(ctx, ctx.Repo.Repository, branch, treePath, content) + diff, err := GetDiffPreview(ctx, ctx.Repo.Repository, branch, treePath, oldContent, content) assert.NoError(t, err) expectedBs, err := json.Marshal(expectedDiff) assert.NoError(t, err) bs, err := json.Marshal(diff) assert.NoError(t, err) - assert.Equal(t, string(expectedBs), string(bs)) + assert.JSONEq(t, string(expectedBs), string(bs)) }) t.Run("empty branch, same results", func(t *testing.T) { - diff, err := GetDiffPreview(ctx, ctx.Repo.Repository, "", treePath, content) + diff, err := GetDiffPreview(ctx, ctx.Repo.Repository, "", treePath, oldContent, content) assert.NoError(t, err) expectedBs, err := json.Marshal(expectedDiff) assert.NoError(t, err) bs, err := json.Marshal(diff) assert.NoError(t, err) - assert.Equal(t, expectedBs, bs) - }) -} - -func TestGetDiffPreviewErrors(t *testing.T) { - unittest.PrepareTestEnv(t) - ctx, _ := contexttest.MockContext(t, "user2/repo1") - ctx.SetPathParam("id", "1") - contexttest.LoadRepo(t, ctx, 1) - contexttest.LoadRepoCommit(t, ctx) - contexttest.LoadUser(t, ctx, 2) - contexttest.LoadGitRepo(t, ctx) - defer ctx.Repo.GitRepo.Close() - - branch := ctx.Repo.Repository.DefaultBranch - treePath := "README.md" - content := "# repo1\n\nDescription for repo1\nthis is a new line" - - t.Run("empty repo", func(t *testing.T) { - diff, err := GetDiffPreview(ctx, &repo_model.Repository{}, branch, treePath, content) - assert.Nil(t, diff) - assert.EqualError(t, err, "repository does not exist [id: 0, uid: 0, owner_name: , name: ]") - }) - - t.Run("bad branch", func(t *testing.T) { - badBranch := "bad_branch" - diff, err := GetDiffPreview(ctx, ctx.Repo.Repository, badBranch, treePath, content) - assert.Nil(t, diff) - assert.EqualError(t, err, "branch does not exist [name: "+badBranch+"]") - }) - - t.Run("empty treePath", func(t *testing.T) { - diff, err := GetDiffPreview(ctx, ctx.Repo.Repository, branch, "", content) - assert.Nil(t, diff) - assert.EqualError(t, err, "path is invalid [path: ]") + assert.JSONEq(t, string(expectedBs), string(bs)) }) } diff --git a/services/repository/files/temp_repo.go b/services/repository/files/temp_repo.go index 63f4f06d25..a579b807ba 100644 --- a/services/repository/files/temp_repo.go +++ b/services/repository/files/temp_repo.go @@ -361,7 +361,7 @@ func (t *TemporaryUploadRepository) Push(ctx context.Context, doer *user_model.U } // DiffIndex returns a Diff of the current index to the head -func (t *TemporaryUploadRepository) DiffIndex(ctx context.Context) (*gitdiff.Diff, error) { +func (t *TemporaryUploadRepository) DiffIndex(ctx context.Context, oldContent, newContent string) (*gitdiff.Diff, error) { var diff *gitdiff.Diff cmd := gitcmd.NewCommand("diff-index", "--src-prefix=\\a/", "--dst-prefix=\\b/", "--cached", "-p", "HEAD") stdoutReader, stdoutReaderClose := cmd.MakeStdoutPipe() @@ -383,6 +383,9 @@ func (t *TemporaryUploadRepository) DiffIndex(ctx context.Context) (*gitdiff.Dif return nil, fmt.Errorf("unable to run diff-index pipeline in temporary repo: %w", err) } + if len(diff.Files) > 0 { + gitdiff.FillDiffFileHighlightLinesByContent(diff.Files[0], util.UnsafeStringToBytes(oldContent), util.UnsafeStringToBytes(newContent)) + } return diff, nil } diff --git a/services/repository/fork.go b/services/repository/fork.go index 9c360c2f82..8cf41d5654 100644 --- a/services/repository/fork.go +++ b/services/repository/fork.go @@ -177,10 +177,10 @@ func ForkRepository(ctx context.Context, doer, owner *user_model.User, opts Fork } defer gitRepo.Close() - if _, err = repo_module.SyncRepoBranchesWithRepo(ctx, repo, gitRepo, doer.ID); err != nil { + if _, _, err = repo_module.SyncRepoBranchesWithRepo(ctx, repo, gitRepo, doer.ID); err != nil { return nil, fmt.Errorf("SyncRepoBranchesWithRepo: %w", err) } - if err = repo_module.SyncReleasesWithTags(ctx, repo, gitRepo); err != nil { + if _, err = repo_module.SyncReleasesWithTags(ctx, repo, gitRepo); err != nil { return nil, fmt.Errorf("Sync releases from git tags failed: %v", err) } diff --git a/services/repository/migrate.go b/services/repository/migrate.go index bc46c5e09b..a51791ed29 100644 --- a/services/repository/migrate.go +++ b/services/repository/migrate.go @@ -145,7 +145,7 @@ func MigrateRepositoryGitData(ctx context.Context, u *user_model.User, } } - if _, err := repo_module.SyncRepoBranchesWithRepo(ctx, repo, gitRepo, u.ID); err != nil { + if _, _, err := repo_module.SyncRepoBranchesWithRepo(ctx, repo, gitRepo, u.ID); err != nil { return repo, fmt.Errorf("SyncRepoBranchesWithRepo: %v", err) } @@ -153,7 +153,7 @@ func MigrateRepositoryGitData(ctx context.Context, u *user_model.User, // otherwise, the releases sync will be done out of this function if !opts.Releases { repo.IsMirror = opts.Mirror - if err = repo_module.SyncReleasesWithTags(ctx, repo, gitRepo); err != nil { + if _, err = repo_module.SyncReleasesWithTags(ctx, repo, gitRepo); err != nil { log.Error("Failed to synchronize tags to releases for repository: %v", err) } } diff --git a/templates/admin/auth/list.tmpl b/templates/admin/auth/list.tmpl index a1e72b742f..7296e95558 100644 --- a/templates/admin/auth/list.tmpl +++ b/templates/admin/auth/list.tmpl @@ -7,7 +7,7 @@
- +
diff --git a/templates/admin/cron.tmpl b/templates/admin/cron.tmpl index 4d01ce51eb..8e44614d97 100644 --- a/templates/admin/cron.tmpl +++ b/templates/admin/cron.tmpl @@ -5,7 +5,7 @@
-
ID
+
diff --git a/templates/admin/emails/list.tmpl b/templates/admin/emails/list.tmpl index 50e453916d..d597a2753f 100644 --- a/templates/admin/emails/list.tmpl +++ b/templates/admin/emails/list.tmpl @@ -24,7 +24,7 @@
-
+
diff --git a/templates/admin/notice.tmpl b/templates/admin/notice.tmpl index 0499b0adbb..7e08946f23 100644 --- a/templates/admin/notice.tmpl +++ b/templates/admin/notice.tmpl @@ -3,7 +3,7 @@

{{ctx.Locale.Tr "admin.notices.system_notice_list"}} ({{ctx.Locale.Tr "admin.total" .Total}})

- +
diff --git a/templates/admin/org/list.tmpl b/templates/admin/org/list.tmpl index 137c42b45d..c2c0280e4c 100644 --- a/templates/admin/org/list.tmpl +++ b/templates/admin/org/list.tmpl @@ -29,7 +29,7 @@
-
+
diff --git a/templates/admin/packages/list.tmpl b/templates/admin/packages/list.tmpl index 4d458f48fb..553130ae09 100644 --- a/templates/admin/packages/list.tmpl +++ b/templates/admin/packages/list.tmpl @@ -26,7 +26,7 @@
-
ID{{SortArrow "oldest" "newest" $.SortType false}}
+
diff --git a/templates/admin/queue.tmpl b/templates/admin/queue.tmpl index 1be35cf765..e6abd1e794 100644 --- a/templates/admin/queue.tmpl +++ b/templates/admin/queue.tmpl @@ -4,7 +4,7 @@ {{ctx.Locale.Tr "admin.monitor.queues"}}
-
ID
+
diff --git a/templates/admin/queue_manage.tmpl b/templates/admin/queue_manage.tmpl index a793fe1350..73da9164cc 100644 --- a/templates/admin/queue_manage.tmpl +++ b/templates/admin/queue_manage.tmpl @@ -4,7 +4,7 @@ {{ctx.Locale.Tr "admin.monitor.queue" .Queue.GetName}}
-
{{ctx.Locale.Tr "admin.monitor.queue.name"}}
+
diff --git a/templates/admin/repo/list.tmpl b/templates/admin/repo/list.tmpl index f21e5bfef2..0af9df2551 100644 --- a/templates/admin/repo/list.tmpl +++ b/templates/admin/repo/list.tmpl @@ -10,7 +10,7 @@ {{template "shared/repo/search" .}}
-
{{ctx.Locale.Tr "admin.monitor.queue.name"}}
+
diff --git a/templates/admin/stats.tmpl b/templates/admin/stats.tmpl index 04fa862a85..bb6a86ac30 100644 --- a/templates/admin/stats.tmpl +++ b/templates/admin/stats.tmpl @@ -4,7 +4,7 @@ {{ctx.Locale.Tr "admin.dashboard.statistic"}}
-
ID{{SortArrow "oldest" "newest" $.SortType false}}
+
{{range $statsKey := .StatsKeys}} diff --git a/templates/admin/user/list.tmpl b/templates/admin/user/list.tmpl index 49f62dda74..168603354d 100644 --- a/templates/admin/user/list.tmpl +++ b/templates/admin/user/list.tmpl @@ -56,7 +56,7 @@
-
{{$statsKey}}
+
diff --git a/templates/package/content/rpm.tmpl b/templates/package/content/rpm.tmpl index 3faa8a0dc7..8aebe62806 100644 --- a/templates/package/content/rpm.tmpl +++ b/templates/package/content/rpm.tmpl @@ -14,6 +14,12 @@ dnf config-manager --add-repo {{- end}} +# Fedora 41+ (DNF5) +{{- range $group := .Groups}} + {{- if $group}}{{$group = print "/" $group}}{{end}} +dnf config-manager addrepo --from-repofile= +{{- end}} + # {{ctx.Locale.Tr "packages.rpm.distros.suse"}} {{- range $group := .Groups}} {{- if $group}}{{$group = print "/" $group}}{{end}} diff --git a/templates/package/shared/cleanup_rules/preview.tmpl b/templates/package/shared/cleanup_rules/preview.tmpl index bb2354c944..0991a07fbc 100644 --- a/templates/package/shared/cleanup_rules/preview.tmpl +++ b/templates/package/shared/cleanup_rules/preview.tmpl @@ -3,7 +3,7 @@

{{ctx.Locale.Tr "packages.owner.settings.cleanuprules.preview.overview" (len .VersionsToRemove)}}

-
ID{{SortArrow "oldest" "newest" .SortType false}}
+
diff --git a/templates/repo/branch/list.tmpl b/templates/repo/branch/list.tmpl index 9f6f24bbf4..5b56b3ed42 100644 --- a/templates/repo/branch/list.tmpl +++ b/templates/repo/branch/list.tmpl @@ -15,7 +15,7 @@
-
{{ctx.Locale.Tr "admin.packages.type"}}
+
@@ -83,7 +83,7 @@
- +
{{range .Branches}} diff --git a/templates/repo/commits_list.tmpl b/templates/repo/commits_list.tmpl index 959f2a9398..1a236582a2 100644 --- a/templates/repo/commits_list.tmpl +++ b/templates/repo/commits_list.tmpl @@ -1,5 +1,5 @@
-
+
diff --git a/templates/shared/actions/runner_edit.tmpl b/templates/shared/actions/runner_edit.tmpl index 8c434971ad..8652d161bc 100644 --- a/templates/shared/actions/runner_edit.tmpl +++ b/templates/shared/actions/runner_edit.tmpl @@ -49,7 +49,7 @@ {{ctx.Locale.Tr "actions.runners.task_list"}}
-
{{ctx.Locale.Tr "repo.commits.author"}}
+
diff --git a/templates/shared/actions/runner_list.tmpl b/templates/shared/actions/runner_list.tmpl index 43321a8dc5..9c541c95ee 100644 --- a/templates/shared/actions/runner_list.tmpl +++ b/templates/shared/actions/runner_list.tmpl @@ -41,7 +41,7 @@
-
{{ctx.Locale.Tr "actions.runners.task_list.run"}}
+
@@ -72,7 +72,7 @@ {{if .Version}}{{.Version}}{{else}}{{ctx.Locale.Tr "unknown"}}{{end}} {{.BelongsToOwnerType.LocaleString ctx.Locale}} - {{range .AgentLabels}}{{.}}{{end}} + {{range .AgentLabels}}{{.}}{{end}} {{if .LastOnline}}{{DateUtils.TimeSince .LastOnline}}{{else}}{{ctx.Locale.Tr "never"}}{{end}} diff --git a/templates/user/dashboard/feeds.tmpl b/templates/user/dashboard/feeds.tmpl index d525ce78ff..c3c042dc38 100644 --- a/templates/user/dashboard/feeds.tmpl +++ b/templates/user/dashboard/feeds.tmpl @@ -56,11 +56,11 @@ {{$index := index .GetIssueInfos 0}} {{ctx.Locale.Tr "action.delete_branch" (.GetRepoLink ctx) .GetBranch (.ShortRepoPath ctx)}} {{else if .GetOpType.InActions "mirror_sync_push"}} - {{ctx.Locale.Tr "action.mirror_sync_push" (.GetRepoLink ctx) (.GetRefLink ctx) .GetBranch (.ShortRepoPath ctx)}} + {{ctx.Locale.Tr "action.mirror_sync_push" (.GetRepoLink ctx) (.GetRefLink ctx) .RefName (.ShortRepoPath ctx)}} {{else if .GetOpType.InActions "mirror_sync_create"}} - {{ctx.Locale.Tr "action.mirror_sync_create" (.GetRepoLink ctx) (.GetRefLink ctx) .GetBranch (.ShortRepoPath ctx)}} + {{ctx.Locale.Tr "action.mirror_sync_create" (.GetRepoLink ctx) (.GetRefLink ctx) .RefName (.ShortRepoPath ctx)}} {{else if .GetOpType.InActions "mirror_sync_delete"}} - {{ctx.Locale.Tr "action.mirror_sync_delete" (.GetRepoLink ctx) .GetBranch (.ShortRepoPath ctx)}} + {{ctx.Locale.Tr "action.mirror_sync_delete" (.GetRepoLink ctx) .RefName (.ShortRepoPath ctx)}} {{else if .GetOpType.InActions "approve_pull_request"}} {{$index := index .GetIssueInfos 0}} {{ctx.Locale.Tr "action.approve_pull_request" (printf "%s/pulls/%s" (.GetRepoLink ctx) $index) $index (.ShortRepoPath ctx)}} diff --git a/tests/integration/editor_test.go b/tests/integration/editor_test.go index d4681f1699..c70bb061c9 100644 --- a/tests/integration/editor_test.go +++ b/tests/integration/editor_test.go @@ -148,10 +148,10 @@ func testEditFileToNewBranch(t *testing.T, session *TestSession, user, repo, bra func testEditorDiffPreview(t *testing.T) { session := loginUser(t, "user2") req := NewRequestWithValues(t, "POST", "/user2/repo1/_preview/master/README.md", map[string]string{ - "content": "Hello, World (Edited)\n", + "content": "# repo1 (Edited)", }) resp := session.MakeRequest(t, req, http.StatusOK) - assert.Contains(t, resp.Body.String(), `Hello, World (Edited)`) + assert.Contains(t, resp.Body.String(), ` (Edited)`) } func testEditorPatchFile(t *testing.T) { diff --git a/tests/integration/pull_commit_test.go b/tests/integration/pull_commit_test.go index 9f3b1a9ef5..01b8ec1ff4 100644 --- a/tests/integration/pull_commit_test.go +++ b/tests/integration/pull_commit_test.go @@ -36,6 +36,6 @@ func TestListPullCommits(t *testing.T) { defer tests.PrintCurrentTest(t)() req = NewRequest(t, "GET", "/user2/repo1/blob_excerpt/985f0301dba5e7b34be866819cd15ad3d8f508ee?last_left=0&last_right=0&left=2&right=2&left_hunk_size=2&right_hunk_size=2&path=README.md&style=split&direction=up") resp = session.MakeRequest(t, req, http.StatusOK) - assert.Contains(t, resp.Body.String(), `# repo1`) + assert.Contains(t, resp.Body.String(), `# repo1`+"\n"+``) }) } diff --git a/updates.config.ts b/updates.config.ts index 7bf680bbde..bc9e368fb4 100644 --- a/updates.config.ts +++ b/updates.config.ts @@ -4,6 +4,7 @@ export default { exclude: [ '@mcaptcha/vanilla-glue', // breaking changes in rc versions need to be handled 'cropperjs', // need to migrate to v2 but v2 is not compatible with v1 + 'eslint', // need to migrate to v10 'tailwindcss', // need to migrate '@eslint/json', // needs eslint 10 ], diff --git a/uv.lock b/uv.lock index 33e9b64b2c..10fdbf2bf3 100644 --- a/uv.lock +++ b/uv.lock @@ -127,11 +127,11 @@ wheels = [ [[package]] name = "pathspec" -version = "1.0.3" +version = "1.0.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4c/b2/bb8e495d5262bfec41ab5cb18f522f1012933347fb5d9e62452d446baca2/pathspec-1.0.3.tar.gz", hash = "sha256:bac5cf97ae2c2876e2d25ebb15078eb04d76e4b98921ee31c6f85ade8b59444d", size = 130841, upload-time = "2026-01-09T15:46:46.009Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fa/36/e27608899f9b8d4dff0617b2d9ab17ca5608956ca44461ac14ac48b44015/pathspec-1.0.4.tar.gz", hash = "sha256:0210e2ae8a21a9137c0d470578cb0e595af87edaa6ebf12ff176f14a02e0e645", size = 131200, upload-time = "2026-01-27T03:59:46.938Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/32/2b/121e912bd60eebd623f873fd090de0e84f322972ab25a7f9044c056804ed/pathspec-1.0.3-py3-none-any.whl", hash = "sha256:e80767021c1cc524aa3fb14bedda9c34406591343cc42797b386ce7b9354fb6c", size = 55021, upload-time = "2026-01-09T15:46:44.652Z" }, + { url = "https://files.pythonhosted.org/packages/ef/3c/2c197d226f9ea224a9ab8d197933f9da0ae0aac5b6e0f884e2b8d9c8e9f7/pathspec-1.0.4-py3-none-any.whl", hash = "sha256:fb6ae2fd4e7c921a165808a552060e722767cfa526f99ca5156ed2ce45a5c723", size = 55206, upload-time = "2026-01-27T03:59:45.137Z" }, ] [[package]] @@ -384,14 +384,14 @@ wheels = [ [[package]] name = "tqdm" -version = "4.67.1" +version = "4.67.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a8/4b/29b4ef32e036bb34e4ab51796dd745cdba7ed47ad142a9f4a1eb8e0c744d/tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2", size = 169737, upload-time = "2024-11-24T20:12:22.481Z" } +sdist = { url = "https://files.pythonhosted.org/packages/09/a9/6ba95a270c6f1fbcd8dac228323f2777d886cb206987444e4bce66338dd4/tqdm-4.67.3.tar.gz", hash = "sha256:7d825f03f89244ef73f1d4ce193cb1774a8179fd96f31d7e1dcde62092b960bb", size = 169598, upload-time = "2026-02-03T17:35:53.048Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", size = 78540, upload-time = "2024-11-24T20:12:19.698Z" }, + { url = "https://files.pythonhosted.org/packages/16/e1/3079a9ff9b8e11b846c6ac5c8b5bfb7ff225eee721825310c91b3b50304f/tqdm-4.67.3-py3-none-any.whl", hash = "sha256:ee1e4c0e59148062281c49d80b25b67771a127c85fc9676d3be5f243206826bf", size = 78374, upload-time = "2026-02-03T17:35:50.982Z" }, ] [[package]] diff --git a/web_src/css/base.css b/web_src/css/base.css index 4dc19d9a5b..c0caee5191 100644 --- a/web_src/css/base.css +++ b/web_src/css/base.css @@ -30,6 +30,7 @@ --page-spacing: 16px; /* space between page elements */ --page-margin-x: 32px; /* minimum space on left and right side of page */ --page-space-bottom: 64px; /* space between last page element and footer */ + --transition-hover-fade: opacity 0.2s ease; /* fade transition for elements that show on hover */ /* z-index */ --z-index-modal: 1001; /* modal dialog, hard-coded from Fomantic modal.css */ diff --git a/web_src/css/markup/codecopy.css b/web_src/css/markup/codecopy.css index 5a7b9955e7..c48f641f68 100644 --- a/web_src/css/markup/codecopy.css +++ b/web_src/css/markup/codecopy.css @@ -3,8 +3,9 @@ top: 8px; right: 6px; padding: 9px; - visibility: hidden; - animation: fadeout 0.2s both; + visibility: hidden; /* prevent from click events even opacity=0 */ + opacity: 0; + transition: var(--transition-hover-fade); } /* adjustments for comment content having only 14px font size */ @@ -23,8 +24,17 @@ background: var(--color-secondary-dark-1) !important; } +/* all rendered code-block elements are in their container, +the manually written code-block elements on "packages" pages don't have the container */ .markup .code-block-container:hover .code-copy, .markup .code-block:hover .code-copy { visibility: visible; - animation: fadein 0.2s both; + opacity: 1; +} + +@media (hover: none) { + .markup .code-copy { + visibility: visible; + opacity: 1; + } } diff --git a/web_src/css/modules/animations.css b/web_src/css/modules/animations.css index aedf53569a..65d7a90e97 100644 --- a/web_src/css/modules/animations.css +++ b/web_src/css/modules/animations.css @@ -82,15 +82,6 @@ code.language-math.is-loading::after { } } -@keyframes fadeout { - 0% { - opacity: 1; - } - 100% { - opacity: 0; - } -} - /* 1p5 means 1-point-5. it can't use "pulse" here, otherwise the animation is not right (maybe due to some conflicts */ @keyframes pulse-1p5 { 0% { diff --git a/web_src/css/modules/table.css b/web_src/css/modules/table.css index 6298471d47..c7d6cb7a48 100644 --- a/web_src/css/modules/table.css +++ b/web_src/css/modules/table.css @@ -196,11 +196,6 @@ margin-bottom: 0; } -.ui.striped.table > tr:nth-child(2n), -.ui.striped.table > tbody > tr:nth-child(2n) { - background: var(--color-light); -} - .ui.table[class*="single line"], .ui.table [class*="single line"] { white-space: nowrap; @@ -291,37 +286,10 @@ .ui.basic.table > tr > td { background: transparent; } -.ui.basic.striped.table > tbody > tr:nth-child(2n) { - background: var(--color-light); -} -.ui.basic.striped.selectable.table > tbody > tr:nth-child(2n):hover { - background: var(--color-hover); -} .ui[class*="very basic"].table { border: none; } -.ui[class*="very basic"].table:not(.striped) > tr > th:first-child, -.ui[class*="very basic"].table:not(.striped) > thead > tr > th:first-child, -.ui[class*="very basic"].table:not(.striped) > tbody > tr > th:first-child, -.ui[class*="very basic"].table:not(.striped) > tfoot > tr > th:first-child, -.ui[class*="very basic"].table:not(.striped) > tr > td:first-child, -.ui[class*="very basic"].table:not(.striped) > tbody > tr > td:first-child, -.ui[class*="very basic"].table:not(.striped) > tfoot > tr > td:first-child { - padding-left: 0; -} -.ui[class*="very basic"].table:not(.striped) > tr > th:last-child, -.ui[class*="very basic"].table:not(.striped) > thead > tr > th:last-child, -.ui[class*="very basic"].table:not(.striped) > tbody > tr > th:last-child, -.ui[class*="very basic"].table:not(.striped) > tfoot > tr > th:last-child, -.ui[class*="very basic"].table:not(.striped) > tr > td:last-child, -.ui[class*="very basic"].table:not(.striped) > tbody > tr > td:last-child, -.ui[class*="very basic"].table:not(.striped) > tfoot > tr > td:last-child { - padding-right: 0; -} -.ui[class*="very basic"].table:not(.striped) > thead > tr:first-child > th { - padding-top: 0; -} .ui.celled.table > tr > th, .ui.celled.table > thead > tr > th, diff --git a/web_src/css/repo.css b/web_src/css/repo.css index 16fbfaec4a..83df3e5c29 100644 --- a/web_src/css/repo.css +++ b/web_src/css/repo.css @@ -774,10 +774,6 @@ td .commit-summary { width: 200px; } -.repository #commits-table.ui.basic.striped.table tbody tr:nth-child(2n) { - background-color: var(--color-light) !important; -} - .repository .data-table { width: 100%; } diff --git a/web_src/css/repo/commit-sign.css b/web_src/css/repo/commit-sign.css index 56eee62ffc..101eb6d528 100644 --- a/web_src/css/repo/commit-sign.css +++ b/web_src/css/repo/commit-sign.css @@ -7,6 +7,10 @@ flex-shrink: 0; } +.ui.label.commit-sign-badge > * { + display: flex; +} + .ui.label.commit-id-short { font-family: var(--fonts-monospace); height: 24px; diff --git a/web_src/js/features/repo-diff.ts b/web_src/js/features/repo-diff.ts index e63de7b3cf..0c8b1357b0 100644 --- a/web_src/js/features/repo-diff.ts +++ b/web_src/js/features/repo-diff.ts @@ -170,7 +170,9 @@ async function loadMoreFiles(btn: Element): Promise { const respFileBoxes = respDoc.querySelector('#diff-file-boxes')!; // the response is a full HTML page, we need to extract the relevant contents: // * append the newly loaded file list items to the existing list - document.querySelector('#diff-incomplete')!.replaceWith(...Array.from(respFileBoxes.children)); + const respFileBoxesChildren = Array.from(respFileBoxes.children); // "children:HTMLCollection" will be empty after replaceWith + document.querySelector('#diff-incomplete')!.replaceWith(...respFileBoxesChildren); + for (const el of respFileBoxesChildren) window.htmx.process(el); onShowMoreFiles(); return true; } catch (error) { @@ -200,7 +202,7 @@ function initRepoDiffShowMore() { const resp = await response.text(); const respDoc = parseDom(resp, 'text/html'); const respFileBody = respDoc.querySelector('#diff-file-boxes .diff-file-body .file-body')!; - const respFileBodyChildren = Array.from(respFileBody.children); // respFileBody.children will be empty after replaceWith + const respFileBodyChildren = Array.from(respFileBody.children); // "children:HTMLCollection" will be empty after replaceWith el.parentElement!.replaceWith(...respFileBodyChildren); for (const el of respFileBodyChildren) window.htmx.process(el); // FIXME: calling onShowMoreFiles is not quite right here. diff --git a/web_src/js/features/tribute.ts b/web_src/js/features/tribute.ts index 0239204f9e..8434a9b2ac 100644 --- a/web_src/js/features/tribute.ts +++ b/web_src/js/features/tribute.ts @@ -1,6 +1,7 @@ import {emojiKeys, emojiHTML, emojiString} from './emoji.ts'; import {html, htmlRaw} from '../utils/html.ts'; import type {TributeCollection} from 'tributejs'; +import type {MentionValue} from '../types.ts'; export async function attachTribute(element: HTMLElement) { const {default: Tribute} = await import(/* webpackChunkName: "tribute" */'tributejs'); @@ -28,7 +29,7 @@ export async function attachTribute(element: HTMLElement) { }, }; - const mentionCollection: TributeCollection> = { + const mentionCollection: TributeCollection = { values: window.config.mentionValues, requireLeadingSpace: true, menuItemTemplate: (item) => { @@ -44,7 +45,10 @@ export async function attachTribute(element: HTMLElement) { }; const tribute = new Tribute({ - collection: [emojiCollection as TributeCollection, mentionCollection], + collection: [ + emojiCollection as TributeCollection, + mentionCollection as TributeCollection, + ], noMatchTemplate: () => '', }); tribute.attach(element); diff --git a/web_src/js/globals.d.ts b/web_src/js/globals.d.ts index ee90bc3c9c..8de6581dbc 100644 --- a/web_src/js/globals.d.ts +++ b/web_src/js/globals.d.ts @@ -29,13 +29,7 @@ interface Window { pageData: Record, notificationSettings: Record, enableTimeTracking: boolean, - mentionValues: Array<{ - key: string, - value: string, - name: string, - fullname: string, - avatar: string, - }>, + mentionValues: Array, mermaidMaxSourceCharacters: number, i18n: Record, }, diff --git a/web_src/js/markup/mermaid.ts b/web_src/js/markup/mermaid.ts index 5d37c81b8f..cd62990ffd 100644 --- a/web_src/js/markup/mermaid.ts +++ b/web_src/js/markup/mermaid.ts @@ -1,16 +1,56 @@ import {isDarkTheme, parseDom} from '../utils.ts'; import {makeCodeCopyButton} from './codecopy.ts'; import {displayError} from './common.ts'; -import {createElementFromAttrs, queryElems} from '../utils/dom.ts'; -import {html, htmlRaw} from '../utils/html.ts'; +import {createElementFromAttrs, createElementFromHTML, getCssRootVariablesText, queryElems} from '../utils/dom.ts'; +import {html} from '../utils/html.ts'; import {load as loadYaml} from 'js-yaml'; import type {MermaidConfig} from 'mermaid'; const {mermaidMaxSourceCharacters} = window.config; -const iframeCss = `:root {color-scheme: normal} -body {margin: 0; padding: 0; overflow: hidden} -#mermaid {display: block; margin: 0 auto}`; +function getIframeCss(): string { + // Inherit some styles (e.g.: root variables) from parent document. + // The buttons should use the same styles as `button.code-copy`, and align with it. + return ` +${getCssRootVariablesText()} + +html, body { height: 100%; } +body { margin: 0; padding: 0; overflow: hidden; } +#mermaid { display: block; margin: 0 auto; } + +.view-controller { + position: absolute; + z-index: 1; + right: 5px; + bottom: 5px; + display: flex; + gap: 4px; + visibility: hidden; + opacity: 0; + transition: var(--transition-hover-fade); + margin-right: 0.25em; +} +body:hover .view-controller { visibility: visible; opacity: 1; } +@media (hover: none) { + .view-controller { visibility: visible; opacity: 1; } +} +.view-controller button { + cursor: pointer; + display: inline-flex; + justify-content: center; + align-items: center; + line-height: 1; + padding: 7.5px 10px; + border: 1px solid var(--color-light-border); + border-radius: var(--border-radius); + background: var(--color-button); + color: var(--color-text); + user-select: none; +} +.view-controller button:hover { background: var(--color-secondary); } +.view-controller button:active { background: var(--color-secondary-dark-1); } +`; +} function isSourceTooLarge(source: string) { return mermaidMaxSourceCharacters >= 0 && source.length > mermaidMaxSourceCharacters; @@ -77,6 +117,76 @@ async function loadMermaid(needElkRender: boolean) { }; } +function initMermaidViewController(dragElement: SVGSVGElement) { + let inited = false, isDragging = false; + let currentScale = 1, initLeft = 0, lastLeft = 0, lastTop = 0, lastPageX = 0, lastPageY = 0; + const container = dragElement.parentElement!; + + const resetView = () => { + currentScale = 1; + lastLeft = initLeft; + lastTop = 0; + dragElement.style.left = `${lastLeft}px`; + dragElement.style.top = `${lastTop}px`; + dragElement.style.position = 'absolute'; + dragElement.style.margin = '0'; + }; + + const initAbsolutePosition = () => { + if (inited) return; + // if we need to drag or zoom, use absolute position and get the current "left" from the "margin: auto" layout. + inited = true; + initLeft = container.getBoundingClientRect().width / 2 - dragElement.getBoundingClientRect().width / 2; + resetView(); + }; + + for (const el of queryElems(container, '[data-control-action]')) { + el.addEventListener('click', () => { + initAbsolutePosition(); + switch (el.getAttribute('data-control-action')) { + case 'zoom-in': + currentScale *= 1.2; + break; + case 'zoom-out': + currentScale /= 1.2; + break; + case 'reset': + resetView(); + break; + } + dragElement.style.transform = `scale(${currentScale})`; + }); + } + + dragElement.addEventListener('mousedown', (e) => { + if (e.button !== 0 || e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) return; // only left mouse button can drag + const target = e.target as Element; + if (target.closest('div, p, a, span, button, input')) return; // don't start the drag if the click is on an interactive element (e.g.: link, button) or text element + + initAbsolutePosition(); + isDragging = true; + lastPageX = e.pageX; + lastPageY = e.pageY; + dragElement.style.cursor = 'grabbing'; + }); + + dragElement.ownerDocument.addEventListener('mousemove', (e) => { + if (!isDragging) return; + lastLeft = e.pageX - lastPageX + lastLeft; + lastTop = e.pageY - lastPageY + lastTop; + dragElement.style.left = `${lastLeft}px`; + dragElement.style.top = `${lastTop}px`; + lastPageX = e.pageX; + lastPageY = e.pageY; + }); + + dragElement.ownerDocument.addEventListener('mouseup', () => { + if (!isDragging) return; + isDragging = false; + dragElement.style.removeProperty('cursor'); + }); +} + let elkLayoutsRegistered = false; export async function initMarkupCodeMermaid(elMarkup: HTMLElement): Promise { @@ -107,6 +217,13 @@ export async function initMarkupCodeMermaid(elMarkup: HTMLElement): Promise { + if (!height) return; + // use a min-height to make sure the buttons won't overlap. + iframe.style.height = `${Math.max(height, 85)}px`; + }; + // mermaid is a globally shared instance, its document also says "Multiple calls to this function will be enqueued to run serially." // so here we just simply render the mermaid blocks one by one, no need to do "Promise.all" concurrently for (const block of mermaidBlocks) { @@ -122,27 +239,37 @@ export async function initMarkupCodeMermaid(elMarkup: HTMLElement): Promise`; + // create an iframe to sandbox the svg with styles, and set correct height by reading svg's viewBox height const iframe = document.createElement('iframe'); iframe.classList.add('markup-content-iframe', 'is-loading'); - iframe.srcdoc = html``; + // the styles are not ready, so don't really render anything before the "load" event, to avoid flicker of unstyled content + iframe.srcdoc = html``; // although the "viewBox" is optional, mermaid's output should always have a correct viewBox with width and height const iframeHeightFromViewBox = Math.ceil(svgNode.viewBox?.baseVal?.height ?? 0); - if (iframeHeightFromViewBox) iframe.style.height = `${iframeHeightFromViewBox}px`; + applyMermaidIframeHeight(iframe, iframeHeightFromViewBox); // the iframe will be fully reloaded if its DOM context is changed (e.g.: moved in the DOM tree). // to avoid unnecessary reloading, we should insert the iframe to its final position only once. iframe.addEventListener('load', () => { - // same origin, so we can operate "iframe body" and all elements directly + // same origin, so we can operate "iframe head/body" and all elements directly + const style = document.createElement('style'); + style.textContent = iframeStyleText; + iframe.contentDocument!.head.append(style); + const iframeBody = iframe.contentDocument!.body; iframeBody.append(svgNode); bindFunctions?.(iframeBody); // follow "mermaid.render" doc, attach event handlers to the svg's container + iframeBody.append(createElementFromHTML(viewControllerHtml)); // according to mermaid, the viewBox height should always exist, here just a fallback for unknown cases. // and keep in mind: clientHeight can be 0 if the element is hidden (display: none). - if (!iframeHeightFromViewBox && iframeBody.clientHeight) iframe.style.height = `${iframeBody.clientHeight}px`; + if (!iframeHeightFromViewBox) applyMermaidIframeHeight(iframe, iframeBody.clientHeight); iframe.classList.remove('is-loading'); + + initMermaidViewController(svgNode); }); const container = createElementFromAttrs('div', {class: 'mermaid-block'}, iframe, makeCodeCopyButton({'data-clipboard-text': source})); diff --git a/web_src/js/render/ansi.ts b/web_src/js/render/ansi.ts index 685e916c9a..f5429ef6ad 100644 --- a/web_src/js/render/ansi.ts +++ b/web_src/js/render/ansi.ts @@ -31,7 +31,7 @@ export function renderAnsi(line: string): string { // handle "\rReading...1%\rReading...5%\rReading...100%", // convert it into a multiple-line string: "Reading...1%\nReading...5%\nReading...100%" - const lines = []; + const lines: Array = []; for (const part of line.split('\r')) { if (part === '') continue; const partHtml = ansi_up.ansi_to_html(part); diff --git a/web_src/js/types.ts b/web_src/js/types.ts index 56527729a1..815dfd2f82 100644 --- a/web_src/js/types.ts +++ b/web_src/js/types.ts @@ -2,6 +2,14 @@ export type IntervalId = ReturnType; export type Intent = 'error' | 'warning' | 'info'; +export type MentionValue = { + key: string, + value: string, + name: string, + fullname: string, + avatar: string, +}; + export type RequestData = string | FormData | URLSearchParams | Record; export type RequestOpts = { diff --git a/web_src/js/utils/dom.ts b/web_src/js/utils/dom.ts index dc504b7056..12b984a9d5 100644 --- a/web_src/js/utils/dom.ts +++ b/web_src/js/utils/dom.ts @@ -352,6 +352,22 @@ export function isPlainClick(e: MouseEvent) { return e.button === 0 && !e.ctrlKey && !e.metaKey && !e.altKey && !e.shiftKey; } +let cssRootVariablesTextCache: string = ''; +export function getCssRootVariablesText(): string { + if (cssRootVariablesTextCache) return cssRootVariablesTextCache; + const style = getComputedStyle(document.documentElement); + let text = ':root {\n'; + for (let i = 0; i < style.length; i++) { + const name = style.item(i); + if (name.startsWith('--')) { + text += ` ${name}: ${style.getPropertyValue(name)};\n`; + } + } + text += '}\n'; + cssRootVariablesTextCache = text; + return text; +} + let elemIdCounter = 0; export function generateElemId(prefix: string = ''): string { return `${prefix}${elemIdCounter++}`; diff --git a/web_src/js/utils/glob.test.ts b/web_src/js/utils/glob.test.ts index 0c5d9783c0..fbf83a6cfe 100644 --- a/web_src/js/utils/glob.test.ts +++ b/web_src/js/utils/glob.test.ts @@ -117,7 +117,6 @@ test('GlobCompiler', async () => { for (const c of golangCases) { const compiled = globCompile(c.pattern, c.separators); const msg = `pattern: ${c.pattern}, input: ${c.input}, separators: ${c.separators || '(none)'}, compiled: ${compiled.regexpPattern}`; - // eslint-disable-next-line vitest/valid-expect -- Unlike Jest, Vitest supports a message as the second argument expect(compiled.regexp.test(c.input), msg).toBe(c.matched); }