Compare commits

..

14 Commits

Author SHA1 Message Date
renovate[bot]
e3eb1d011f chore(deps): update node.js to v22.17.0 2025-06-25 02:12:22 +00:00
syuilo
06d31c0b78 fix(frontend): ファイルがドライブの既定アップロード先に指定したフォルダにアップロードされない問題を修正
Fix #16206
2025-06-25 10:55:30 +09:00
syuilo
32d4c312ef enhance(frontend): ファイルアップロード前にキャプション設定を行えるように
Resolve #16210
2025-06-25 10:49:58 +09:00
syuilo
36fde67992 enhance(frontend): improve theme settings 2025-06-25 10:23:39 +09:00
syuilo
43abbce2af enhance(frontend): 全てのページネーションにおいてコンテキストメニューからリロードできるように 2025-06-25 10:08:44 +09:00
syuilo
684424f26a enhance(frontend): improve useScrollPositionKeeper 2025-06-24 20:30:32 +09:00
syuilo
36989e0cd3 Update about-misskey.vue 2025-06-24 20:24:34 +09:00
syuilo
d518682e73 add note 2025-06-24 11:44:16 +09:00
syuilo
0ada970337 enhance(frontend): 設定の自動バックアップをオンにした直後に自動バックアップするように 2025-06-23 17:12:25 +09:00
github-actions[bot]
a812dfe853 [skip ci] Update CHANGELOG.md (prepend template) 2025-06-16 11:13:27 +00:00
github-actions[bot]
2baec208f5 Release: 2025.6.3 2025-06-16 11:13:22 +00:00
github-actions[bot]
4093616e23 Bump version to 2025.6.3-alpha.0 2025-06-16 10:52:09 +00:00
syuilo
062d5170df fix(frontend): キャッシュを削除しないとクライアントが使用できないことがある問題を修正
Fix #16196
2025-06-16 19:51:26 +09:00
github-actions[bot]
a279bd4d49 [skip ci] Update CHANGELOG.md (prepend template) 2025-06-16 08:58:37 +00:00
16 changed files with 109 additions and 18 deletions

View File

@@ -5,7 +5,7 @@
"workspaceFolder": "/workspace",
"features": {
"ghcr.io/devcontainers/features/node:1": {
"version": "22.15.0"
"version": "22.17.0"
},
"ghcr.io/devcontainers-extra/features/pnpm:2": {
"version": "10.10.0"

View File

@@ -1,3 +1,22 @@
## Unreleased
### General
-
### Client
- Enhance: 設定の自動バックアップをオンにした直後に自動バックアップするように
- Enhance: ファイルアップロード前にキャプション設定を行えるように
- Fix: ファイルがドライブの既定アップロード先に指定したフォルダにアップロードされない問題を修正
### Server
-
## 2025.6.3
### Client
- Fix: キャッシュを削除しないとクライアントが使用できないことがある問題を修正
## 2025.6.2
### Client

4
locales/index.d.ts vendored
View File

@@ -8366,6 +8366,10 @@ export interface Locale extends ILocale {
* テーマコード
*/
"code": string;
/**
* テーマコードをコピー
*/
"copyThemeCode": string;
/**
* 説明
*/

View File

@@ -2193,6 +2193,7 @@ _theme:
install: "テーマのインストール"
manage: "テーマの管理"
code: "テーマコード"
copyThemeCode: "テーマコードをコピー"
description: "説明"
installed: "{name}をインストールしました"
installedThemes: "インストールされたテーマ"

View File

@@ -1,6 +1,6 @@
{
"name": "misskey",
"version": "2025.6.2",
"version": "2025.6.3",
"codename": "nasubi",
"repository": {
"type": "git",

View File

@@ -72,7 +72,7 @@ export class I18n<T extends ILocale> {
console.error(`Unexpected locale key: ${String(p)}`);
return p;
return new Proxy({} as any, new Handler<TTarget[keyof TTarget] & ILocale>());
}
}
@@ -141,7 +141,7 @@ export class I18n<T extends ILocale> {
console.error(`Unexpected locale key: ${String(p)}`);
return p;
return new Proxy((() => p) as any, new Handler<TTarget[keyof TTarget] & ILocale>());
}
}

View File

@@ -16,7 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only
>
<template #header>{{ i18n.ts.describeFile }}</template>
<div class="_spacer" style="--MI_SPACER-min: 20px; --MI_SPACER-max: 28px;">
<MkDriveFileThumbnail :file="file" fit="contain" style="height: 100px; margin-bottom: 16px;"/>
<MkDriveFileThumbnail v-if="file" :file="file" fit="contain" style="height: 100px; margin-bottom: 16px;"/>
<MkTextarea v-model="caption" autofocus :placeholder="i18n.ts.inputNewDescription">
<template #label>{{ i18n.ts.caption }}</template>
</MkTextarea>
@@ -33,8 +33,8 @@ import MkDriveFileThumbnail from '@/components/MkDriveFileThumbnail.vue';
import { i18n } from '@/i18n.js';
const props = defineProps<{
file: Misskey.entities.DriveFile;
default: string;
file?: Misskey.entities.DriveFile | null;
default?: string | null;
}>();
const emit = defineEmits<{
@@ -44,7 +44,7 @@ const emit = defineEmits<{
const dialog = useTemplateRef('dialog');
const caption = ref(props.default);
const caption = ref(props.default ?? '');
async function ok() {
emit('done', caption.value);

View File

@@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<component :is="prefer.s.enablePullToRefresh && pullToRefresh ? MkPullToRefresh : 'div'" :refresher="() => paginator.reload()">
<component :is="prefer.s.enablePullToRefresh && pullToRefresh ? MkPullToRefresh : 'div'" :refresher="() => paginator.reload()" @contextmenu.prevent.stop="onContextmenu">
<!-- :css="prefer.s.animation" にしたいけどバグる(おそらくvueのバグ) https://github.com/misskey-dev/misskey/issues/16078 -->
<Transition
:enterActiveClass="prefer.s.animation ? $style.transition_fade_enterActive : ''"
@@ -41,6 +41,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup generic="T extends PagingCtx">
import { isLink } from '@@/js/is-link.js';
import type { PagingCtx } from '@/composables/use-pagination.js';
import type { UnwrapRef } from 'vue';
import MkButton from '@/components/MkButton.vue';
@@ -48,6 +49,7 @@ import { i18n } from '@/i18n.js';
import { prefer } from '@/preferences.js';
import { usePagination } from '@/composables/use-pagination.js';
import MkPullToRefresh from '@/components/MkPullToRefresh.vue';
import * as os from '@/os.js';
type Paginator = ReturnType<typeof usePagination<T['endpoint']>>;
@@ -73,6 +75,19 @@ function appearFetchMore() {
paginator.fetchOlder();
}
function onContextmenu(ev: MouseEvent) {
if (ev.target && isLink(ev.target as HTMLElement)) return;
if (window.getSelection()?.toString() !== '') return;
os.contextMenu([{
icon: 'ti ti-refresh',
text: i18n.ts.reload,
action: () => {
paginator.reload();
},
}], ev);
}
defineSlots<{
empty: () => void;
default: (props: { items: UnwrapRef<Paginator['items']> }) => void;

View File

@@ -22,6 +22,12 @@ export function useScrollPositionKeeper(scrollContainerRef: Ref<HTMLElement | nu
if (!el) return;
if (!ready) return;
if (el.scrollTop < 100) {
// 上部にいるときはanchorを参照するとズレの原因になるし位置復元するメリットも乏しいため設定しない
anchorId = null;
return;
}
const scrollContainerRect = el.getBoundingClientRect();
const viewPosition = scrollContainerRect.height / 2;

View File

@@ -82,6 +82,7 @@ export type UploaderItem = {
file: File;
watermarkPresetId: string | null;
isSensitive?: boolean;
caption?: string | null;
abort?: (() => void) | null;
};
@@ -193,6 +194,21 @@ export function useUploader(options: {
get: () => item.isSensitive ?? false,
set: (value) => item.isSensitive = value,
}),
}, {
text: i18n.ts.describeFile,
icon: 'ti ti-text-caption',
action: async () => {
const { dispose } = await os.popupAsyncWithDialog(import('@/components/MkFileCaptionEditWindow.vue').then(x => x.default), {
default: item.caption ?? null,
}, {
done: caption => {
if (caption != null) {
item.caption = caption.trim().length === 0 ? null : caption;
}
},
closed: () => dispose(),
});
},
}, {
type: 'divider',
});
@@ -406,8 +422,9 @@ export function useUploader(options: {
const { filePromise, abort } = uploadFile(item.preprocessedFile ?? item.file, {
name: item.uploadName ?? item.name,
folderId: options.folderId,
folderId: options.folderId === undefined ? prefer.s.uploadFolder : options.folderId,
isSensitive: item.isSensitive ?? false,
caption: item.caption ?? null,
onProgress: (progress) => {
if (item.progress == null) {
item.progress = { max: progress.total, value: progress.loaded };

View File

@@ -280,6 +280,9 @@ const patronsWithIcon = [{
}, {
name: '新井 治',
icon: 'https://assets.misskey-hub.net/patrons/d160876f20394674a17963a0e609600a.jpg',
}, {
name: 'しきいし',
icon: 'https://assets.misskey-hub.net/patrons/77dd5387db41427ba9cbdc8849e76402.jpg',
}];
const patrons = [

View File

@@ -56,7 +56,7 @@ SPDX-License-Identifier: AGPL-3.0-only
:class="$style.themeRadio"
:value="instanceLightTheme.id"
/>
<label :for="`themeRadio_${instanceLightTheme.id}`" :class="$style.themeItemRoot" class="_button">
<label :for="`themeRadio_${instanceLightTheme.id}`" :class="$style.themeItemRoot" class="_button" @contextmenu.prevent.stop="onThemeContextmenu(instanceLightTheme, $event)">
<MkThemePreview :theme="instanceLightTheme" :class="$style.themeItemPreview"/>
<div :class="$style.themeItemCaption">{{ instanceLightTheme.name }}</div>
</label>
@@ -76,7 +76,7 @@ SPDX-License-Identifier: AGPL-3.0-only
:class="$style.themeRadio"
:value="theme.id"
/>
<label :for="`themeRadio_${theme.id}`" :class="$style.themeItemRoot" class="_button">
<label :for="`themeRadio_${theme.id}`" :class="$style.themeItemRoot" class="_button" @contextmenu.prevent.stop="onThemeContextmenu(theme, $event)">
<MkThemePreview :theme="theme" :class="$style.themeItemPreview"/>
<div :class="$style.themeItemCaption">{{ theme.name }}</div>
</label>
@@ -96,7 +96,7 @@ SPDX-License-Identifier: AGPL-3.0-only
:class="$style.themeRadio"
:value="theme.id"
/>
<label :for="`themeRadio_${theme.id}`" :class="$style.themeItemRoot" class="_button">
<label :for="`themeRadio_${theme.id}`" :class="$style.themeItemRoot" class="_button" @contextmenu.prevent.stop="onThemeContextmenu(theme, $event)">
<MkThemePreview :theme="theme" :class="$style.themeItemPreview"/>
<div :class="$style.themeItemCaption">{{ theme.name }}</div>
</label>
@@ -127,7 +127,7 @@ SPDX-License-Identifier: AGPL-3.0-only
:class="$style.themeRadio"
:value="instanceDarkTheme.id"
/>
<label :for="`themeRadio_${instanceDarkTheme.id}`" :class="$style.themeItemRoot" class="_button">
<label :for="`themeRadio_${instanceDarkTheme.id}`" :class="$style.themeItemRoot" class="_button" @contextmenu.prevent.stop="onThemeContextmenu(instanceDarkTheme, $event)">
<MkThemePreview :theme="instanceDarkTheme" :class="$style.themeItemPreview"/>
<div :class="$style.themeItemCaption">{{ instanceDarkTheme.name }}</div>
</label>
@@ -147,7 +147,7 @@ SPDX-License-Identifier: AGPL-3.0-only
:class="$style.themeRadio"
:value="theme.id"
/>
<label :for="`themeRadio_${theme.id}`" :class="$style.themeItemRoot" class="_button">
<label :for="`themeRadio_${theme.id}`" :class="$style.themeItemRoot" class="_button" @contextmenu.prevent.stop="onThemeContextmenu(theme, $event)">
<MkThemePreview :theme="theme" :class="$style.themeItemPreview"/>
<div :class="$style.themeItemCaption">{{ theme.name }}</div>
</label>
@@ -167,7 +167,7 @@ SPDX-License-Identifier: AGPL-3.0-only
:class="$style.themeRadio"
:value="theme.id"
/>
<label :for="`themeRadio_${theme.id}`" :class="$style.themeItemRoot" class="_button">
<label :for="`themeRadio_${theme.id}`" :class="$style.themeItemRoot" class="_button" @contextmenu.prevent.stop="onThemeContextmenu(theme, $event)">
<MkThemePreview :theme="theme" :class="$style.themeItemPreview"/>
<div :class="$style.themeItemCaption">{{ theme.name }}</div>
</label>
@@ -210,7 +210,7 @@ import FormSection from '@/components/form/section.vue';
import FormLink from '@/components/form/link.vue';
import MkFolder from '@/components/MkFolder.vue';
import MkThemePreview from '@/components/MkThemePreview.vue';
import { getBuiltinThemesRef, getThemesRef } from '@/theme.js';
import { getBuiltinThemesRef, getThemesRef, removeTheme } from '@/theme.js';
import { isDeviceDarkmode } from '@/utility/is-device-darkmode.js';
import { store } from '@/store.js';
import { i18n } from '@/i18n.js';
@@ -218,6 +218,7 @@ import { instance } from '@/instance.js';
import { uniqueBy } from '@/utility/array.js';
import { definePage } from '@/page.js';
import { prefer } from '@/preferences.js';
import { copyToClipboard } from '@/utility/copy-to-clipboard.js';
const installedThemes = getThemesRef();
const builtinThemes = getBuiltinThemesRef();
@@ -295,6 +296,26 @@ function changeThemesSyncEnabled(value: boolean) {
}
}
function onThemeContextmenu(theme: Theme, ev: MouseEvent) {
os.contextMenu([{
type: 'label',
text: theme.name,
}, {
icon: 'ti ti-clipboard',
text: i18n.ts._theme.copyThemeCode,
action: () => {
copyToClipboard(JSON5.stringify(theme, null, '\t'));
},
}, {
icon: 'ti ti-trash',
text: i18n.ts.delete,
danger: true,
action: () => {
removeTheme(theme);
},
}], ev);
}
const headerActions = computed(() => []);
const headerTabs = computed(() => []);

View File

@@ -162,6 +162,7 @@ export class PreferencesManager {
this.r[key].value = this.s[key] = v;
}
// TODO: desync対策 cloudの値のfetchが正常に完了していない状態でcommitすると多分値が上書きされる
public commit<K extends keyof PREF>(key: K, value: ValueOf<K>) {
const v = JSON.parse(JSON.stringify(value)); // deep copy 兼 vueのプロキシ解除

View File

@@ -35,6 +35,8 @@ export function getPreferencesProfileMenu(): MenuItem[] {
}
store.set('enablePreferencesAutoCloudBackup', true);
cloudBackup();
} else {
store.set('enablePreferencesAutoCloudBackup', false);
}

View File

@@ -33,6 +33,7 @@ export function uploadFile(file: File | Blob, options: {
name?: string;
folderId?: string | null;
isSensitive?: boolean;
caption?: string | null;
onProgress?: (ctx: { total: number; loaded: number; }) => void;
} = {}): UploadReturnType {
const xhr = new XMLHttpRequest();
@@ -142,6 +143,7 @@ export function uploadFile(file: File | Blob, options: {
formData.append('file', file);
formData.append('name', options.name ?? (file instanceof File ? file.name : 'untitled'));
formData.append('isSensitive', options.isSensitive ? 'true' : 'false');
if (options.caption != null) formData.append('comment', options.caption);
if (options.folderId) formData.append('folderId', options.folderId);
xhr.send(formData);

View File

@@ -1,7 +1,7 @@
{
"type": "module",
"name": "misskey-js",
"version": "2025.6.2",
"version": "2025.6.3",
"description": "Misskey SDK for JavaScript",
"license": "MIT",
"main": "./built/index.js",