diff --git a/.github/workflows/release-edit-with-push.yml b/.github/workflows/release-edit-with-push.yml
index 86ee0b3fb5..57657a4ba7 100644
--- a/.github/workflows/release-edit-with-push.yml
+++ b/.github/workflows/release-edit-with-push.yml
@@ -3,7 +3,7 @@ name: "Release Manager: sync changelog with PR"
on:
push:
branches:
- - release/**
+ - develop
paths:
- 'CHANGELOG.md'
@@ -20,24 +20,29 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- # headがrelease/かつopenのPRを1つ取得
+ # headが$GITHUB_REF_NAME, baseが$STABLE_BRANCHかつopenのPRを1つ取得
- name: Get PR
run: |
- echo "pr_number=$(gh pr list --limit 1 --head "$GITHUB_REF_NAME" --json number --jq '.[] | .number')" >> $GITHUB_OUTPUT
+ echo "pr_number=$(gh pr list --limit 1 --search "head:$GITHUB_REF_NAME base:$STABLE_BRANCH is:open" --json number --jq '.[] | .number')" >> $GITHUB_OUTPUT
id: get_pr
+ env:
+ STABLE_BRANCH: ${{ vars.STABLE_BRANCH }}
- name: Get target version
- uses: misskey-dev/release-manager-actions/.github/actions/get-target-version@v1
+ if: steps.get_pr.outputs.pr_number != ''
+ uses: misskey-dev/release-manager-actions/.github/actions/get-target-version@v2
id: v
# CHANGELOG.mdの内容を取得
- name: Get changelog
- uses: misskey-dev/release-manager-actions/.github/actions/get-changelog@v1
+ if: steps.get_pr.outputs.pr_number != ''
+ uses: misskey-dev/release-manager-actions/.github/actions/get-changelog@v2
with:
version: ${{ steps.v.outputs.target_version }}
id: changelog
# PRのnotesを更新
- name: Update PR
+ if: steps.get_pr.outputs.pr_number != ''
run: |
gh pr edit "$PR_NUMBER" --body "$CHANGELOG"
env:
- CHANGELOG: ${{ steps.changelog.outputs.changelog }}
PR_NUMBER: ${{ steps.get_pr.outputs.pr_number }}
+ CHANGELOG: ${{ steps.changelog.outputs.changelog }}
diff --git a/.github/workflows/release-with-dispatch.yml b/.github/workflows/release-with-dispatch.yml
index bc6448cb37..0936bc0ae8 100644
--- a/.github/workflows/release-with-dispatch.yml
+++ b/.github/workflows/release-with-dispatch.yml
@@ -33,18 +33,21 @@ jobs:
pr_number: ${{ steps.get_pr.outputs.pr_number }}
steps:
- uses: actions/checkout@v4
- # headがrelease/かつopenのPRを1つ取得
+ # headが$GITHUB_REF_NAME, baseが$STABLE_BRANCHかつopenのPRを1つ取得
- name: Get PRs
run: |
- echo "pr_number=$(gh pr list --limit 1 --search "head:release/ is:open" --json number --jq '.[] | .number')" >> $GITHUB_OUTPUT
+ echo "pr_number=$(gh pr list --limit 1 --search "head:$GITHUB_REF_NAME base:$STABLE_BRANCH is:open" --json number --jq '.[] | .number')" >> $GITHUB_OUTPUT
id: get_pr
+ env:
+ STABLE_BRANCH: ${{ vars.STABLE_BRANCH }}
merge:
- uses: misskey-dev/release-manager-actions/.github/workflows/merge.yml@v1
+ uses: misskey-dev/release-manager-actions/.github/workflows/merge.yml@v2
needs: get-pr
if: ${{ needs.get-pr.outputs.pr_number != '' && inputs.merge == true }}
with:
pr_number: ${{ needs.get-pr.outputs.pr_number }}
+ user: 'github-actions[bot]'
package_jsons_to_rewrite: ${{ vars.PACKAGE_JSONS_TO_REWRITE }}
# Text to prepend to the changelog
# The first line must be `## Unreleased`
@@ -65,15 +68,14 @@ jobs:
secrets:
RELEASE_APP_ID: ${{ secrets.RELEASE_APP_ID }}
RELEASE_APP_PRIVATE_KEY: ${{ secrets.RELEASE_APP_PRIVATE_KEY }}
- RULESET_EDIT_APP_ID: ${{ secrets.RULESET_EDIT_APP_ID }}
- RULESET_EDIT_APP_PRIVATE_KEY: ${{ secrets.RULESET_EDIT_APP_PRIVATE_KEY }}
create-prerelease:
- uses: misskey-dev/release-manager-actions/.github/workflows/create-prerelease.yml@v1
+ uses: misskey-dev/release-manager-actions/.github/workflows/create-prerelease.yml@v2
needs: get-pr
if: ${{ needs.get-pr.outputs.pr_number != '' && inputs.merge != true }}
with:
pr_number: ${{ needs.get-pr.outputs.pr_number }}
+ user: 'github-actions[bot]'
package_jsons_to_rewrite: ${{ vars.PACKAGE_JSONS_TO_REWRITE }}
use_external_app_to_release: ${{ vars.USE_RELEASE_APP == 'true' }}
indent: ${{ vars.INDENT }}
@@ -82,10 +84,11 @@ jobs:
RELEASE_APP_PRIVATE_KEY: ${{ secrets.RELEASE_APP_PRIVATE_KEY }}
create-target:
- uses: misskey-dev/release-manager-actions/.github/workflows/create-target.yml@v1
+ uses: misskey-dev/release-manager-actions/.github/workflows/create-target.yml@v2
needs: get-pr
if: ${{ needs.get-pr.outputs.pr_number == '' }}
with:
+ user: 'github-actions[bot]'
# The script for version increment.
# process.env.CURRENT_VERSION: The current version.
#
@@ -118,8 +121,7 @@ jobs:
package_jsons_to_rewrite: ${{ vars.PACKAGE_JSONS_TO_REWRITE }}
use_external_app_to_release: ${{ vars.USE_RELEASE_APP == 'true' }}
indent: ${{ vars.INDENT }}
+ stable_branch: ${{ vars.STABLE_BRANCH }}
secrets:
RELEASE_APP_ID: ${{ secrets.RELEASE_APP_ID }}
RELEASE_APP_PRIVATE_KEY: ${{ secrets.RELEASE_APP_PRIVATE_KEY }}
- RULESET_EDIT_APP_ID: ${{ secrets.RULESET_EDIT_APP_ID }}
- RULESET_EDIT_APP_PRIVATE_KEY: ${{ secrets.RULESET_EDIT_APP_PRIVATE_KEY }}
diff --git a/.github/workflows/release-with-ready.yml b/.github/workflows/release-with-ready.yml
index a0fad0e336..79b6ade012 100644
--- a/.github/workflows/release-with-ready.yml
+++ b/.github/workflows/release-with-ready.yml
@@ -16,23 +16,26 @@ jobs:
check:
runs-on: ubuntu-latest
outputs:
- ref: ${{ steps.get_pr.outputs.ref }}
+ head: ${{ steps.get_pr.outputs.head }}
+ base: ${{ steps.get_pr.outputs.base }}
steps:
- uses: actions/checkout@v4
# PR情報を取得
- name: Get PR
run: |
- pr_json=$(gh pr view "$PR_NUMBER" --json isDraft,headRefName)
- echo "ref=$(echo $pr_json | jq -r '.headRefName')" >> $GITHUB_OUTPUT
+ pr_json=$(gh pr view "$PR_NUMBER" --json isDraft,headRefName,baseRefName)
+ echo "head=$(echo $pr_json | jq -r '.headRefName')" >> $GITHUB_OUTPUT
+ echo "base=$(echo $pr_json | jq -r '.baseRefName')" >> $GITHUB_OUTPUT
id: get_pr
env:
PR_NUMBER: ${{ github.event.pull_request.number }}
release:
- uses: misskey-dev/release-manager-actions/.github/workflows/create-prerelease.yml@v1
+ uses: misskey-dev/release-manager-actions/.github/workflows/create-prerelease.yml@v2
needs: check
- if: startsWith(needs.check.outputs.ref, 'release/')
+ if: needs.check.outputs.head == github.event.repository.default_branch && needs.check.outputs.base == vars.STABLE_BRANCH
with:
pr_number: ${{ github.event.pull_request.number }}
+ user: 'github-actions[bot]'
package_jsons_to_rewrite: ${{ vars.PACKAGE_JSONS_TO_REWRITE }}
use_external_app_to_release: ${{ vars.USE_RELEASE_APP == 'true' }}
indent: ${{ vars.INDENT }}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6686a30947..01ceb8634a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,12 +8,15 @@
### Client
- Feat: ノート単体・ユーザーのノート・クリップのノートの埋め込み機能
- 埋め込みコードやウェブサイトへの実装方法の詳細はMisskey Hubに掲載予定です
+- Enhance: 内蔵APIドキュメントのデザイン・パフォーマンスを改善
- Fix: `/about#federation` ページなどで各インスタンスのチャートが表示されなくなっていた問題を修正
- Fix: ユーザーページの追加情報のラベルを投稿者のサーバーの絵文字で表示する (#13968)
- Fix: リバーシの対局を正しく共有できないことがある問題を修正
- Fix: コントロールパネルでベースロールのポリシーを編集してもUI上では変更が反映されない問題を修正
- Fix: アンテナの編集画面のボタンに隙間を追加
- Fix: テーマプレビューが見れない問題を修正
+- Fix: ショートカットキーが連打できる問題を修正
+ (Cherry-picked from https://github.com/taiyme/misskey/pull/234)
### Server
- Feat: レートリミット制限に引っかかったときに`Retry-After`ヘッダーを返すように (#13949)
@@ -36,6 +39,7 @@
### Misskey.js
- Feat: `/drive/files/create` のリクエストに対応(`multipart/form-data`に対応)
+- Feat: `/admin/role/create` のロールポリシーの型を修正
## 2024.5.0
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 06c2d2f21d..b718f3703f 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -165,7 +165,7 @@ cp .github/misskey/test.yml .config/
```
Prepare DB/Redis for testing.
```
-docker compose -f packages/backend/test/compose.yaml up
+docker compose -f packages/backend/test/compose.yml up
```
Alternatively, prepare an empty (data can be erased) DB and edit `.config/test.yml`.
diff --git a/packages/backend/assets/api-doc.html b/packages/backend/assets/api-doc.html
new file mode 100644
index 0000000000..19e0349d47
--- /dev/null
+++ b/packages/backend/assets/api-doc.html
@@ -0,0 +1,20 @@
+
+
+
+ Misskey API
+
+
+
+
+
+
+
+
+
diff --git a/packages/backend/assets/redoc.html b/packages/backend/assets/redoc.html
deleted file mode 100644
index 2557b4532e..0000000000
--- a/packages/backend/assets/redoc.html
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
- Misskey API
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/packages/backend/src/server/api/openapi/OpenApiServerService.ts b/packages/backend/src/server/api/openapi/OpenApiServerService.ts
index 5210e4d2bc..f124aa9f39 100644
--- a/packages/backend/src/server/api/openapi/OpenApiServerService.ts
+++ b/packages/backend/src/server/api/openapi/OpenApiServerService.ts
@@ -25,7 +25,7 @@ export class OpenApiServerService {
public createServer(fastify: FastifyInstance, _options: FastifyPluginOptions, done: (err?: Error) => void) {
fastify.get('/api-doc', async (_request, reply) => {
reply.header('Cache-Control', 'public, max-age=86400');
- return await reply.sendFile('/redoc.html', staticAssets);
+ return await reply.sendFile('/api-doc.html', staticAssets);
});
fastify.get('/api.json', (_request, reply) => {
reply.header('Cache-Control', 'public, max-age=600');
diff --git a/packages/backend/src/server/api/openapi/gen-spec.ts b/packages/backend/src/server/api/openapi/gen-spec.ts
index 2a14270a24..efa47a6986 100644
--- a/packages/backend/src/server/api/openapi/gen-spec.ts
+++ b/packages/backend/src/server/api/openapi/gen-spec.ts
@@ -15,7 +15,6 @@ export function genOpenapiSpec(config: Config, includeSelfRef = false) {
info: {
version: config.version,
title: 'Misskey API',
- 'x-logo': { url: '/static-assets/api-doc.png' },
},
externalDocs: {
diff --git a/packages/frontend/src/boot/main-boot.ts b/packages/frontend/src/boot/main-boot.ts
index faf230a1a2..d327016317 100644
--- a/packages/frontend/src/boot/main-boot.ts
+++ b/packages/frontend/src/boot/main-boot.ts
@@ -13,7 +13,6 @@ import * as sound from '@/scripts/sound.js';
import { $i, signout, updateAccount } from '@/account.js';
import { instance } from '@/instance.js';
import { ColdDeviceStorage, defaultStore } from '@/store.js';
-import { makeHotkey } from '@/scripts/hotkey.js';
import { reactionPicker } from '@/scripts/reaction-picker.js';
import { miLocalStorage } from '@/local-storage.js';
import { claimAchievement, claimedAchievements } from '@/scripts/achievements.js';
@@ -21,6 +20,7 @@ import { initializeSw } from '@/scripts/initialize-sw.js';
import { deckStore } from '@/ui/deck/deck-store.js';
import { emojiPicker } from '@/scripts/emoji-picker.js';
import { mainRouter } from '@/router/main.js';
+import { type Keymap, makeHotkey } from '@/scripts/hotkey.js';
export async function mainBoot() {
const { isClientUpdated } = await common(() => createApp(
@@ -69,14 +69,6 @@ export async function mainBoot() {
});
}
- const hotkeys = {
- 'd': (): void => {
- defaultStore.set('darkMode', !defaultStore.state.darkMode);
- },
- 's': (): void => {
- mainRouter.push('/search');
- },
- };
try {
if (defaultStore.state.enableSeasonalScreenEffect) {
const month = new Date().getMonth() + 1;
@@ -105,9 +97,6 @@ export async function mainBoot() {
}
if ($i) {
- // only add post shortcuts if logged in
- hotkeys['p|n'] = post;
-
defaultStore.loaded.then(() => {
if (defaultStore.state.accountSetupWizard !== -1) {
const { dispose } = popup(defineAsyncComponent(() => import('@/components/MkUserSetupDialog.vue')), {}, {
@@ -334,7 +323,19 @@ export async function mainBoot() {
}
// shortcut
- document.addEventListener('keydown', makeHotkey(hotkeys));
+ const keymap = {
+ 'p|n': () => {
+ if ($i == null) return;
+ post();
+ },
+ 'd': () => {
+ defaultStore.set('darkMode', !defaultStore.state.darkMode);
+ },
+ 's': () => {
+ mainRouter.push('/search');
+ },
+ } as const satisfies Keymap;
+ document.addEventListener('keydown', makeHotkey(keymap), { passive: false });
initializeSw();
}
diff --git a/packages/frontend/src/components/MkMediaAudio.vue b/packages/frontend/src/components/MkMediaAudio.vue
index 0f61450a13..366d6f7b81 100644
--- a/packages/frontend/src/components/MkMediaAudio.vue
+++ b/packages/frontend/src/components/MkMediaAudio.vue
@@ -82,6 +82,7 @@ import type { MenuItem } from '@/types/menu.js';
import { defaultStore } from '@/store.js';
import { i18n } from '@/i18n.js';
import * as os from '@/os.js';
+import { type Keymap } from '@/scripts/hotkey.js';
import bytes from '@/filters/bytes.js';
import { hms } from '@/filters/hms.js';
import MkMediaRange from '@/components/MkMediaRange.vue';
@@ -94,29 +95,41 @@ const props = defineProps<{
const inEmbedPage = inject('EMBED_PAGE', false);
const keymap = {
- 'up': () => {
- if (inEmbedPage) return;
- if (hasFocus() && audioEl.value) {
- volume.value = Math.min(volume.value + 0.1, 1);
- }
+ 'up': {
+ allowRepeat: true,
+ callback: () => {
+ if (inEmbedPage) return;
+ if (hasFocus() && audioEl.value) {
+ volume.value = Math.min(volume.value + 0.1, 1);
+ }
+ },
},
- 'down': () => {
- if (inEmbedPage) return;
- if (hasFocus() && audioEl.value) {
- volume.value = Math.max(volume.value - 0.1, 0);
- }
+ 'down': {
+ allowRepeat: true,
+ callback: () => {
+ if (inEmbedPage) return;
+ if (hasFocus() && audioEl.value) {
+ volume.value = Math.max(volume.value - 0.1, 0);
+ }
+ },
},
- 'left': () => {
- if (inEmbedPage) return;
- if (hasFocus() && audioEl.value) {
- audioEl.value.currentTime = Math.max(audioEl.value.currentTime - 5, 0);
- }
+ 'left': {
+ allowRepeat: true,
+ callback: () => {
+ if (inEmbedPage) return;
+ if (hasFocus() && audioEl.value) {
+ audioEl.value.currentTime = Math.max(audioEl.value.currentTime - 5, 0);
+ }
+ },
},
- 'right': () => {
- if (inEmbedPage) return;
- if (hasFocus() && audioEl.value) {
- audioEl.value.currentTime = Math.min(audioEl.value.currentTime + 5, audioEl.value.duration);
- }
+ 'right': {
+ allowRepeat: true,
+ callback: () => {
+ if (inEmbedPage) return;
+ if (hasFocus() && audioEl.value) {
+ audioEl.value.currentTime = Math.min(audioEl.value.currentTime + 5, audioEl.value.duration);
+ }
+ },
},
'space': () => {
if (inEmbedPage) return;
@@ -124,7 +137,7 @@ const keymap = {
togglePlayPause();
}
},
-};
+} as const satisfies Keymap;
// PlayerElもしくはその子要素にフォーカスがあるかどうか
function hasFocus() {
diff --git a/packages/frontend/src/components/MkMediaVideo.vue b/packages/frontend/src/components/MkMediaVideo.vue
index 5f502f450f..7bf1bd596b 100644
--- a/packages/frontend/src/components/MkMediaVideo.vue
+++ b/packages/frontend/src/components/MkMediaVideo.vue
@@ -113,6 +113,7 @@ SPDX-License-Identifier: AGPL-3.0-only
import { ref, shallowRef, computed, watch, inject, onDeactivated, onActivated, onMounted } from 'vue';
import * as Misskey from 'misskey-js';
import type { MenuItem } from '@/types/menu.js';
+import { type Keymap } from '@/scripts/hotkey.js';
import bytes from '@/filters/bytes.js';
import { hms } from '@/filters/hms.js';
import { defaultStore } from '@/store.js';
@@ -130,29 +131,41 @@ const props = defineProps<{
const inEmbedPage = inject('EMBED_PAGE', false);
const keymap = {
- 'up': () => {
- if (inEmbedPage) return;
- if (hasFocus() && videoEl.value) {
- volume.value = Math.min(volume.value + 0.1, 1);
- }
+ 'up': {
+ allowRepeat: true,
+ callback: () => {
+ if (inEmbedPage) return;
+ if (hasFocus() && videoEl.value) {
+ volume.value = Math.min(volume.value + 0.1, 1);
+ }
+ },
},
- 'down': () => {
- if (inEmbedPage) return;
- if (hasFocus() && videoEl.value) {
- volume.value = Math.max(volume.value - 0.1, 0);
- }
+ 'down': {
+ allowRepeat: true,
+ callback: () => {
+ if (inEmbedPage) return;
+ if (hasFocus() && videoEl.value) {
+ volume.value = Math.max(volume.value - 0.1, 0);
+ }
+ },
},
- 'left': () => {
- if (inEmbedPage) return;
- if (hasFocus() && videoEl.value) {
- videoEl.value.currentTime = Math.max(videoEl.value.currentTime - 5, 0);
- }
+ 'left': {
+ allowRepeat: true,
+ callback: () => {
+ if (inEmbedPage) return;
+ if (hasFocus() && videoEl.value) {
+ videoEl.value.currentTime = Math.max(videoEl.value.currentTime - 5, 0);
+ }
+ },
},
- 'right': () => {
- if (inEmbedPage) return;
- if (hasFocus() && videoEl.value) {
- videoEl.value.currentTime = Math.min(videoEl.value.currentTime + 5, videoEl.value.duration);
- }
+ 'right': {
+ allowRepeat: true,
+ callback: () => {
+ if (inEmbedPage) return;
+ if (hasFocus() && videoEl.value) {
+ videoEl.value.currentTime = Math.min(videoEl.value.currentTime + 5, videoEl.value.duration);
+ }
+ },
},
'space': () => {
if (inEmbedPage) return;
@@ -160,7 +173,7 @@ const keymap = {
togglePlayPause();
}
},
-};
+} as const satisfies Keymap;
// PlayerElもしくはその子要素にフォーカスがあるかどうか
function hasFocus() {
diff --git a/packages/frontend/src/components/MkMenu.vue b/packages/frontend/src/components/MkMenu.vue
index d91239b9e2..119504f744 100644
--- a/packages/frontend/src/components/MkMenu.vue
+++ b/packages/frontend/src/components/MkMenu.vue
@@ -98,6 +98,7 @@ import { MenuItem, InnerMenuItem, MenuPending, MenuAction, MenuSwitch, MenuRadio
import * as os from '@/os.js';
import { i18n } from '@/i18n.js';
import { isTouchUsing } from '@/scripts/touch.js';
+import { type Keymap } from '@/scripts/hotkey.js';
const childrenCache = new WeakMap();
@@ -125,11 +126,20 @@ const items2 = ref();
const child = shallowRef>();
-const keymap = computed(() => ({
- 'up|k|shift+tab': focusUp,
- 'down|j|tab': focusDown,
- 'esc': close,
-}));
+const keymap = {
+ 'up|k|shift+tab': {
+ allowRepeat: true,
+ callback: () => focusUp(),
+ },
+ 'down|j|tab': {
+ allowRepeat: true,
+ callback: () => focusDown(),
+ },
+ 'esc': {
+ allowRepeat: true,
+ callback: () => close(false),
+ },
+} as const satisfies Keymap;
const childShowingItem = ref