Compare commits
1 Commits
5533b03f67
...
6f5ee6fead
Author | SHA1 | Date |
---|---|---|
dependabot[bot] | 6f5ee6fead |
|
@ -8,17 +8,15 @@
|
||||||
- 埋め込みコードやウェブサイトへの実装方法の詳細はMisskey Hubに掲載予定です
|
- 埋め込みコードやウェブサイトへの実装方法の詳細はMisskey Hubに掲載予定です
|
||||||
- Enhance: サイズ制限を超過するファイルをアップロードしようとした際にエラーを出すように
|
- Enhance: サイズ制限を超過するファイルをアップロードしようとした際にエラーを出すように
|
||||||
- Enhance: アイコンデコレーション管理画面にプレビューを追加
|
- Enhance: アイコンデコレーション管理画面にプレビューを追加
|
||||||
- Enhance: コントロールパネル内のファイル一覧でセンシティブなファイルを区別しやすく
|
|
||||||
- Fix: サーバーメトリクスが2つ以上あるとリロード直後の表示がおかしくなる問題を修正
|
- Fix: サーバーメトリクスが2つ以上あるとリロード直後の表示がおかしくなる問題を修正
|
||||||
- Fix: 月の違う同じ日はセパレータが表示されないのを修正
|
- Fix: 月の違う同じ日はセパレータが表示されないのを修正
|
||||||
|
|
||||||
### Server
|
### Server
|
||||||
- Fix: アンテナの書き込み時にキーワードが与えられなかった場合のエラーをApiErrorとして投げるように
|
|
||||||
- この変更により、公式フロントエンドでは入力の不備が内部エラーとして報告される代わりに一般的なエラーダイアログで報告されます
|
|
||||||
- Fix: ファイルがサイズの制限を超えてアップロードされた際にエラーを返さなかった問題を修正
|
- Fix: ファイルがサイズの制限を超えてアップロードされた際にエラーを返さなかった問題を修正
|
||||||
- Fix: 外部ページを解析する際に、ページに紐づけられた関連リソースも読み込まれてしまう問題を修正
|
- Fix: 外部ページを解析する際に、ページに紐づけられた関連リソースも読み込まれてしまう問題を修正
|
||||||
(Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/commit/26e0412fbb91447c37e8fb06ffb0487346063bb8)
|
(Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/commit/26e0412fbb91447c37e8fb06ffb0487346063bb8)
|
||||||
|
|
||||||
|
|
||||||
## 2024.8.0
|
## 2024.8.0
|
||||||
|
|
||||||
### General
|
### General
|
||||||
|
|
|
@ -100,7 +100,7 @@
|
||||||
"async-mutex": "0.5.0",
|
"async-mutex": "0.5.0",
|
||||||
"bcryptjs": "2.4.3",
|
"bcryptjs": "2.4.3",
|
||||||
"blurhash": "2.0.5",
|
"blurhash": "2.0.5",
|
||||||
"body-parser": "1.20.3",
|
"body-parser": "1.20.2",
|
||||||
"bullmq": "5.10.4",
|
"bullmq": "5.10.4",
|
||||||
"cacheable-lookup": "7.0.0",
|
"cacheable-lookup": "7.0.0",
|
||||||
"cbor": "9.0.2",
|
"cbor": "9.0.2",
|
||||||
|
|
|
@ -10,9 +10,8 @@
|
||||||
* The getter will return a .bind version of the function
|
* The getter will return a .bind version of the function
|
||||||
* and memoize the result against a symbol on the instance
|
* and memoize the result against a symbol on the instance
|
||||||
*/
|
*/
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
export function bindThis(target: any, key: string, descriptor: any) {
|
export function bindThis(target: any, key: string, descriptor: any) {
|
||||||
const fn = descriptor.value;
|
let fn = descriptor.value;
|
||||||
|
|
||||||
if (typeof fn !== 'function') {
|
if (typeof fn !== 'function') {
|
||||||
throw new TypeError(`@bindThis decorator can only be applied to methods not: ${typeof fn}`);
|
throw new TypeError(`@bindThis decorator can only be applied to methods not: ${typeof fn}`);
|
||||||
|
@ -22,18 +21,26 @@ export function bindThis(target: any, key: string, descriptor: any) {
|
||||||
configurable: true,
|
configurable: true,
|
||||||
get() {
|
get() {
|
||||||
// eslint-disable-next-line no-prototype-builtins
|
// eslint-disable-next-line no-prototype-builtins
|
||||||
if (this === target.prototype || this.hasOwnProperty(key)) {
|
if (this === target.prototype || this.hasOwnProperty(key) ||
|
||||||
|
typeof fn !== 'function') {
|
||||||
return fn;
|
return fn;
|
||||||
}
|
}
|
||||||
|
|
||||||
const boundFn = fn.bind(this);
|
const boundFn = fn.bind(this);
|
||||||
Reflect.defineProperty(this, key, {
|
Object.defineProperty(this, key, {
|
||||||
value: boundFn,
|
|
||||||
configurable: true,
|
configurable: true,
|
||||||
writable: true,
|
get() {
|
||||||
|
return boundFn;
|
||||||
|
},
|
||||||
|
set(value) {
|
||||||
|
fn = value;
|
||||||
|
delete this[key];
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return boundFn;
|
return boundFn;
|
||||||
},
|
},
|
||||||
|
set(value: any) {
|
||||||
|
fn = value;
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,12 +34,6 @@ export const meta = {
|
||||||
code: 'TOO_MANY_ANTENNAS',
|
code: 'TOO_MANY_ANTENNAS',
|
||||||
id: 'faf47050-e8b5-438c-913c-db2b1576fde4',
|
id: 'faf47050-e8b5-438c-913c-db2b1576fde4',
|
||||||
},
|
},
|
||||||
|
|
||||||
emptyKeyword: {
|
|
||||||
message: 'Either keywords or excludeKeywords is required.',
|
|
||||||
code: 'EMPTY_KEYWORD',
|
|
||||||
id: '53ee222e-1ddd-4f9a-92e5-9fb82ddb463a',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
|
||||||
res: {
|
res: {
|
||||||
|
@ -93,7 +87,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
) {
|
) {
|
||||||
super(meta, paramDef, async (ps, me) => {
|
super(meta, paramDef, async (ps, me) => {
|
||||||
if (ps.keywords.flat().every(x => x === '') && ps.excludeKeywords.flat().every(x => x === '')) {
|
if (ps.keywords.flat().every(x => x === '') && ps.excludeKeywords.flat().every(x => x === '')) {
|
||||||
throw new ApiError(meta.errors.emptyKeyword);
|
throw new Error('either keywords or excludeKeywords is required.');
|
||||||
}
|
}
|
||||||
|
|
||||||
const currentAntennasCount = await this.antennasRepository.countBy({
|
const currentAntennasCount = await this.antennasRepository.countBy({
|
||||||
|
|
|
@ -32,12 +32,6 @@ export const meta = {
|
||||||
code: 'NO_SUCH_USER_LIST',
|
code: 'NO_SUCH_USER_LIST',
|
||||||
id: '1c6b35c9-943e-48c2-81e4-2844989407f7',
|
id: '1c6b35c9-943e-48c2-81e4-2844989407f7',
|
||||||
},
|
},
|
||||||
|
|
||||||
emptyKeyword: {
|
|
||||||
message: 'Either keywords or excludeKeywords is required.',
|
|
||||||
code: 'EMPTY_KEYWORD',
|
|
||||||
id: '721aaff6-4e1b-4d88-8de6-877fae9f68c4',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
|
||||||
res: {
|
res: {
|
||||||
|
@ -91,7 +85,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
super(meta, paramDef, async (ps, me) => {
|
super(meta, paramDef, async (ps, me) => {
|
||||||
if (ps.keywords && ps.excludeKeywords) {
|
if (ps.keywords && ps.excludeKeywords) {
|
||||||
if (ps.keywords.flat().every(x => x === '') && ps.excludeKeywords.flat().every(x => x === '')) {
|
if (ps.keywords.flat().every(x => x === '') && ps.excludeKeywords.flat().every(x => x === '')) {
|
||||||
throw new ApiError(meta.errors.emptyKeyword);
|
throw new Error('either keywords or excludeKeywords is required.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Fetch the antenna
|
// Fetch the antenna
|
||||||
|
|
|
@ -228,17 +228,6 @@ describe('アンテナ', () => {
|
||||||
assert.deepStrictEqual(response, expected);
|
assert.deepStrictEqual(response, expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('を作成する時キーワードが指定されていないとエラーになる', async () => {
|
|
||||||
await failedApiCall({
|
|
||||||
endpoint: 'antennas/create',
|
|
||||||
parameters: { ...defaultParam, keywords: [[]], excludeKeywords: [[]] },
|
|
||||||
user: alice
|
|
||||||
}, {
|
|
||||||
status: 400,
|
|
||||||
code: 'EMPTY_KEYWORD',
|
|
||||||
id: '53ee222e-1ddd-4f9a-92e5-9fb82ddb463a'
|
|
||||||
})
|
|
||||||
});
|
|
||||||
//#endregion
|
//#endregion
|
||||||
//#region 更新(antennas/update)
|
//#region 更新(antennas/update)
|
||||||
|
|
||||||
|
@ -266,18 +255,6 @@ describe('アンテナ', () => {
|
||||||
id: '1c6b35c9-943e-48c2-81e4-2844989407f7',
|
id: '1c6b35c9-943e-48c2-81e4-2844989407f7',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
test('を変更する時キーワードが指定されていないとエラーになる', async () => {
|
|
||||||
const antenna = await successfulApiCall({ endpoint: 'antennas/create', parameters: defaultParam, user: alice });
|
|
||||||
await failedApiCall({
|
|
||||||
endpoint: 'antennas/update',
|
|
||||||
parameters: { ...defaultParam, antennaId: antenna.id, keywords: [[]], excludeKeywords: [[]] },
|
|
||||||
user: alice
|
|
||||||
}, {
|
|
||||||
status: 400,
|
|
||||||
code: 'EMPTY_KEYWORD',
|
|
||||||
id: '721aaff6-4e1b-4d88-8de6-877fae9f68c4'
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
//#endregion
|
//#endregion
|
||||||
//#region 表示(antennas/show)
|
//#region 表示(antennas/show)
|
||||||
|
|
|
@ -4,13 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div ref="thumbnail" :class="$style.root">
|
||||||
ref="thumbnail"
|
|
||||||
:class="[
|
|
||||||
$style.root,
|
|
||||||
{ [$style.sensitiveHighlight]: highlightWhenSensitive && file.isSensitive },
|
|
||||||
]"
|
|
||||||
>
|
|
||||||
<ImgWithBlurhash v-if="isThumbnailAvailable" :hash="file.blurhash" :src="file.thumbnailUrl" :alt="file.name" :title="file.name" :cover="fit !== 'contain'"/>
|
<ImgWithBlurhash v-if="isThumbnailAvailable" :hash="file.blurhash" :src="file.thumbnailUrl" :alt="file.name" :title="file.name" :cover="fit !== 'contain'"/>
|
||||||
<i v-else-if="is === 'image'" class="ti ti-photo" :class="$style.icon"></i>
|
<i v-else-if="is === 'image'" class="ti ti-photo" :class="$style.icon"></i>
|
||||||
<i v-else-if="is === 'video'" class="ti ti-video" :class="$style.icon"></i>
|
<i v-else-if="is === 'video'" class="ti ti-video" :class="$style.icon"></i>
|
||||||
|
@ -33,7 +27,6 @@ import ImgWithBlurhash from '@/components/MkImgWithBlurhash.vue';
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
file: Misskey.entities.DriveFile;
|
file: Misskey.entities.DriveFile;
|
||||||
fit: 'cover' | 'contain';
|
fit: 'cover' | 'contain';
|
||||||
highlightWhenSensitive?: boolean;
|
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const is = computed(() => {
|
const is = computed(() => {
|
||||||
|
@ -74,18 +67,6 @@ const isThumbnailAvailable = computed(() => {
|
||||||
overflow: clip;
|
overflow: clip;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sensitiveHighlight::after {
|
|
||||||
content: "";
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
pointer-events: none;
|
|
||||||
border-radius: inherit;
|
|
||||||
box-shadow: inset 0 0 0 4px var(--warn);
|
|
||||||
}
|
|
||||||
|
|
||||||
.iconSub {
|
.iconSub {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 30%;
|
width: 30%;
|
||||||
|
|
|
@ -14,7 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
class="file _button"
|
class="file _button"
|
||||||
>
|
>
|
||||||
<div v-if="file.isSensitive" class="sensitive-label">{{ i18n.ts.sensitive }}</div>
|
<div v-if="file.isSensitive" class="sensitive-label">{{ i18n.ts.sensitive }}</div>
|
||||||
<MkDriveFileThumbnail class="thumbnail" :file="file" fit="contain" :highlightWhenSensitive="true"/>
|
<MkDriveFileThumbnail class="thumbnail" :file="file" fit="contain"/>
|
||||||
<div v-if="viewMode === 'list'" class="body">
|
<div v-if="viewMode === 'list'" class="body">
|
||||||
<div>
|
<div>
|
||||||
<small style="opacity: 0.7;">{{ file.name }}</small>
|
<small style="opacity: 0.7;">{{ file.name }}</small>
|
||||||
|
|
|
@ -63,7 +63,7 @@ async function detachAndDeleteMedia(file: Misskey.entities.DriveFile) {
|
||||||
|
|
||||||
const { canceled } = await os.confirm({
|
const { canceled } = await os.confirm({
|
||||||
type: 'warning',
|
type: 'warning',
|
||||||
text: i18n.tsx.driveFileDeleteConfirm({ name: file.name }),
|
text: i18n.t('driveFileDeleteConfirm', { name: file.name }),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (canceled) return;
|
if (canceled) return;
|
||||||
|
|
|
@ -6,12 +6,12 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<template>
|
<template>
|
||||||
<div class="_gaps_m">
|
<div class="_gaps_m">
|
||||||
<div :class="$style.buttons">
|
<div :class="$style.buttons">
|
||||||
<MkButton inline primary @click="saveNew">{{ i18n.ts._preferencesBackups.saveNew }}</MkButton>
|
<MkButton inline primary @click="saveNew">{{ ts._preferencesBackups.saveNew }}</MkButton>
|
||||||
<MkButton inline @click="loadFile">{{ i18n.ts._preferencesBackups.loadFile }}</MkButton>
|
<MkButton inline @click="loadFile">{{ ts._preferencesBackups.loadFile }}</MkButton>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<FormSection>
|
<FormSection>
|
||||||
<template #label>{{ i18n.ts._preferencesBackups.list }}</template>
|
<template #label>{{ ts._preferencesBackups.list }}</template>
|
||||||
<template v-if="profiles && Object.keys(profiles).length > 0">
|
<template v-if="profiles && Object.keys(profiles).length > 0">
|
||||||
<div class="_gaps_s">
|
<div class="_gaps_s">
|
||||||
<div
|
<div
|
||||||
|
@ -23,13 +23,13 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
@contextmenu.prevent.stop="$event => menu($event, id)"
|
@contextmenu.prevent.stop="$event => menu($event, id)"
|
||||||
>
|
>
|
||||||
<div :class="$style.profileName">{{ profile.name }}</div>
|
<div :class="$style.profileName">{{ profile.name }}</div>
|
||||||
<div :class="$style.profileTime">{{ i18n.tsx._preferencesBackups.createdAt({ date: (new Date(profile.createdAt)).toLocaleDateString(), time: (new Date(profile.createdAt)).toLocaleTimeString() }) }}</div>
|
<div :class="$style.profileTime">{{ t('_preferencesBackups.createdAt', { date: (new Date(profile.createdAt)).toLocaleDateString(), time: (new Date(profile.createdAt)).toLocaleTimeString() }) }}</div>
|
||||||
<div v-if="profile.updatedAt" :class="$style.profileTime">{{ i18n.tsx._preferencesBackups.updatedAt({ date: (new Date(profile.updatedAt)).toLocaleDateString(), time: (new Date(profile.updatedAt)).toLocaleTimeString() }) }}</div>
|
<div v-if="profile.updatedAt" :class="$style.profileTime">{{ t('_preferencesBackups.updatedAt', { date: (new Date(profile.updatedAt)).toLocaleDateString(), time: (new Date(profile.updatedAt)).toLocaleTimeString() }) }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div v-else-if="profiles">
|
<div v-else-if="profiles">
|
||||||
<MkInfo>{{ i18n.ts._preferencesBackups.noBackups }}</MkInfo>
|
<MkInfo>{{ ts._preferencesBackups.noBackups }}</MkInfo>
|
||||||
</div>
|
</div>
|
||||||
<MkLoading v-else/>
|
<MkLoading v-else/>
|
||||||
</FormSection>
|
</FormSection>
|
||||||
|
@ -52,6 +52,7 @@ import { i18n } from '@/i18n.js';
|
||||||
import { version, host } from '@@/js/config.js';
|
import { version, host } from '@@/js/config.js';
|
||||||
import { definePageMetadata } from '@/scripts/page-metadata.js';
|
import { definePageMetadata } from '@/scripts/page-metadata.js';
|
||||||
import { miLocalStorage } from '@/local-storage.js';
|
import { miLocalStorage } from '@/local-storage.js';
|
||||||
|
const { t, ts } = i18n;
|
||||||
|
|
||||||
const defaultStoreSaveKeys: (keyof typeof defaultStore['state'])[] = [
|
const defaultStoreSaveKeys: (keyof typeof defaultStore['state'])[] = [
|
||||||
'collapseRenotes',
|
'collapseRenotes',
|
||||||
|
@ -200,15 +201,15 @@ async function saveNew(): Promise<void> {
|
||||||
if (!profiles.value) return;
|
if (!profiles.value) return;
|
||||||
|
|
||||||
const { canceled, result: name } = await os.inputText({
|
const { canceled, result: name } = await os.inputText({
|
||||||
title: i18n.ts._preferencesBackups.inputName,
|
title: ts._preferencesBackups.inputName,
|
||||||
default: '',
|
default: '',
|
||||||
});
|
});
|
||||||
if (canceled) return;
|
if (canceled) return;
|
||||||
|
|
||||||
if (Object.values(profiles.value).some(x => x.name === name)) {
|
if (Object.values(profiles.value).some(x => x.name === name)) {
|
||||||
return os.alert({
|
return os.alert({
|
||||||
title: i18n.ts._preferencesBackups.cannotSave,
|
title: ts._preferencesBackups.cannotSave,
|
||||||
text: i18n.tsx._preferencesBackups.nameAlreadyExists({ name }),
|
text: t('_preferencesBackups.nameAlreadyExists', { name }),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -237,8 +238,8 @@ function loadFile(): void {
|
||||||
if (file.type !== 'application/json') {
|
if (file.type !== 'application/json') {
|
||||||
return os.alert({
|
return os.alert({
|
||||||
type: 'error',
|
type: 'error',
|
||||||
title: i18n.ts._preferencesBackups.cannotLoad,
|
title: ts._preferencesBackups.cannotLoad,
|
||||||
text: i18n.ts._preferencesBackups.invalidFile,
|
text: ts._preferencesBackups.invalidFile,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -249,7 +250,7 @@ function loadFile(): void {
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return os.alert({
|
return os.alert({
|
||||||
type: 'error',
|
type: 'error',
|
||||||
title: i18n.ts._preferencesBackups.cannotLoad,
|
title: ts._preferencesBackups.cannotLoad,
|
||||||
text: (err as any)?.message ?? '',
|
text: (err as any)?.message ?? '',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -275,8 +276,8 @@ async function applyProfile(id: string): Promise<void> {
|
||||||
|
|
||||||
const { canceled: cancel1 } = await os.confirm({
|
const { canceled: cancel1 } = await os.confirm({
|
||||||
type: 'warning',
|
type: 'warning',
|
||||||
title: i18n.ts._preferencesBackups.apply,
|
title: ts._preferencesBackups.apply,
|
||||||
text: i18n.tsx._preferencesBackups.applyConfirm({ name: profile.name }),
|
text: t('_preferencesBackups.applyConfirm', { name: profile.name }),
|
||||||
});
|
});
|
||||||
if (cancel1) return;
|
if (cancel1) return;
|
||||||
|
|
||||||
|
@ -321,7 +322,7 @@ async function applyProfile(id: string): Promise<void> {
|
||||||
|
|
||||||
const { canceled: cancel2 } = await os.confirm({
|
const { canceled: cancel2 } = await os.confirm({
|
||||||
type: 'info',
|
type: 'info',
|
||||||
text: i18n.ts.reloadToApplySetting,
|
text: ts.reloadToApplySetting,
|
||||||
});
|
});
|
||||||
if (cancel2) return;
|
if (cancel2) return;
|
||||||
|
|
||||||
|
@ -333,8 +334,8 @@ async function deleteProfile(id: string): Promise<void> {
|
||||||
|
|
||||||
const { canceled } = await os.confirm({
|
const { canceled } = await os.confirm({
|
||||||
type: 'info',
|
type: 'info',
|
||||||
title: i18n.ts.delete,
|
title: ts.delete,
|
||||||
text: i18n.tsx.deleteAreYouSure({ x: profiles.value[id].name }),
|
text: t('deleteAreYouSure', { x: profiles.value[id].name }),
|
||||||
});
|
});
|
||||||
if (canceled) return;
|
if (canceled) return;
|
||||||
|
|
||||||
|
@ -349,8 +350,8 @@ async function save(id: string): Promise<void> {
|
||||||
|
|
||||||
const { canceled } = await os.confirm({
|
const { canceled } = await os.confirm({
|
||||||
type: 'info',
|
type: 'info',
|
||||||
title: i18n.ts._preferencesBackups.save,
|
title: ts._preferencesBackups.save,
|
||||||
text: i18n.tsx._preferencesBackups.saveConfirm({ name }),
|
text: t('_preferencesBackups.saveConfirm', { name }),
|
||||||
});
|
});
|
||||||
if (canceled) return;
|
if (canceled) return;
|
||||||
|
|
||||||
|
@ -369,15 +370,15 @@ async function rename(id: string): Promise<void> {
|
||||||
if (!profiles.value) return;
|
if (!profiles.value) return;
|
||||||
|
|
||||||
const { canceled: cancel1, result: name } = await os.inputText({
|
const { canceled: cancel1, result: name } = await os.inputText({
|
||||||
title: i18n.ts._preferencesBackups.inputName,
|
title: ts._preferencesBackups.inputName,
|
||||||
default: '',
|
default: '',
|
||||||
});
|
});
|
||||||
if (cancel1 || profiles.value[id].name === name) return;
|
if (cancel1 || profiles.value[id].name === name) return;
|
||||||
|
|
||||||
if (Object.values(profiles.value).some(x => x.name === name)) {
|
if (Object.values(profiles.value).some(x => x.name === name)) {
|
||||||
return os.alert({
|
return os.alert({
|
||||||
title: i18n.ts._preferencesBackups.cannotSave,
|
title: ts._preferencesBackups.cannotSave,
|
||||||
text: i18n.tsx._preferencesBackups.nameAlreadyExists({ name }),
|
text: t('_preferencesBackups.nameAlreadyExists', { name }),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -385,8 +386,8 @@ async function rename(id: string): Promise<void> {
|
||||||
|
|
||||||
const { canceled: cancel2 } = await os.confirm({
|
const { canceled: cancel2 } = await os.confirm({
|
||||||
type: 'info',
|
type: 'info',
|
||||||
title: i18n.ts.rename,
|
title: ts.rename,
|
||||||
text: i18n.tsx._preferencesBackups.renameConfirm({ old: registry.name, new: name }),
|
text: t('_preferencesBackups.renameConfirm', { old: registry.name, new: name }),
|
||||||
});
|
});
|
||||||
if (cancel2) return;
|
if (cancel2) return;
|
||||||
|
|
||||||
|
@ -398,25 +399,25 @@ function menu(ev: MouseEvent, profileId: string) {
|
||||||
if (!profiles.value) return;
|
if (!profiles.value) return;
|
||||||
|
|
||||||
return os.popupMenu([{
|
return os.popupMenu([{
|
||||||
text: i18n.ts._preferencesBackups.apply,
|
text: ts._preferencesBackups.apply,
|
||||||
icon: 'ti ti-check',
|
icon: 'ti ti-check',
|
||||||
action: () => applyProfile(profileId),
|
action: () => applyProfile(profileId),
|
||||||
}, {
|
}, {
|
||||||
type: 'a',
|
type: 'a',
|
||||||
text: i18n.ts.download,
|
text: ts.download,
|
||||||
icon: 'ti ti-download',
|
icon: 'ti ti-download',
|
||||||
href: URL.createObjectURL(new Blob([JSON.stringify(profiles.value[profileId], null, 2)], { type: 'application/json' })),
|
href: URL.createObjectURL(new Blob([JSON.stringify(profiles.value[profileId], null, 2)], { type: 'application/json' })),
|
||||||
download: `${profiles.value[profileId].name}.json`,
|
download: `${profiles.value[profileId].name}.json`,
|
||||||
}, { type: 'divider' }, {
|
}, { type: 'divider' }, {
|
||||||
text: i18n.ts.rename,
|
text: ts.rename,
|
||||||
icon: 'ti ti-forms',
|
icon: 'ti ti-forms',
|
||||||
action: () => rename(profileId),
|
action: () => rename(profileId),
|
||||||
}, {
|
}, {
|
||||||
text: i18n.ts._preferencesBackups.save,
|
text: ts._preferencesBackups.save,
|
||||||
icon: 'ti ti-device-floppy',
|
icon: 'ti ti-device-floppy',
|
||||||
action: () => save(profileId),
|
action: () => save(profileId),
|
||||||
}, { type: 'divider' }, {
|
}, { type: 'divider' }, {
|
||||||
text: i18n.ts.delete,
|
text: ts.delete,
|
||||||
icon: 'ti ti-trash',
|
icon: 'ti ti-trash',
|
||||||
action: () => deleteProfile(profileId),
|
action: () => deleteProfile(profileId),
|
||||||
danger: true,
|
danger: true,
|
||||||
|
@ -438,7 +439,7 @@ onUnmounted(() => {
|
||||||
});
|
});
|
||||||
|
|
||||||
definePageMetadata(() => ({
|
definePageMetadata(() => ({
|
||||||
title: i18n.ts.preferencesBackups,
|
title: ts.preferencesBackups,
|
||||||
icon: 'ti ti-device-floppy',
|
icon: 'ti ti-device-floppy',
|
||||||
}));
|
}));
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { createHighlighterCore, loadWasm } from 'shiki/core';
|
import { getHighlighterCore, loadWasm } from 'shiki/core';
|
||||||
import darkPlus from 'shiki/themes/dark-plus.mjs';
|
import darkPlus from 'shiki/themes/dark-plus.mjs';
|
||||||
import { bundledThemesInfo } from 'shiki/themes';
|
import { bundledThemesInfo } from 'shiki/themes';
|
||||||
import { bundledLanguagesInfo } from 'shiki/langs';
|
import { bundledLanguagesInfo } from 'shiki/langs';
|
||||||
|
@ -69,7 +69,7 @@ async function initHighlighter() {
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const jsLangInfo = bundledLanguagesInfo.find(t => t.id === 'javascript');
|
const jsLangInfo = bundledLanguagesInfo.find(t => t.id === 'javascript');
|
||||||
const highlighter = await createHighlighterCore({
|
const highlighter = await getHighlighterCore({
|
||||||
themes,
|
themes,
|
||||||
langs: [
|
langs: [
|
||||||
...(jsLangInfo ? [async () => await jsLangInfo.import()] : []),
|
...(jsLangInfo ? [async () => await jsLangInfo.import()] : []),
|
||||||
|
|
Loading…
Reference in New Issue