diff --git a/.github/workflows/on-release-created.yml b/.github/workflows/on-release-created.yml index 7d19678574..4e7ff2c3c4 100644 --- a/.github/workflows/on-release-created.yml +++ b/.github/workflows/on-release-created.yml @@ -28,6 +28,9 @@ jobs: cache: 'pnpm' # see https://docs.github.com/actions/use-cases-and-examples/publishing-packages/publishing-nodejs-packages#publishing-packages-to-the-npm-registry registry-url: 'https://registry.npmjs.org' + # Ensure npm 11.5.1 or later is installed + - name: Update npm + run: npm install -g npm@latest - name: Publish package run: | pnpm i --frozen-lockfile diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d579ec0b8..f9447fc10b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ - Fix: 一部のページ内リンクが正しく動作しない問題を修正 - Fix: ドライブへの画像アップロード時にファイル名の変更が無視される不具合を修正 - Fix: 連合が無効化されたサーバーで一部の設定項目が空欄で表示される問題を修正 +- Fix: オーディオ、動画の再生速度メニューが開けない問題を修正 ### Server - Enhance: メモリ使用量を削減 @@ -23,6 +24,12 @@ - Fix: ID生成アルゴリズムにULIDを使用している場合にMisskeyが正しく動作しない問題を修正 - Fix: リレー経由で届いたノートがリノートとして表示される問題を修正 - Fix: robots.txtの内容を調整 +- Fix: 特定のユーザーに管理者権限を持つロールが複数ついている際に、取得できるユーザーIDが重複する問題を修正 + (Cherry-picked from https://github.com/lqvp/misskey-tempura/commit/17ed4108cec4b6bd2fd989db5a9091db91fa37a7) +- Fix: ブロックしたサーバーからのInboxジョブが蓄積し続ける問題を修正 + (Cherry-picked from https://github.com/lqvp/misskey-tempura/commit/3f0f4bfe923f2b3a7837017b54841598f421c6ef) +- Fix: support activity with `actor` as an id string or embedded object in inbox processor and ActivityPub inbox service +- Fix: コンフィグファイルに `meilisearch` の設定がある状態でほかの検索プロバイダを利用すると、UI上からリモートのノートの検索ができない問題を修正 ## 2026.3.2 diff --git a/package.json b/package.json index c0471700b6..1d372fb045 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2026.4.0-alpha.6", + "version": "2026.4.0-beta.0", "codename": "nasubi", "repository": { "type": "git", @@ -53,7 +53,7 @@ "cleanall": "pnpm clean-all" }, "dependencies": { - "cssnano": "7.1.4", + "cssnano": "7.1.5", "esbuild": "0.28.0", "execa": "9.6.1", "ignore-walk": "8.0.0", @@ -67,16 +67,16 @@ "@misskey-dev/eslint-plugin": "2.1.0", "@types/js-yaml": "4.0.9", "@types/node": "24.12.2", - "@typescript-eslint/eslint-plugin": "8.58.1", - "@typescript-eslint/parser": "8.58.1", - "@typescript/native-preview": "7.0.0-dev.20260116.1", + "@typescript-eslint/eslint-plugin": "8.58.2", + "@typescript-eslint/parser": "8.58.2", + "@typescript/native-preview": "7.0.0-dev.20260421.2", "cross-env": "10.1.0", "cypress": "15.13.1", "eslint": "9.39.4", - "globals": "17.4.0", + "globals": "17.5.0", "ncp": "2.0.0", "pnpm": "10.33.0", - "start-server-and-test": "2.1.5", + "start-server-and-test": "3.0.2", "typescript": "5.9.3" }, "optionalDependencies": { diff --git a/packages/backend/package.json b/packages/backend/package.json index b3fc45e32d..3908243ba9 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -53,28 +53,28 @@ "utf-8-validate": "6.0.6" }, "dependencies": { - "@aws-sdk/client-s3": "3.1027.0", - "@aws-sdk/lib-storage": "3.1027.0", + "@aws-sdk/client-s3": "3.1030.0", + "@aws-sdk/lib-storage": "3.1030.0", "@discordapp/twemoji": "16.0.1", "@fastify/accepts": "5.0.4", "@fastify/cors": "11.2.0", "@fastify/express": "4.0.5", "@fastify/http-proxy": "11.4.4", - "@fastify/multipart": "9.4.0", - "@fastify/static": "9.1.0", + "@fastify/multipart": "10.0.0", + "@fastify/static": "9.1.3", "@kitajs/html": "4.2.13", "@misskey-dev/sharp-read-bmp": "1.2.0", "@misskey-dev/summaly": "5.2.5", "@napi-rs/canvas": "0.1.97", - "@nestjs/common": "11.1.18", - "@nestjs/core": "11.1.18", - "@nestjs/testing": "11.1.18", - "@oxc-project/runtime": "0.124.0", + "@nestjs/common": "11.1.19", + "@nestjs/core": "11.1.19", + "@nestjs/testing": "11.1.19", + "@oxc-project/runtime": "0.125.0", "@peertube/http-signature": "1.7.0", - "@sentry/node": "10.47.0", - "@sentry/profiling-node": "10.47.0", + "@sentry/node": "10.48.0", + "@sentry/profiling-node": "10.48.0", "@simplewebauthn/server": "13.3.0", - "@sinonjs/fake-timers": "15.3.0", + "@sinonjs/fake-timers": "15.3.2", "@smithy/node-http-handler": "4.5.2", "@twemoji/parser": "16.0.0", "accepts": "1.3.8", @@ -84,7 +84,7 @@ "bcryptjs": "3.0.3", "blurhash": "2.0.5", "body-parser": "2.2.2", - "bullmq": "5.73.2", + "bullmq": "5.73.5", "cacheable-lookup": "7.0.0", "chalk": "5.6.2", "chalk-template": "1.1.2", @@ -96,7 +96,7 @@ "fastify": "5.8.5", "fastify-raw-body": "5.0.0", "feed": "5.2.0", - "file-type": "21.3.4", + "file-type": "22.0.1", "fluent-ffmpeg": "2.1.3", "form-data": "4.0.5", "got": "14.6.6", @@ -138,7 +138,7 @@ "rename": "1.0.4", "rss-parser": "3.13.0", "rxjs": "7.8.2", - "sanitize-html": "2.17.2", + "sanitize-html": "2.17.3", "secure-json-parse": "4.1.0", "semver": "7.7.4", "sharp": "0.33.5", @@ -158,9 +158,9 @@ }, "devDependencies": { "@kitajs/ts-html-plugin": "4.1.4", - "@nestjs/platform-express": "11.1.18", + "@nestjs/platform-express": "11.1.19", "@rollup/plugin-esm-shim": "0.1.8", - "@sentry/vue": "10.47.0", + "@sentry/vue": "10.48.0", "@simplewebauthn/types": "12.0.0", "@types/accepts": "1.3.7", "@types/archiver": "7.0.0", @@ -174,7 +174,7 @@ "@types/mime-types": "3.0.1", "@types/ms": "2.1.0", "@types/node": "24.12.2", - "@types/nodemailer": "7.0.11", + "@types/nodemailer": "8.0.0", "@types/oauth2orize": "1.11.5", "@types/oauth2orize-pkce": "0.1.2", "@types/pg": "8.20.0", @@ -192,8 +192,8 @@ "@types/vary": "1.1.3", "@types/web-push": "3.6.4", "@types/ws": "8.18.1", - "@typescript-eslint/eslint-plugin": "8.58.1", - "@typescript-eslint/parser": "8.58.1", + "@typescript-eslint/eslint-plugin": "8.58.2", + "@typescript-eslint/parser": "8.58.2", "@vitest/coverage-v8": "4.1.4", "aws-sdk-client-mock": "4.1.0", "cbor": "10.0.12", @@ -208,6 +208,6 @@ "supertest": "7.2.2", "vite": "8.0.8", "vitest": "4.1.4", - "vitest-mock-extended": "3.1.1" + "vitest-mock-extended": "4.0.0" } } diff --git a/packages/backend/rolldown.config.ts b/packages/backend/rolldown.config.ts index 87e6e9961b..950bc63560 100644 --- a/packages/backend/rolldown.config.ts +++ b/packages/backend/rolldown.config.ts @@ -74,6 +74,7 @@ export default defineConfig((args) => { 're2', 'ipaddr.js', 'oauth2orize', + 'file-type', ]; if (isE2E) { diff --git a/packages/backend/src/core/RoleService.ts b/packages/backend/src/core/RoleService.ts index 2ffee69c21..4515cfd29c 100644 --- a/packages/backend/src/core/RoleService.ts +++ b/packages/backend/src/core/RoleService.ts @@ -533,7 +533,8 @@ export class RoleService implements OnApplicationShutdown, OnModuleInit { roleId: In(administratorRoles.map(r => r.id)), }) : []; // TODO: isRootなアカウントも含める - return assigns.map(a => a.userId); + // Setを経由して重複を除去(ユーザIDは重複する可能性があるので) + return [...new Set(assigns.map(a => a.userId))].sort((x, y) => x.localeCompare(y)); } @bindThis diff --git a/packages/backend/src/core/SignupService.ts b/packages/backend/src/core/SignupService.ts index a85da62b86..bb1b8f9f3a 100644 --- a/packages/backend/src/core/SignupService.ts +++ b/packages/backend/src/core/SignupService.ts @@ -164,4 +164,3 @@ export class SignupService { return { account, secret }; } } - diff --git a/packages/backend/src/core/activitypub/ApInboxService.ts b/packages/backend/src/core/activitypub/ApInboxService.ts index 4f926b99d4..27ab0e3447 100644 --- a/packages/backend/src/core/activitypub/ApInboxService.ts +++ b/packages/backend/src/core/activitypub/ApInboxService.ts @@ -259,7 +259,7 @@ export class ApInboxService { @bindThis private async add(actor: MiRemoteUser, activity: IAdd, resolver?: Resolver): Promise { - if (actor.uri !== activity.actor) { + if (actor.uri !== getApId(activity.actor)) { return 'invalid actor'; } @@ -469,7 +469,7 @@ export class ApInboxService { @bindThis private async delete(actor: MiRemoteUser, activity: IDelete): Promise { - if (actor.uri !== activity.actor) { + if (actor.uri !== getApId(activity.actor)) { return 'invalid actor'; } @@ -623,7 +623,7 @@ export class ApInboxService { @bindThis private async remove(actor: MiRemoteUser, activity: IRemove, resolver?: Resolver): Promise { - if (actor.uri !== activity.actor) { + if (actor.uri !== getApId(activity.actor)) { return 'invalid actor'; } @@ -643,7 +643,7 @@ export class ApInboxService { @bindThis private async undo(actor: MiRemoteUser, activity: IUndo, resolver?: Resolver): Promise { - if (actor.uri !== activity.actor) { + if (actor.uri !== getApId(activity.actor)) { return 'invalid actor'; } @@ -777,7 +777,7 @@ export class ApInboxService { @bindThis private async update(actor: MiRemoteUser, activity: IUpdate, resolver?: Resolver): Promise { - if (actor.uri !== activity.actor) { + if (actor.uri !== getApId(activity.actor)) { return 'skip: invalid actor'; } diff --git a/packages/backend/src/core/entities/MetaEntityService.ts b/packages/backend/src/core/entities/MetaEntityService.ts index 8e56ddbc02..b9b656bd10 100644 --- a/packages/backend/src/core/entities/MetaEntityService.ts +++ b/packages/backend/src/core/entities/MetaEntityService.ts @@ -132,7 +132,7 @@ export class MetaEntityService { sentryForFrontend: this.config.sentryForFrontend ?? null, mediaProxy: this.config.mediaProxy, enableUrlPreview: instance.urlPreviewEnabled, - noteSearchableScope: (this.config.meilisearch == null || this.config.meilisearch.scope !== 'local') ? 'global' : 'local', + noteSearchableScope: (this.config.fulltextSearch?.provider === 'meilisearch' && this.config.meilisearch?.scope === 'local') ? 'local' : 'global', maxFileSize: this.config.maxFileSize, federation: this.meta.federation, }; diff --git a/packages/backend/src/queue/processors/InboxProcessorService.ts b/packages/backend/src/queue/processors/InboxProcessorService.ts index 73fad43eb4..78261a096a 100644 --- a/packages/backend/src/queue/processors/InboxProcessorService.ts +++ b/packages/backend/src/queue/processors/InboxProcessorService.ts @@ -115,9 +115,9 @@ export class InboxProcessorService implements OnApplicationShutdown { // 対象が4xxならスキップ if (err instanceof StatusError) { if (!err.isRetryable) { - throw new Bull.UnrecoverableError(`skip: Ignored deleted actors on both ends ${activity.actor} - ${err.statusCode}`); + throw new Bull.UnrecoverableError(`skip: Ignored deleted actors on both ends ${getApId(activity.actor)} - ${err.statusCode}`); } - throw new Error(`Error in actor ${activity.actor} - ${err.statusCode}`); + throw new Error(`Error in actor ${getApId(activity.actor)} - ${err.statusCode}`); } } } @@ -136,7 +136,7 @@ export class InboxProcessorService implements OnApplicationShutdown { const httpSignatureValidated = httpSignature.verifySignature(signature, authUser.key.keyPem); // また、signatureのsignerは、activity.actorと一致する必要がある - if (!httpSignatureValidated || authUser.user.uri !== activity.actor) { + if (!httpSignatureValidated || authUser.user.uri !== getApId(activity.actor)) { // 一致しなくても、でもLD-Signatureがありそうならそっちも見る const ldSignature = activity.signature; if (ldSignature) { @@ -187,8 +187,8 @@ export class InboxProcessorService implements OnApplicationShutdown { //#endregion // もう一度actorチェック - if (authUser.user.uri !== activity.actor) { - throw new Bull.UnrecoverableError(`skip: LD-Signature user(${authUser.user.uri}) !== activity.actor(${activity.actor})`); + if (authUser.user.uri !== getApId(activity.actor)) { + throw new Bull.UnrecoverableError(`skip: LD-Signature user(${authUser.user.uri}) !== activity.actor(${getApId(activity.actor)})`); } const ldHost = this.utilityService.extractDbHost(authUser.user.uri); @@ -243,17 +243,17 @@ export class InboxProcessorService implements OnApplicationShutdown { } } catch (e) { if (e instanceof IdentifiableError) { - if (e.id === '689ee33f-f97c-479a-ac49-1b9f8140af99') { - return 'blocked notes with prohibited words'; - } - if (e.id === '85ab9bd7-3a41-4530-959d-f07073900109') { - return 'actor has been suspended'; - } - if (e.id === 'd450b8a9-48e4-4dab-ae36-f4db763fda7c') { // invalid Note - return e.message; - } - if (e.id === '9f466dab-c856-48cd-9e65-ff90ff750580') { - return 'note contains too many mentions'; + switch (e.id) { + case '689ee33f-f97c-479a-ac49-1b9f8140af99': + return 'blocked notes with prohibited words'; + case '85ab9bd7-3a41-4530-959d-f07073900109': + return 'actor has been suspended'; + case 'd450b8a9-48e4-4dab-ae36-f4db763fda7c': // invalid Note + return e.message; + case '9f466dab-c856-48cd-9e65-ff90ff750580': + return 'note contains too many mentions'; + case '09d79f9e-64f1-4316-9cfa-e75c4d091574': // Instance is blocked + return 'skip: blocked instance'; } } throw e; diff --git a/packages/backend/test/unit/RoleService.ts b/packages/backend/test/unit/RoleService.ts index d599bb50ec..ec1e7ca134 100644 --- a/packages/backend/test/unit/RoleService.ts +++ b/packages/backend/test/unit/RoleService.ts @@ -696,6 +696,19 @@ describe('RoleService', () => { expect(adminIds).toHaveLength(0); }); + test('should not include duplicate user IDs if a user has multiple administrator roles', async () => { + const adminUser = await createUser(); + const adminRole1 = await createRole({ name: 'admin1', isAdministrator: true }); + const adminRole2 = await createRole({ name: 'admin2', isAdministrator: true }); + + await roleService.assign(adminUser.id, adminRole1.id); + await roleService.assign(adminUser.id, adminRole2.id); + + const adminIds = await roleService.getAdministratorIds(); + + expect(adminIds).toEqual([adminUser.id]); + }); + // TODO: rootユーザーは現在実装に含まれていないため、テストもそれに倣う test('should not include the root user', async () => { const rootUser = await createUser(); diff --git a/packages/frontend-builder/package.json b/packages/frontend-builder/package.json index 4ffeeda827..f19e901b34 100644 --- a/packages/frontend-builder/package.json +++ b/packages/frontend-builder/package.json @@ -12,8 +12,8 @@ "devDependencies": { "@types/estree": "1.0.8", "@types/node": "24.12.2", - "@typescript-eslint/eslint-plugin": "8.58.1", - "@typescript-eslint/parser": "8.58.1", + "@typescript-eslint/eslint-plugin": "8.58.2", + "@typescript-eslint/parser": "8.58.2", "rollup": "4.60.1" }, "dependencies": { diff --git a/packages/frontend-embed/package.json b/packages/frontend-embed/package.json index 4323175a13..154a71b92d 100644 --- a/packages/frontend-embed/package.json +++ b/packages/frontend-embed/package.json @@ -14,7 +14,7 @@ "@rollup/plugin-json": "6.1.0", "@rollup/pluginutils": "5.3.0", "@twemoji/parser": "16.0.0", - "@vitejs/plugin-vue": "6.0.5", + "@vitejs/plugin-vue": "6.0.6", "buraha": "0.0.1", "estree-walker": "3.0.3", "frontend-shared": "workspace:*", @@ -40,21 +40,21 @@ "@types/punycode.js": "npm:@types/punycode@2.1.4", "@types/tinycolor2": "1.4.6", "@types/ws": "8.18.1", - "@typescript-eslint/eslint-plugin": "8.58.1", - "@typescript-eslint/parser": "8.58.1", - "@vitest/coverage-v8": "4.1.3", + "@typescript-eslint/eslint-plugin": "8.58.2", + "@typescript-eslint/parser": "8.58.2", + "@vitest/coverage-v8": "4.1.4", "@vue/runtime-core": "3.5.32", "acorn": "8.16.0", "cross-env": "10.1.0", "eslint-plugin-import": "2.32.0", "eslint-plugin-vue": "10.8.0", - "happy-dom": "20.8.9", + "happy-dom": "20.9.0", "intersection-observer": "0.12.2", "micromatch": "4.0.8", - "msw": "2.13.2", - "prettier": "3.8.1", + "msw": "2.13.3", + "prettier": "3.8.3", "sass-embedded": "1.99.0", - "start-server-and-test": "2.1.5", + "start-server-and-test": "3.0.2", "tsx": "4.21.0", "vite": "8.0.8", "vite-plugin-turbosnap": "1.0.3", diff --git a/packages/frontend-shared/package.json b/packages/frontend-shared/package.json index 3bbdb6ab91..60c630af14 100644 --- a/packages/frontend-shared/package.json +++ b/packages/frontend-shared/package.json @@ -22,8 +22,8 @@ }, "devDependencies": { "@types/node": "24.12.2", - "@typescript-eslint/eslint-plugin": "8.58.1", - "@typescript-eslint/parser": "8.58.1", + "@typescript-eslint/eslint-plugin": "8.58.2", + "@typescript-eslint/parser": "8.58.2", "esbuild": "0.28.0", "eslint-plugin-vue": "10.8.0", "nodemon": "3.1.14", diff --git a/packages/frontend-shared/tsconfig.json b/packages/frontend-shared/tsconfig.json index 6b1804a0fc..c2600dc45e 100644 --- a/packages/frontend-shared/tsconfig.json +++ b/packages/frontend-shared/tsconfig.json @@ -7,7 +7,7 @@ "declaration": true, "declarationMap": true, "sourceMap": false, - "outDir": "./js-built/", + "outDir": "./js-built", "removeComments": true, "resolveJsonModule": true, "strict": true, @@ -17,6 +17,7 @@ "noImplicitReturns": true, "esModuleInterop": true, "verbatimModuleSyntax": true, + "rootDir": "./js", "paths": { "@/*": ["./*"], "@@/*": ["./*"] diff --git a/packages/frontend/package.json b/packages/frontend/package.json index 6ea85ff6ce..5e44171b4c 100644 --- a/packages/frontend/package.json +++ b/packages/frontend/package.json @@ -25,11 +25,11 @@ "@github/webauthn-json": "2.1.1", "@mcaptcha/core-glue": "0.1.0-alpha-5", "@misskey-dev/browser-image-resizer": "2024.1.0", - "@sentry/vue": "10.47.0", + "@sentry/vue": "10.48.0", "@syuilo/aiscript": "1.2.1", "@syuilo/aiscript-0-19-0": "npm:@syuilo/aiscript@^0.19.0", "@twemoji/parser": "16.0.0", - "@vitejs/plugin-vue": "6.0.5", + "@vitejs/plugin-vue": "6.0.6", "aiscript-vscode": "github:aiscript-dev/aiscript-vscode#v0.1.16", "analytics": "0.8.19", "broadcast-channel": "7.3.0", @@ -46,7 +46,7 @@ "date-fns": "4.1.0", "eventemitter3": "5.0.4", "execa": "9.6.1", - "exifreader": "4.38.0", + "exifreader": "4.38.1", "frontend-shared": "workspace:*", "i18n": "workspace:*", "icons-subsetter": "workspace:*", @@ -110,9 +110,9 @@ "@types/textarea-caret": "3.0.4", "@types/throttle-debounce": "5.0.2", "@types/tinycolor2": "1.4.6", - "@typescript-eslint/eslint-plugin": "8.58.1", - "@typescript-eslint/parser": "8.58.1", - "@vitest/coverage-v8": "4.1.3", + "@typescript-eslint/eslint-plugin": "8.58.2", + "@typescript-eslint/parser": "8.58.2", + "@vitest/coverage-v8": "4.1.4", "@vue/compiler-core": "3.5.32", "acorn": "8.16.0", "astring": "1.9.0", @@ -121,27 +121,27 @@ "eslint-plugin-import": "2.32.0", "eslint-plugin-vue": "10.8.0", "estree-walker": "3.0.3", - "happy-dom": "20.8.9", + "happy-dom": "20.9.0", "intersection-observer": "0.12.2", "micromatch": "4.0.8", "minimatch": "10.2.5", - "msw": "2.13.2", + "msw": "2.13.3", "msw-storybook-addon": "2.0.7", "nodemon": "3.1.14", - "prettier": "3.8.1", + "prettier": "3.8.3", "react": "19.2.5", "react-dom": "19.2.5", "rolldown": "1.0.0-rc.15", "sass-embedded": "1.99.0", "seedrandom": "3.0.5", - "start-server-and-test": "2.1.5", + "start-server-and-test": "3.0.2", "storybook": "10.3.5", "storybook-addon-misskey-theme": "github:misskey-dev/storybook-addon-misskey-theme", "tsx": "4.21.0", "vite": "8.0.8", "vite-plugin-glsl": "1.6.0", "vite-plugin-turbosnap": "1.0.3", - "vitest": "4.1.3", + "vitest": "4.1.4", "vitest-fetch-mock": "0.4.5", "vue-component-type-helpers": "3.2.6", "vue-eslint-parser": "10.4.0", diff --git a/packages/frontend/src/components/MkMediaAudio.vue b/packages/frontend/src/components/MkMediaAudio.vue index efcbf26a29..c178b923ae 100644 --- a/packages/frontend/src/components/MkMediaAudio.vue +++ b/packages/frontend/src/components/MkMediaAudio.vue @@ -182,15 +182,28 @@ function showMenu(ev: MouseEvent) { text: i18n.ts._mediaControls.playbackRate, icon: 'ti ti-clock-play', ref: speed, - options: { - '0.25x': 0.25, - '0.5x': 0.5, - '0.75x': 0.75, - '1.0x': 1, - '1.25x': 1.25, - '1.5x': 1.5, - '2.0x': 2, - }, + options: [{ + label: '0.25x', + value: 0.25, + }, { + label: '0.5x', + value: 0.5, + }, { + label: '0.75x', + value: 0.75, + }, { + label: '1.0x', + value: 1, + }, { + label: '1.25x', + value: 1.25, + }, { + label: '1.5x', + value: 1.5, + }, { + label: '2.0x', + value: 2, + }], }, { type: 'divider', diff --git a/packages/frontend/src/components/MkMediaVideo.vue b/packages/frontend/src/components/MkMediaVideo.vue index 4d06e42c05..be3b3ad396 100644 --- a/packages/frontend/src/components/MkMediaVideo.vue +++ b/packages/frontend/src/components/MkMediaVideo.vue @@ -204,15 +204,28 @@ function showMenu(ev: PointerEvent) { text: i18n.ts._mediaControls.playbackRate, icon: 'ti ti-clock-play', ref: speed, - options: { - '0.25x': 0.25, - '0.5x': 0.5, - '0.75x': 0.75, - '1.0x': 1, - '1.25x': 1.25, - '1.5x': 1.5, - '2.0x': 2, - }, + options: [{ + label: '0.25x', + value: 0.25, + }, { + label: '0.5x', + value: 0.5, + }, { + label: '0.75x', + value: 0.75, + }, { + label: '1.0x', + value: 1, + }, { + label: '1.25x', + value: 1.25, + }, { + label: '1.5x', + value: 1.5, + }, { + label: '2.0x', + value: 2, + }], }, ...(window.document.pictureInPictureEnabled ? [{ text: i18n.ts._mediaControls.pip, diff --git a/packages/frontend/src/components/MkMenu.vue b/packages/frontend/src/components/MkMenu.vue index 26db8e1b5b..bbe0c1c4df 100644 --- a/packages/frontend/src/components/MkMenu.vue +++ b/packages/frontend/src/components/MkMenu.vue @@ -132,6 +132,7 @@ SPDX-License-Identifier: AGPL-3.0-only :class="['_button', $style.item, $style.parent, { [$style.active]: childShowingItem === item }]" :disabled="unref(item.disabled)" @mouseenter.prevent="preferClick ? null : showRadioOptions(item, $event)" + @mousemove="parentMouseMove" @keydown.enter.prevent="preferClick ? null : showRadioOptions(item, $event)" @click.prevent="!preferClick ? null : showRadioOptions(item, $event)" > @@ -232,7 +233,7 @@ SPDX-License-Identifier: AGPL-3.0-only