forked from mirrors/misskey
Compare commits
14 Commits
2025.6.2
...
renovate/d
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e3eb1d011f | ||
|
|
06d31c0b78 | ||
|
|
32d4c312ef | ||
|
|
36fde67992 | ||
|
|
43abbce2af | ||
|
|
684424f26a | ||
|
|
36989e0cd3 | ||
|
|
d518682e73 | ||
|
|
0ada970337 | ||
|
|
a812dfe853 | ||
|
|
2baec208f5 | ||
|
|
4093616e23 | ||
|
|
062d5170df | ||
|
|
a279bd4d49 |
@@ -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"
|
||||
|
||||
19
CHANGELOG.md
19
CHANGELOG.md
@@ -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
4
locales/index.d.ts
vendored
@@ -8366,6 +8366,10 @@ export interface Locale extends ILocale {
|
||||
* テーマコード
|
||||
*/
|
||||
"code": string;
|
||||
/**
|
||||
* テーマコードをコピー
|
||||
*/
|
||||
"copyThemeCode": string;
|
||||
/**
|
||||
* 説明
|
||||
*/
|
||||
|
||||
@@ -2193,6 +2193,7 @@ _theme:
|
||||
install: "テーマのインストール"
|
||||
manage: "テーマの管理"
|
||||
code: "テーマコード"
|
||||
copyThemeCode: "テーマコードをコピー"
|
||||
description: "説明"
|
||||
installed: "{name}をインストールしました"
|
||||
installedThemes: "インストールされたテーマ"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "misskey",
|
||||
"version": "2025.6.2",
|
||||
"version": "2025.6.3",
|
||||
"codename": "nasubi",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -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>());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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 };
|
||||
|
||||
@@ -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 = [
|
||||
|
||||
@@ -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(() => []);
|
||||
|
||||
@@ -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のプロキシ解除
|
||||
|
||||
|
||||
@@ -35,6 +35,8 @@ export function getPreferencesProfileMenu(): MenuItem[] {
|
||||
}
|
||||
|
||||
store.set('enablePreferencesAutoCloudBackup', true);
|
||||
|
||||
cloudBackup();
|
||||
} else {
|
||||
store.set('enablePreferencesAutoCloudBackup', false);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user