mirror of
https://github.com/misskey-dev/misskey.git
synced 2026-07-30 11:55:49 +00:00
Compare commits
20 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fbf8db618c | |||
| 6f3cc2cdf7 | |||
| 7c1f4c9037 | |||
| 2da20bf3e8 | |||
| 6d54370f01 | |||
| 905d3c87f1 | |||
| fc244067e0 | |||
| 8449354887 | |||
| 57e0f1b4ef | |||
| a1e170e065 | |||
| 73de40b81e | |||
| 2c836e3c24 | |||
| c2c5898221 | |||
| 99adf12355 | |||
| 7df0a6c55f | |||
| e092008dc5 | |||
| 0cfc910cdc | |||
| 62f68de800 | |||
| 5bf13c4cc2 | |||
| 16f47adcc6 |
@@ -9,9 +9,23 @@
|
||||
- 定期的に参照されていない古いリモートの投稿を削除する機能が実装されました(コントロールパネル→パフォーマンス→Remote Notes Cleaning)
|
||||
- 既存のサーバーでは**デフォルトでオフ**、新規サーバーでは**デフォルトでオン**になります
|
||||
- データベースの肥大化を防止することが可能です
|
||||
- 既存のサーバーで当機能を有効化した場合は、処理量が多くなるため、一時的にストレージ使用量が増加する可能性があります。
|
||||
- 増加量を抑えるには、最大処理継続時間をデフォルトより短くしてください。
|
||||
- サーバーの初期設定が完了するまでは連合がオンにならないようになりました
|
||||
- 日本語における公開範囲名称の「ダイレクト」が「指名」に改称されました
|
||||
- mfm.jsをアップデートしました
|
||||
- Enhance: Unicode 15.1 および 16.0 に収録されている絵文字に対応
|
||||
- Enhance: acctに `.` が入っているユーザーのメンションに対応
|
||||
- Fix: Unicode絵文字に隣接する異体字セレクタ(`U+FE0F`)が絵文字として認識される問題を修正
|
||||
|
||||
### Client
|
||||
- Feat: セーフモード
|
||||
- プラグイン・テーマ・カスタムCSSの使用でクライアントの起動に問題が発生した際に、これらを無効にして起動できます
|
||||
- 以下の方法でセーフモードを起動できます
|
||||
- `g` キーを連打する
|
||||
- URLに`?safemode=true`を付ける
|
||||
- PWAのショートカットで Safemode を選択して起動する
|
||||
- Enhance: コントロールパネルを検索できるように
|
||||
- Fix: 一部の設定検索結果が存在しないパスになる問題を修正
|
||||
(Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/1171)
|
||||
- Fix: テーマエディタが動作しない問題を修正
|
||||
|
||||
Vendored
+27
-7
@@ -315,11 +315,11 @@ export interface Locale extends ILocale {
|
||||
*/
|
||||
"mention": string;
|
||||
/**
|
||||
* あなた宛て
|
||||
* メンション
|
||||
*/
|
||||
"mentions": string;
|
||||
/**
|
||||
* ダイレクト投稿
|
||||
* 指名
|
||||
*/
|
||||
"directNotes": string;
|
||||
/**
|
||||
@@ -5501,6 +5501,22 @@ export interface Locale extends ILocale {
|
||||
* 日
|
||||
*/
|
||||
"inDays": string;
|
||||
/**
|
||||
* セーフモードが有効です
|
||||
*/
|
||||
"safeModeEnabled": string;
|
||||
/**
|
||||
* セーフモードが有効なため、プラグインはすべて無効化されています。
|
||||
*/
|
||||
"pluginsAreDisabledBecauseSafeMode": string;
|
||||
/**
|
||||
* セーフモードが有効なため、カスタムCSSは適用されていません。
|
||||
*/
|
||||
"customCssIsDisabledBecauseSafeMode": string;
|
||||
/**
|
||||
* セーフモードが有効な間はデフォルトのテーマが使用されます。セーフモードをオフにすると元に戻ります。
|
||||
*/
|
||||
"themeIsDefaultBecauseSafeMode": string;
|
||||
"_order": {
|
||||
/**
|
||||
* 新しい順
|
||||
@@ -6337,7 +6353,7 @@ export interface Locale extends ILocale {
|
||||
*/
|
||||
"followers": string;
|
||||
/**
|
||||
* 指定したユーザーにのみ公開され、また相手に通知が入ります。ダイレクトメッセージのかわりにお使いいただけます。
|
||||
* 指定したユーザーにのみ公開され、また相手に通知が入ります。
|
||||
*/
|
||||
"direct": string;
|
||||
/**
|
||||
@@ -6345,7 +6361,7 @@ export interface Locale extends ILocale {
|
||||
*/
|
||||
"doNotSendConfidencialOnDirect1": string;
|
||||
/**
|
||||
* 送信先のサーバーの管理者は投稿内容を見ることが可能なので、信頼できないサーバーのユーザーにダイレクト投稿を送信する場合は、機密情報の扱いに注意が必要です。
|
||||
* 送信先のサーバーの管理者は投稿内容を見ることが可能なので、信頼できないサーバーのユーザーが含まれる限定公開のノートを作成する際は、機密情報の扱いに注意が必要です。
|
||||
*/
|
||||
"doNotSendConfidencialOnDirect2": string;
|
||||
/**
|
||||
@@ -9625,7 +9641,7 @@ export interface Locale extends ILocale {
|
||||
*/
|
||||
"followersDescription": string;
|
||||
/**
|
||||
* ダイレクト
|
||||
* 指名
|
||||
*/
|
||||
"specified": string;
|
||||
/**
|
||||
@@ -10514,11 +10530,11 @@ export interface Locale extends ILocale {
|
||||
*/
|
||||
"channel": string;
|
||||
/**
|
||||
* あなた宛て
|
||||
* メンション
|
||||
*/
|
||||
"mentions": string;
|
||||
/**
|
||||
* ダイレクト
|
||||
* 指名
|
||||
*/
|
||||
"direct": string;
|
||||
/**
|
||||
@@ -11839,6 +11855,10 @@ export interface Locale extends ILocale {
|
||||
* 修復ツールを起動
|
||||
*/
|
||||
"otherOption3": string;
|
||||
/**
|
||||
* Misskeyをセーフモードで起動
|
||||
*/
|
||||
"otherOption4": string;
|
||||
};
|
||||
"_search": {
|
||||
/**
|
||||
|
||||
+12
-7
@@ -74,8 +74,8 @@ youGotNewFollower: "フォローされました"
|
||||
receiveFollowRequest: "フォローリクエストされました"
|
||||
followRequestAccepted: "フォローが承認されました"
|
||||
mention: "メンション"
|
||||
mentions: "あなた宛て"
|
||||
directNotes: "ダイレクト投稿"
|
||||
mentions: "メンション"
|
||||
directNotes: "指名"
|
||||
importAndExport: "インポートとエクスポート"
|
||||
import: "インポート"
|
||||
export: "エクスポート"
|
||||
@@ -1370,6 +1370,10 @@ defaultImageCompressionLevel: "デフォルトの画像圧縮度"
|
||||
defaultImageCompressionLevel_description: "低くすると画質を保てますが、ファイルサイズは増加します。<br>高くするとファイルサイズを減らせますが、画質は低下します。"
|
||||
inMinutes: "分"
|
||||
inDays: "日"
|
||||
safeModeEnabled: "セーフモードが有効です"
|
||||
pluginsAreDisabledBecauseSafeMode: "セーフモードが有効なため、プラグインはすべて無効化されています。"
|
||||
customCssIsDisabledBecauseSafeMode: "セーフモードが有効なため、カスタムCSSは適用されていません。"
|
||||
themeIsDefaultBecauseSafeMode: "セーフモードが有効な間はデフォルトのテーマが使用されます。セーフモードをオフにすると元に戻ります。"
|
||||
|
||||
_order:
|
||||
newest: "新しい順"
|
||||
@@ -1605,9 +1609,9 @@ _initialTutorial:
|
||||
public: "すべてのユーザーに公開。"
|
||||
home: "ホームタイムラインのみに公開。フォロワー・プロフィールを見に来た人・リノートから、他のユーザーも見ることができます。"
|
||||
followers: "フォロワーにのみ公開。本人以外がリノートすることはできず、またフォロワー以外は閲覧できません。"
|
||||
direct: "指定したユーザーにのみ公開され、また相手に通知が入ります。ダイレクトメッセージのかわりにお使いいただけます。"
|
||||
direct: "指定したユーザーにのみ公開され、また相手に通知が入ります。"
|
||||
doNotSendConfidencialOnDirect1: "機密情報は送信する際は注意してください。"
|
||||
doNotSendConfidencialOnDirect2: "送信先のサーバーの管理者は投稿内容を見ることが可能なので、信頼できないサーバーのユーザーにダイレクト投稿を送信する場合は、機密情報の扱いに注意が必要です。"
|
||||
doNotSendConfidencialOnDirect2: "送信先のサーバーの管理者は投稿内容を見ることが可能なので、信頼できないサーバーのユーザーが含まれる限定公開のノートを作成する際は、機密情報の扱いに注意が必要です。"
|
||||
localOnly: "他のサーバーに投稿を連合しません。上記の公開範囲に関わらず、他のサーバーのユーザーは、この設定がついたノートを直接閲覧することができなくなります。"
|
||||
_cw:
|
||||
title: "内容を隠す(CW)"
|
||||
@@ -2532,7 +2536,7 @@ _visibility:
|
||||
homeDescription: "ホームタイムラインのみに公開"
|
||||
followers: "フォロワー"
|
||||
followersDescription: "自分のフォロワーのみに公開"
|
||||
specified: "ダイレクト"
|
||||
specified: "指名"
|
||||
specifiedDescription: "指定したユーザーのみに公開"
|
||||
disableFederation: "連合なし"
|
||||
disableFederationDescription: "他サーバーへの配信を行いません"
|
||||
@@ -2778,8 +2782,8 @@ _deck:
|
||||
antenna: "アンテナ"
|
||||
list: "リスト"
|
||||
channel: "チャンネル"
|
||||
mentions: "あなた宛て"
|
||||
direct: "ダイレクト"
|
||||
mentions: "メンション"
|
||||
direct: "指名"
|
||||
roleTimeline: "ロールタイムライン"
|
||||
chat: "チャット"
|
||||
|
||||
@@ -3164,6 +3168,7 @@ _bootErrors:
|
||||
otherOption1: "クライアント設定とキャッシュを削除"
|
||||
otherOption2: "簡易クライアントを起動"
|
||||
otherOption3: "修復ツールを起動"
|
||||
otherOption4: "Misskeyをセーフモードで起動"
|
||||
|
||||
_search:
|
||||
searchScopeAll: "全て"
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "misskey",
|
||||
"version": "2025.8.0-alpha.1",
|
||||
"version": "2025.8.0-alpha.4",
|
||||
"codename": "nasubi",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -7,7 +7,7 @@ export class RemoteNotesCleaning1753863104203 {
|
||||
name = 'RemoteNotesCleaning1753863104203'
|
||||
|
||||
async up(queryRunner) {
|
||||
await queryRunner.query(`ALTER TABLE "meta" ADD "enableRemoteNotesCleaning" boolean NOT NULL DEFAULT true`);
|
||||
await queryRunner.query(`ALTER TABLE "meta" ADD "enableRemoteNotesCleaning" boolean NOT NULL DEFAULT false`);
|
||||
await queryRunner.query('ALTER TABLE "meta" ADD "remoteNotesCleaningMaxProcessingDurationInMinutes" integer NOT NULL DEFAULT \'60\'');
|
||||
await queryRunner.query('ALTER TABLE "meta" ADD "remoteNotesCleaningExpiryDaysForEachNotes" integer NOT NULL DEFAULT \'90\'');
|
||||
}
|
||||
|
||||
@@ -8,11 +8,9 @@ export class TweakDefaultFederationSettings1754019326356 {
|
||||
|
||||
async up(queryRunner) {
|
||||
await queryRunner.query(`ALTER TABLE "meta" ALTER COLUMN "federation" SET DEFAULT 'none'`);
|
||||
await queryRunner.query(`ALTER TABLE "meta" ALTER COLUMN "enableRemoteNotesCleaning" SET DEFAULT false`);
|
||||
}
|
||||
|
||||
async down(queryRunner) {
|
||||
await queryRunner.query(`ALTER TABLE "meta" ALTER COLUMN "enableRemoteNotesCleaning" SET DEFAULT true`);
|
||||
await queryRunner.query(`ALTER TABLE "meta" ALTER COLUMN "federation" SET DEFAULT 'all'`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-s3": "3.826.0",
|
||||
"@aws-sdk/lib-storage": "3.826.0",
|
||||
"@discordapp/twemoji": "15.1.0",
|
||||
"@discordapp/twemoji": "16.0.1",
|
||||
"@fastify/accepts": "5.0.2",
|
||||
"@fastify/cookie": "11.0.2",
|
||||
"@fastify/cors": "10.1.0",
|
||||
@@ -93,7 +93,7 @@
|
||||
"@smithy/node-http-handler": "2.5.0",
|
||||
"@swc/cli": "0.7.7",
|
||||
"@swc/core": "1.12.0",
|
||||
"@twemoji/parser": "15.1.1",
|
||||
"@twemoji/parser": "16.0.0",
|
||||
"@types/redis-info": "3.0.3",
|
||||
"accepts": "1.3.8",
|
||||
"ajv": "8.17.1",
|
||||
@@ -135,7 +135,7 @@
|
||||
"jsrsasign": "11.1.0",
|
||||
"juice": "11.0.1",
|
||||
"meilisearch": "0.51.0",
|
||||
"mfm-js": "0.24.0",
|
||||
"mfm-js": "0.25.0",
|
||||
"microformats-parser": "2.0.3",
|
||||
"mime-types": "2.1.35",
|
||||
"misskey-js": "workspace:*",
|
||||
|
||||
@@ -227,9 +227,9 @@ export class SearchService {
|
||||
|
||||
if (opts.host) {
|
||||
if (opts.host === '.') {
|
||||
query.andWhere('user.host IS NULL');
|
||||
query.andWhere('note.userHost IS NULL');
|
||||
} else {
|
||||
query.andWhere('user.host = :host', { host: opts.host });
|
||||
query.andWhere('note.userHost = :host', { host: opts.host });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -188,6 +188,10 @@ export class ClientServerService {
|
||||
'url': 'url',
|
||||
},
|
||||
},
|
||||
'shortcuts': [{
|
||||
'name': 'Safemode',
|
||||
'url': '/?safemode=true',
|
||||
}],
|
||||
};
|
||||
|
||||
manifest = {
|
||||
|
||||
@@ -94,23 +94,37 @@
|
||||
}
|
||||
//#endregion
|
||||
|
||||
//#region Theme
|
||||
const theme = localStorage.getItem('theme');
|
||||
if (theme) {
|
||||
for (const [k, v] of Object.entries(JSON.parse(theme))) {
|
||||
document.documentElement.style.setProperty(`--MI_THEME-${k}`, v.toString());
|
||||
let isSafeMode = (localStorage.getItem('isSafeMode') === 'true');
|
||||
|
||||
// HTMLの theme-color 適用
|
||||
if (k === 'htmlThemeColor') {
|
||||
for (const tag of document.head.children) {
|
||||
if (tag.tagName === 'META' && tag.getAttribute('name') === 'theme-color') {
|
||||
tag.setAttribute('content', v);
|
||||
break;
|
||||
if (!isSafeMode) {
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
|
||||
if (urlParams.has('safemode') && urlParams.get('safemode') === 'true') {
|
||||
localStorage.setItem('isSafeMode', 'true');
|
||||
isSafeMode = true;
|
||||
}
|
||||
}
|
||||
|
||||
//#region Theme
|
||||
if (!isSafeMode) {
|
||||
const theme = localStorage.getItem('theme');
|
||||
if (theme) {
|
||||
for (const [k, v] of Object.entries(JSON.parse(theme))) {
|
||||
document.documentElement.style.setProperty(`--MI_THEME-${k}`, v.toString());
|
||||
|
||||
// HTMLの theme-color 適用
|
||||
if (k === 'htmlThemeColor') {
|
||||
for (const tag of document.head.children) {
|
||||
if (tag.tagName === 'META' && tag.getAttribute('name') === 'theme-color') {
|
||||
tag.setAttribute('content', v);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const colorScheme = localStorage.getItem('colorScheme');
|
||||
if (colorScheme) {
|
||||
document.documentElement.style.setProperty('color-scheme', colorScheme);
|
||||
@@ -127,11 +141,13 @@
|
||||
document.documentElement.classList.add('useSystemFont');
|
||||
}
|
||||
|
||||
const customCss = localStorage.getItem('customCss');
|
||||
if (customCss && customCss.length > 0) {
|
||||
const style = document.createElement('style');
|
||||
style.innerHTML = customCss;
|
||||
document.head.appendChild(style);
|
||||
if (!isSafeMode) {
|
||||
const customCss = localStorage.getItem('customCss');
|
||||
if (customCss && customCss.length > 0) {
|
||||
const style = document.createElement('style');
|
||||
style.innerHTML = customCss;
|
||||
document.head.appendChild(style);
|
||||
}
|
||||
}
|
||||
|
||||
async function addStyle(styleText) {
|
||||
@@ -159,9 +175,13 @@
|
||||
otherOption1: 'Clear preferences and cache',
|
||||
otherOption2: 'Start the simple client',
|
||||
otherOption3: 'Start the repair tool',
|
||||
otherOption4: 'Start Misskey in safe mode',
|
||||
}, locale?._bootErrors || {});
|
||||
const reload = locale?.reload || 'Reload';
|
||||
|
||||
const safeModeUrl = new URL(window.location.href);
|
||||
safeModeUrl.searchParams.set('safemode', 'true');
|
||||
|
||||
let errorsElement = document.getElementById('errors');
|
||||
|
||||
if (!errorsElement) {
|
||||
@@ -182,6 +202,12 @@
|
||||
<p>${messages.solution4}</p>
|
||||
<details style="color: #86b300;">
|
||||
<summary>${messages.otherOption}</summary>
|
||||
<a href="${safeModeUrl}">
|
||||
<button class="button-small">
|
||||
<span class="button-label-small">${messages.otherOption4}</span>
|
||||
</button>
|
||||
</a>
|
||||
<br>
|
||||
<a href="/flush">
|
||||
<button class="button-small">
|
||||
<span class="button-label-small">${messages.otherOption1}</span>
|
||||
|
||||
@@ -34,5 +34,11 @@
|
||||
"text": "text",
|
||||
"url": "url"
|
||||
}
|
||||
}
|
||||
},
|
||||
"shortcuts": [
|
||||
{
|
||||
"name": "Safemode",
|
||||
"url": "/?safemode=true"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -79,6 +79,9 @@ async function createAdmin(host: Host): Promise<Misskey.entities.SignupResponse
|
||||
rateLimitFactor: 0 as never,
|
||||
},
|
||||
}, res.token);
|
||||
await client.request('admin/update-meta', {
|
||||
federation: 'all',
|
||||
}, res.token);
|
||||
return res;
|
||||
}).catch(err => {
|
||||
if (err.info.e.message === 'access denied') return undefined;
|
||||
|
||||
@@ -24,6 +24,7 @@ describe('Endpoints', () => {
|
||||
bob = await signup({ username: 'bob' });
|
||||
carol = await signup({ username: 'carol' });
|
||||
dave = await signup({ username: 'dave' });
|
||||
await api('admin/update-meta', { federation: 'all' }, alice as misskey.entities.SignupResponse);
|
||||
}, 1000 * 60 * 2);
|
||||
|
||||
describe('signup', () => {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
process.env.NODE_ENV = 'test';
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { channel, clip, galleryPost, page, play, post, signup, simpleGet, uploadFile } from '../utils.js';
|
||||
import { api, channel, clip, galleryPost, page, play, post, signup, simpleGet, uploadFile } from '../utils.js';
|
||||
import type { SimpleGetResponse } from '../utils.js';
|
||||
import type * as misskey from 'misskey-js';
|
||||
|
||||
@@ -78,6 +78,7 @@ describe('Webリソース', () => {
|
||||
|
||||
beforeAll(async () => {
|
||||
alice = await signup({ username: 'alice' });
|
||||
await api('admin/update-meta', { federation: 'all' }, alice as misskey.entities.SignupResponse);
|
||||
aliceUploadedFile = (await uploadFile(alice)).body;
|
||||
alicesPost = await post(alice, {
|
||||
text: 'test',
|
||||
|
||||
@@ -16,6 +16,7 @@ describe('FF visibility', () => {
|
||||
beforeAll(async () => {
|
||||
alice = await signup({ username: 'alice' });
|
||||
bob = await signup({ username: 'bob' });
|
||||
await api('admin/update-meta', { federation: 'all' }, alice as misskey.entities.SignupResponse);
|
||||
}, 1000 * 60 * 2);
|
||||
|
||||
test('followingVisibility, followersVisibility がともに public なユーザーのフォロー/フォロワーを誰でも見れる', async () => {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
process.env.NODE_ENV = 'test';
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { host, origin, relativeFetch, signup } from '../utils.js';
|
||||
import { api, host, origin, relativeFetch, signup } from '../utils.js';
|
||||
import type * as misskey from 'misskey-js';
|
||||
|
||||
describe('.well-known', () => {
|
||||
@@ -14,6 +14,7 @@ describe('.well-known', () => {
|
||||
|
||||
beforeAll(async () => {
|
||||
alice = await signup({ username: 'alice' });
|
||||
await api('admin/update-meta', { federation: 'all' }, alice as misskey.entities.SignupResponse);
|
||||
}, 1000 * 60 * 2);
|
||||
|
||||
test('nodeinfo', async () => {
|
||||
|
||||
@@ -10,11 +10,11 @@
|
||||
"lint": "pnpm typecheck && pnpm eslint"
|
||||
},
|
||||
"dependencies": {
|
||||
"@discordapp/twemoji": "15.1.0",
|
||||
"@discordapp/twemoji": "16.0.1",
|
||||
"@rollup/plugin-json": "6.1.0",
|
||||
"@rollup/plugin-replace": "6.0.2",
|
||||
"@rollup/pluginutils": "5.2.0",
|
||||
"@twemoji/parser": "15.1.1",
|
||||
"@twemoji/parser": "16.0.0",
|
||||
"@vitejs/plugin-vue": "5.2.4",
|
||||
"@vue/compiler-sfc": "3.5.17",
|
||||
"astring": "1.9.0",
|
||||
@@ -23,7 +23,7 @@
|
||||
"icons-subsetter": "workspace:*",
|
||||
"frontend-shared": "workspace:*",
|
||||
"json5": "2.2.3",
|
||||
"mfm-js": "0.24.0",
|
||||
"mfm-js": "0.25.0",
|
||||
"misskey-js": "workspace:*",
|
||||
"punycode.js": "2.3.1",
|
||||
"rollup": "4.45.1",
|
||||
|
||||
@@ -23,6 +23,7 @@ export const version = _VERSION_;
|
||||
export const instanceName = (siteName === 'Misskey' || siteName == null) ? host : siteName;
|
||||
export const ui = localStorage.getItem('ui');
|
||||
export const debug = localStorage.getItem('debug') === 'true';
|
||||
export const isSafeMode = localStorage.getItem('isSafeMode') === 'true';
|
||||
|
||||
export function updateLocale(newLocale: Locale): void {
|
||||
locale = newLocale;
|
||||
|
||||
@@ -44,6 +44,8 @@
|
||||
["😑", "expressionless", 0],
|
||||
["😒", "unamused", 0],
|
||||
["🙄", "roll_eyes", 0],
|
||||
["🙂↔️", "head_shaking_horizontally", 0],
|
||||
["🙂↕️", "head_shaking_vertically", 0],
|
||||
["🤔", "thinking", 0],
|
||||
["🤥", "lying_face", 0],
|
||||
["🤭", "hand_over_mouth", 0],
|
||||
@@ -76,6 +78,7 @@
|
||||
["😥", "disappointed_relieved", 0],
|
||||
["🤤", "drooling_face", 0],
|
||||
["😪", "sleepy", 0],
|
||||
["", "face_with_bags_under_eyes", 0],
|
||||
["😓", "sweat", 0],
|
||||
["🥵", "hot", 0],
|
||||
["🥶", "cold", 0],
|
||||
@@ -92,6 +95,7 @@
|
||||
["🥴", "woozy", 0],
|
||||
["🥱", "yawning", 0],
|
||||
["😴", "sleeping", 0],
|
||||
["👁️🗨️", "eye_in_speech_bubble", 0],
|
||||
["💤", "zzz", 0],
|
||||
["😶🌫️", "face_in_clouds", 0],
|
||||
["😮💨", "face_exhaling", 0],
|
||||
@@ -185,6 +189,7 @@
|
||||
["👤", "bust_in_silhouette", 1],
|
||||
["👥", "busts_in_silhouette", 1],
|
||||
["🗣", "speaking_head", 1],
|
||||
["", "fingerprint", 1],
|
||||
["👶", "baby", 1],
|
||||
["🧒", "child", 1],
|
||||
["👦", "boy", 1],
|
||||
@@ -198,8 +203,9 @@
|
||||
["🧑🦰", "red_hair", 1],
|
||||
["👩🦰", "red_hair_woman", 1],
|
||||
["👨🦰", "red_hair_man", 1],
|
||||
["👱", "blonde_person", 1],
|
||||
["👱♀️", "blonde_woman", 1],
|
||||
["👱", "blonde_man", 1],
|
||||
["👱♂️", "blonde_man", 1],
|
||||
["🧑🦳", "white_hair", 1],
|
||||
["👩🦳", "white_hair_woman", 1],
|
||||
["👨🦳", "white_hair_man", 1],
|
||||
@@ -207,21 +213,28 @@
|
||||
["👩🦲", "bald_woman", 1],
|
||||
["👨🦲", "bald_man", 1],
|
||||
["🧔", "bearded_person", 1],
|
||||
["🧔♀️", "beard_woman", 1],
|
||||
["🧔♂️", "beard_man", 1],
|
||||
["🧓", "older_adult", 1],
|
||||
["👴", "older_man", 1],
|
||||
["👵", "older_woman", 1],
|
||||
["👲", "man_with_gua_pi_mao", 1],
|
||||
["🧕", "woman_with_headscarf", 1],
|
||||
["👳", "person_with_turban", 1],
|
||||
["👳♀️", "woman_with_turban", 1],
|
||||
["👳", "man_with_turban", 1],
|
||||
["👳♂️", "man_with_turban", 1],
|
||||
["👮", "police_officer", 1],
|
||||
["👮♀️", "policewoman", 1],
|
||||
["👮", "policeman", 1],
|
||||
["👮♂️", "policeman", 1],
|
||||
["👷", "construction_worker", 1],
|
||||
["👷♀️", "construction_worker_woman", 1],
|
||||
["👷", "construction_worker_man", 1],
|
||||
["👷♂️", "construction_worker_man", 1],
|
||||
["💂", "guard", 1],
|
||||
["💂♀️", "guardswoman", 1],
|
||||
["💂", "guardsman", 1],
|
||||
["💂♂️", "guardsman", 1],
|
||||
["🕵", "detective", 1],
|
||||
["🕵️♀️", "female_detective", 1],
|
||||
["🕵", "male_detective", 1],
|
||||
["🕵️♂️", "male_detective", 1],
|
||||
["🧑⚕️", "health_worker", 1],
|
||||
["👩⚕️", "woman_health_worker", 1],
|
||||
["👨⚕️", "man_health_worker", 1],
|
||||
@@ -270,26 +283,35 @@
|
||||
["🧑⚖️", "judge", 1],
|
||||
["👩⚖️", "woman_judge", 1],
|
||||
["👨⚖️", "man_judge", 1],
|
||||
["🦸", "superhero", 1],
|
||||
["🦸♀️", "woman_superhero", 1],
|
||||
["🦸♂️", "man_superhero", 1],
|
||||
["🦹", "supervillain", 1],
|
||||
["🦹♀️", "woman_supervillain", 1],
|
||||
["🦹♂️", "man_supervillain", 1],
|
||||
["🤶", "mrs_claus", 1],
|
||||
["🧑🎄", "mx_claus", 1],
|
||||
["🎅", "santa", 1],
|
||||
["🥷", "ninja", 1],
|
||||
["🧙", "mage", 1],
|
||||
["🧙♀️", "sorceress", 1],
|
||||
["🧙♂️", "wizard", 1],
|
||||
["🧝", "elf", 1],
|
||||
["🧝♀️", "woman_elf", 1],
|
||||
["🧝♂️", "man_elf", 1],
|
||||
["🧛", "vampire", 1],
|
||||
["🧛♀️", "woman_vampire", 1],
|
||||
["🧛♂️", "man_vampire", 1],
|
||||
["🧟", "zombie", 1],
|
||||
["🧟♀️", "woman_zombie", 1],
|
||||
["🧟♂️", "man_zombie", 1],
|
||||
["🧞", "genie", 1],
|
||||
["🧞♀️", "woman_genie", 1],
|
||||
["🧞♂️", "man_genie", 1],
|
||||
["🧜", "merperson", 1],
|
||||
["🧜♀️", "mermaid", 1],
|
||||
["🧜♂️", "merman", 1],
|
||||
["🧚", "fairy", 1],
|
||||
["🧚♀️", "woman_fairy", 1],
|
||||
["🧚♂️", "man_fairy", 1],
|
||||
["👼", "angel", 1],
|
||||
@@ -305,68 +327,108 @@
|
||||
["👸", "princess", 1],
|
||||
["🤴", "prince", 1],
|
||||
["👰", "person_with_veil", 1],
|
||||
["👰", "bride_with_veil", 1],
|
||||
["👰♀️", "bride_with_veil", 1],
|
||||
["👰♂️", "man_with_veil", 1],
|
||||
["🤵", "person_in_tuxedo", 1],
|
||||
["🤵", "man_in_tuxedo", 1],
|
||||
["🤵♀️", "woman_in_tuxedo", 1],
|
||||
["🤵♂️", "man_in_tuxedo", 1],
|
||||
["🏃", "running_person", 1],
|
||||
["🏃➡️", "running_person_facing_right", 1],
|
||||
["🏃♀️", "running_woman", 1],
|
||||
["🏃", "running_man", 1],
|
||||
["🏃♀️➡️", "running_woman_facing_right", 1],
|
||||
["🏃♂️", "running_man", 1],
|
||||
["🏃♂️➡️", "running_man_facing_right", 1],
|
||||
["🚶", "walking_person", 1],
|
||||
["🚶➡️", "walking_person_facing_right", 1],
|
||||
["🚶♀️", "walking_woman", 1],
|
||||
["🚶", "walking_man", 1],
|
||||
["🚶♀️➡️", "walking_woman_facing_right", 1],
|
||||
["🚶♂️", "walking_man", 1],
|
||||
["🚶♂️➡️", "walking_man_facing_right", 1],
|
||||
["💃", "dancer", 1],
|
||||
["🕺", "man_dancing", 1],
|
||||
["👯", "dancing_women", 1],
|
||||
["👯", "dancing_people", 1],
|
||||
["👯♀️", "dancing_women", 1],
|
||||
["👯♂️", "dancing_men", 1],
|
||||
["👫", "couple", 1],
|
||||
["🧑🤝🧑", "people_holding_hands", 1],
|
||||
["👬", "two_men_holding_hands", 1],
|
||||
["👭", "two_women_holding_hands", 1],
|
||||
["🫂", "people_hugging", 1],
|
||||
["🙇", "bowing_person", 1],
|
||||
["🙇♀️", "bowing_woman", 1],
|
||||
["🙇", "bowing_man", 1],
|
||||
["🙇♂️", "bowing_man", 1],
|
||||
["🤦", "person_facepalming", 1],
|
||||
["🤦♂️", "man_facepalming", 1],
|
||||
["🤦♀️", "woman_facepalming", 1],
|
||||
["🤷", "woman_shrugging", 1],
|
||||
["🤷", "person_shrugging", 1],
|
||||
["🤷♀️", "woman_shrugging", 1],
|
||||
["🤷♂️", "man_shrugging", 1],
|
||||
["💁", "tipping_hand_woman", 1],
|
||||
["💁", "tipping_hand_person", 1],
|
||||
["💁♀️", "tipping_hand_woman", 1],
|
||||
["💁♂️", "tipping_hand_man", 1],
|
||||
["🙅", "no_good_woman", 1],
|
||||
["🙅", "no_good_person", 1],
|
||||
["🙅♀️", "no_good_woman", 1],
|
||||
["🙅♂️", "no_good_man", 1],
|
||||
["🙆", "ok_woman", 1],
|
||||
["🙆", "ok_person", 1],
|
||||
["🙆♀️", "ok_woman", 1],
|
||||
["🙆♂️", "ok_man", 1],
|
||||
["🙋", "raising_hand_woman", 1],
|
||||
["🙋", "raising_hand_person", 1],
|
||||
["🙋♀️", "raising_hand_woman", 1],
|
||||
["🙋♂️", "raising_hand_man", 1],
|
||||
["🙎", "pouting_woman", 1],
|
||||
["🙎", "pouting_person", 1],
|
||||
["🙎♀️", "pouting_woman", 1],
|
||||
["🙎♂️", "pouting_man", 1],
|
||||
["🙍", "frowning_woman", 1],
|
||||
["🙍", "frowning_person", 1],
|
||||
["🙍♀️", "frowning_woman", 1],
|
||||
["🙍♂️", "frowning_man", 1],
|
||||
["💇", "haircut_woman", 1],
|
||||
["💇", "haircut_person", 1],
|
||||
["💇♀️", "haircut_woman", 1],
|
||||
["💇♂️", "haircut_man", 1],
|
||||
["💆", "massage_woman", 1],
|
||||
["💆", "massage_person", 1],
|
||||
["💆♀️", "massage_woman", 1],
|
||||
["💆♂️", "massage_man", 1],
|
||||
["🧖", "person_in_steamy_room", 1],
|
||||
["🧖♀️", "woman_in_steamy_room", 1],
|
||||
["🧖♂️", "man_in_steamy_room", 1],
|
||||
["🧏", "person_deaf", 1],
|
||||
["🧏♀️", "woman_deaf", 1],
|
||||
["🧏♂️", "man_deaf", 1],
|
||||
["🧍", "person_standing", 1],
|
||||
["🧍♀️", "woman_standing", 1],
|
||||
["🧍♂️", "man_standing", 1],
|
||||
["🧎", "person_kneeling", 1],
|
||||
["🧎➡️", "person_kneeling_facing_right", 1],
|
||||
["🧎♀️", "woman_kneeling", 1],
|
||||
["🧎♀️➡️", "woman_kneeling_facing_right", 1],
|
||||
["🧎♂️", "man_kneeling", 1],
|
||||
["🧎♂️➡️", "man_kneeling_facing_right", 1],
|
||||
["🧑🦯", "person_with_probing_cane", 1],
|
||||
["🧑🦯➡️", "person_with_probing_cane_facing_right", 1],
|
||||
["👩🦯", "woman_with_probing_cane", 1],
|
||||
["👩🦯➡️", "woman_with_probing_cane_facing_right", 1],
|
||||
["👨🦯", "man_with_probing_cane", 1],
|
||||
["👨🦯➡️", "man_with_probing_cane_facing_right", 1],
|
||||
["🧑🦼", "person_in_motorized_wheelchair", 1],
|
||||
["🧑🦼➡️", "person_in_motorized_wheelchair_facing_right", 1],
|
||||
["👩🦼", "woman_in_motorized_wheelchair", 1],
|
||||
["👩🦼➡️", "woman_in_motorized_wheelchair_facing_right", 1],
|
||||
["👨🦼", "man_in_motorized_wheelchair", 1],
|
||||
["👨🦼➡️", "man_in_motorized_wheelchair_facing_right", 1],
|
||||
["🧑🦽", "person_in_manual_wheelchair", 1],
|
||||
["🧑🦽➡️", "person_in_manual_wheelchair_facing_right", 1],
|
||||
["👩🦽", "woman_in_manual_wheelchair", 1],
|
||||
["👩🦽➡️", "woman_in_manual_wheelchair_facing_right", 1],
|
||||
["👨🦽", "man_in_manual_wheelchair", 1],
|
||||
["💑", "couple_with_heart_woman_man", 1],
|
||||
["👨🦽➡️", "man_in_manual_wheelchair_facing_right", 1],
|
||||
["💑", "couple_with_heart", 1],
|
||||
["👩❤️👨", "couple_with_heart_woman_man", 1],
|
||||
["👩❤️👩", "couple_with_heart_woman_woman", 1],
|
||||
["👨❤️👨", "couple_with_heart_man_man", 1],
|
||||
["💏", "couplekiss_man_woman", 1],
|
||||
["💏", "couplekiss", 1],
|
||||
["👩❤️💋👨", "couplekiss_woman_man", 1],
|
||||
["👩❤️💋👩", "couplekiss_woman_woman", 1],
|
||||
["👨❤️💋👨", "couplekiss_man_man", 1],
|
||||
["👪", "family_man_woman_boy", 1],
|
||||
["👨👩👦", "family_man_woman_boy", 1],
|
||||
["👨👩👧", "family_man_woman_girl", 1],
|
||||
["👨👩👧👦", "family_man_woman_girl_boy", 1],
|
||||
["👨👩👦👦", "family_man_woman_boy_boy", 1],
|
||||
@@ -391,6 +453,11 @@
|
||||
["👨👧👦", "family_man_girl_boy", 1],
|
||||
["👨👦👦", "family_man_boy_boy", 1],
|
||||
["👨👧👧", "family_man_girl_girl", 1],
|
||||
["👪", "family", 1],
|
||||
["🧑🧑🧒", "family_adult_adult_child", 1],
|
||||
["🧑🧑🧒🧒", "family_adult_adult_child_child", 1],
|
||||
["🧑🧒", "family_adult_child", 1],
|
||||
["🧑🧒🧒", "family_adult_child_child", 1],
|
||||
["🧶", "yarn", 1],
|
||||
["🧵", "thread", 1],
|
||||
["🧥", "coat", 1],
|
||||
@@ -475,6 +542,7 @@
|
||||
["🐦⬛", "black_bird", 2],
|
||||
["🦅", "eagle", 2],
|
||||
["🦉", "owl", 2],
|
||||
["🐦🔥", "phoenix", 2],
|
||||
["🦇", "bat", 2],
|
||||
["🐺", "wolf", 2],
|
||||
["🐗", "boar", 2],
|
||||
@@ -575,6 +643,7 @@
|
||||
["🌿", "herb", 2],
|
||||
["☘", "shamrock", 2],
|
||||
["🍀", "four_leaf_clover", 2],
|
||||
["", "leafless_tree", 2],
|
||||
["🎍", "bamboo", 2],
|
||||
["🎋", "tanabata_tree", 2],
|
||||
["🍃", "leaves", 2],
|
||||
@@ -648,6 +717,7 @@
|
||||
["🪸", "coral", 2],
|
||||
["🪹", "empty_nest", 2],
|
||||
["🪺", "nest_with_eggs", 2],
|
||||
["🍋🟩", "lime", 3],
|
||||
["🍏", "green_apple", 3],
|
||||
["🍎", "apple", 3],
|
||||
["🍐", "pear", 3],
|
||||
@@ -667,6 +737,8 @@
|
||||
["🥑", "avocado", 3],
|
||||
["🫛", "pea_pod", 3],
|
||||
["🥦", "broccoli", 3],
|
||||
["🍄🟫", "brown_mushroom", 3],
|
||||
["", "root_vegetable", 3],
|
||||
["🍅", "tomato", 3],
|
||||
["🍆", "eggplant", 3],
|
||||
["🥒", "cucumber", 3],
|
||||
@@ -786,8 +858,9 @@
|
||||
["🥏", "flying_disc", 4],
|
||||
["🎱", "8ball", 4],
|
||||
["⛳", "golf", 4],
|
||||
["🏌", "golfing_person", 4],
|
||||
["🏌️♀️", "golfing_woman", 4],
|
||||
["🏌", "golfing_man", 4],
|
||||
["🏌️♂️", "golfing_man", 4],
|
||||
["🏓", "ping_pong", 4],
|
||||
["🏸", "badminton", 4],
|
||||
["🥅", "goal_net", 4],
|
||||
@@ -799,10 +872,13 @@
|
||||
["⛷", "skier", 4],
|
||||
["🏂", "snowboarder", 4],
|
||||
["🤺", "person_fencing", 4],
|
||||
["🤼", "people_wrestling", 4],
|
||||
["🤼♀️", "women_wrestling", 4],
|
||||
["🤼♂️", "men_wrestling", 4],
|
||||
["🤸", "person_cartwheeling", 4],
|
||||
["🤸♀️", "woman_cartwheeling", 4],
|
||||
["🤸♂️", "man_cartwheeling", 4],
|
||||
["🤾", "person_playing_handball", 4],
|
||||
["🤾♀️", "woman_playing_handball", 4],
|
||||
["🤾♂️", "man_playing_handball", 4],
|
||||
["⛸", "ice_skate", 4],
|
||||
@@ -813,27 +889,37 @@
|
||||
["🎣", "fishing_pole_and_fish", 4],
|
||||
["🥊", "boxing_glove", 4],
|
||||
["🥋", "martial_arts_uniform", 4],
|
||||
["🚣", "rowing_person", 4],
|
||||
["🚣♀️", "rowing_woman", 4],
|
||||
["🚣", "rowing_man", 4],
|
||||
["🚣♂️", "rowing_man", 4],
|
||||
["🧗", "climbing_person", 4],
|
||||
["🧗♀️", "climbing_woman", 4],
|
||||
["🧗♂️", "climbing_man", 4],
|
||||
["🏊", "swimming_person", 4],
|
||||
["🏊♀️", "swimming_woman", 4],
|
||||
["🏊", "swimming_man", 4],
|
||||
["🏊♂️", "swimming_man", 4],
|
||||
["🤽", "person_playing_water_polo", 4],
|
||||
["🤽♀️", "woman_playing_water_polo", 4],
|
||||
["🤽♂️", "man_playing_water_polo", 4],
|
||||
["🧘", "person_in_lotus_position", 4],
|
||||
["🧘♀️", "woman_in_lotus_position", 4],
|
||||
["🧘♂️", "man_in_lotus_position", 4],
|
||||
["🏄", "surfing_person", 4],
|
||||
["🏄♀️", "surfing_woman", 4],
|
||||
["🏄", "surfing_man", 4],
|
||||
["🏄♂️", "surfing_man", 4],
|
||||
["🛀", "bath", 4],
|
||||
["⛹", "basketball_person", 4],
|
||||
["⛹️♀️", "basketball_woman", 4],
|
||||
["⛹", "basketball_man", 4],
|
||||
["⛹️♂️", "basketball_man", 4],
|
||||
["🏋", "weight_lifting_person", 4],
|
||||
["🏋️♀️", "weight_lifting_woman", 4],
|
||||
["🏋", "weight_lifting_man", 4],
|
||||
["🏋️♂️", "weight_lifting_man", 4],
|
||||
["🚴", "biking_person", 4],
|
||||
["🚴♀️", "biking_woman", 4],
|
||||
["🚴", "biking_man", 4],
|
||||
["🚴♂️", "biking_man", 4],
|
||||
["🚵", "mountain_biking_person", 4],
|
||||
["🚵♀️", "mountain_biking_woman", 4],
|
||||
["🚵", "mountain_biking_man", 4],
|
||||
["🚵♂️", "mountain_biking_man", 4],
|
||||
["🏇", "horse_racing", 4],
|
||||
["🤿", "diving_mask", 4],
|
||||
["🪀", "yo_yo", 4],
|
||||
@@ -856,6 +942,7 @@
|
||||
["🎭", "performing_arts", 4],
|
||||
["🎨", "art", 4],
|
||||
["🎪", "circus_tent", 4],
|
||||
["🤹", "person_juggling", 4],
|
||||
["🤹♀️", "woman_juggling", 4],
|
||||
["🤹♂️", "man_juggling", 4],
|
||||
["🎤", "microphone", 4],
|
||||
@@ -872,6 +959,7 @@
|
||||
["🪕", "banjo", 4],
|
||||
["🪗", "accordion", 4],
|
||||
["🪘", "long_drum", 4],
|
||||
["", "harp", 4],
|
||||
["🎬", "clapper", 4],
|
||||
["🎮", "video_game", 4],
|
||||
["👾", "space_invader", 4],
|
||||
@@ -1076,8 +1164,10 @@
|
||||
["🪙", "coin", 6],
|
||||
["💳", "credit_card", 6],
|
||||
["🪪", "identification_card", 6],
|
||||
["🥾", "hiking_boot", 6],
|
||||
["💎", "gem", 6],
|
||||
["⚖", "balance_scale", 6],
|
||||
["⛓️💥", "broken_chain", 6],
|
||||
["🧰", "toolbox", 6],
|
||||
["🔧", "wrench", 6],
|
||||
["🔨", "hammer", 6],
|
||||
@@ -1093,6 +1183,7 @@
|
||||
["🪛", "screwdriver", 6],
|
||||
["🪝", "hook", 6],
|
||||
["🪜", "ladder", 6],
|
||||
["", "shovel", 6],
|
||||
["🧱", "brick", 6],
|
||||
["⛓", "chains", 6],
|
||||
["🧲", "magnet", 6],
|
||||
@@ -1304,6 +1395,8 @@
|
||||
["♓", "pisces", 7],
|
||||
["🆔", "id", 7],
|
||||
["⚛", "atom_symbol", 7],
|
||||
["♀️", "female_sign", 7],
|
||||
["♂️", "male_sign", 7],
|
||||
["⚧️", "transgender_symbol", 7],
|
||||
["🈳", "u7a7a", 7],
|
||||
["🈹", "u5272", 7],
|
||||
@@ -1463,9 +1556,11 @@
|
||||
["♾", "infinity", 7],
|
||||
["💲", "heavy_dollar_sign", 7],
|
||||
["💱", "currency_exchange", 7],
|
||||
["⚕️", "medical_symbol", 7],
|
||||
["©️", "copyright", 7],
|
||||
["®️", "registered", 7],
|
||||
["™️", "tm", 7],
|
||||
["", "splatter", 7],
|
||||
["🔚", "end", 7],
|
||||
["🔙", "back", 7],
|
||||
["🔛", "on", 7],
|
||||
@@ -1576,6 +1671,7 @@
|
||||
["🇧🇲", "bermuda", 8],
|
||||
["🇧🇹", "bhutan", 8],
|
||||
["🇧🇴", "bolivia", 8],
|
||||
["🇧🇻", "bouvet_island", 8],
|
||||
["🇧🇶", "caribbean_netherlands", 8],
|
||||
["🇧🇦", "bosnia_herzegovina", 8],
|
||||
["🇧🇼", "botswana", 8],
|
||||
@@ -1593,10 +1689,12 @@
|
||||
["🇮🇨", "canary_islands", 8],
|
||||
["🇰🇾", "cayman_islands", 8],
|
||||
["🇨🇫", "central_african_republic", 8],
|
||||
["🇪🇦", "ceuta_melilla", 8],
|
||||
["🇹🇩", "chad", 8],
|
||||
["🇨🇱", "chile", 8],
|
||||
["🇨🇳", "cn", 8],
|
||||
["🇨🇽", "christmas_island", 8],
|
||||
["🇨🇵", "clipperton_island", 8],
|
||||
["🇨🇨", "cocos_islands", 8],
|
||||
["🇨🇴", "colombia", 8],
|
||||
["🇰🇲", "comoros", 8],
|
||||
@@ -1610,6 +1708,7 @@
|
||||
["🇨🇾", "cyprus", 8],
|
||||
["🇨🇿", "czech_republic", 8],
|
||||
["🇩🇰", "denmark", 8],
|
||||
["🇩🇬", "diego_garcia", 8],
|
||||
["🇩🇯", "djibouti", 8],
|
||||
["🇩🇲", "dominica", 8],
|
||||
["🇩🇴", "dominican_republic", 8],
|
||||
@@ -1646,6 +1745,7 @@
|
||||
["🇬🇼", "guinea_bissau", 8],
|
||||
["🇬🇾", "guyana", 8],
|
||||
["🇭🇹", "haiti", 8],
|
||||
["🇭🇲", "heard_mcdonald_islands", 8],
|
||||
["🇭🇳", "honduras", 8],
|
||||
["🇭🇰", "hong_kong", 8],
|
||||
["🇭🇺", "hungary", 8],
|
||||
@@ -1733,10 +1833,12 @@
|
||||
["🇷🇴", "romania", 8],
|
||||
["🇷🇺", "ru", 8],
|
||||
["🇷🇼", "rwanda", 8],
|
||||
["🇨🇶", "sark", 8],
|
||||
["🇧🇱", "st_barthelemy", 8],
|
||||
["🇸🇭", "st_helena", 8],
|
||||
["🇰🇳", "st_kitts_nevis", 8],
|
||||
["🇱🇨", "st_lucia", 8],
|
||||
["🇲🇫", "st_martin", 8],
|
||||
["🇵🇲", "st_pierre_miquelon", 8],
|
||||
["🇻🇨", "st_vincent_grenadines", 8],
|
||||
["🇼🇸", "samoa", 8],
|
||||
@@ -1762,6 +1864,7 @@
|
||||
["🇸🇩", "sudan", 8],
|
||||
["🇸🇷", "suriname", 8],
|
||||
["🇸🇿", "swaziland", 8],
|
||||
["🇸🇯", "svalbard_jan_mayen", 8],
|
||||
["🇸🇪", "sweden", 8],
|
||||
["🇨🇭", "switzerland", 8],
|
||||
["🇸🇾", "syria", 8],
|
||||
@@ -1788,6 +1891,7 @@
|
||||
["🏴", "scotland", 8],
|
||||
["🏴", "wales", 8],
|
||||
["🇺🇸", "us", 8],
|
||||
["🇺🇲", "us_outlying_islands", 8],
|
||||
["🇻🇮", "us_virgin_islands", 8],
|
||||
["🇺🇾", "uruguay", 8],
|
||||
["🇺🇿", "uzbekistan", 8],
|
||||
|
||||
@@ -34,7 +34,7 @@ export const commonHandlers = [
|
||||
}),
|
||||
http.get('/twemoji/:codepoints.svg', async ({ params }) => {
|
||||
const { codepoints } = params;
|
||||
const value = await fetch(`https://unpkg.com/@discordapp/twemoji@15.0.2/dist/svg/${codepoints}.svg`).then((response) => response.blob());
|
||||
const value = await fetch(`https://unpkg.com/@discordapp/twemoji@16.0.1/dist/svg/${codepoints}.svg`).then((response) => response.blob());
|
||||
return new HttpResponse(value, {
|
||||
headers: {
|
||||
'Content-Type': 'image/svg+xml',
|
||||
|
||||
@@ -39,6 +39,7 @@ export interface SearchIndexItem {
|
||||
path?: string;
|
||||
label: string;
|
||||
keywords: string[];
|
||||
texts: string[];
|
||||
icon?: string;
|
||||
inlining?: string[];
|
||||
}
|
||||
@@ -227,14 +228,14 @@ function extractElementText2Inner(node: TemplateChildNode, processingNodeName: s
|
||||
// region extractUsageInfoFromTemplateAst
|
||||
|
||||
/**
|
||||
* SearchLabel/SearchKeyword/SearchIconを探して抽出する関数
|
||||
* SearchLabel/SearchText/SearchIconを探して抽出する関数
|
||||
*/
|
||||
function extractSugarTags(nodes: TemplateChildNode[], id: string): { label: string | null, keywords: string[], icon: string | null } {
|
||||
function extractSugarTags(nodes: TemplateChildNode[], id: string): { label: string | null; texts: string[]; icon: string | null; } {
|
||||
let label: string | null | undefined = undefined;
|
||||
let icon: string | null | undefined = undefined;
|
||||
const keywords: string[] = [];
|
||||
const texts: string[] = [];
|
||||
|
||||
logger.info(`Extracting labels and keywords from ${nodes.length} nodes`);
|
||||
logger.info(`Extracting labels and texts from ${nodes.length} nodes`);
|
||||
|
||||
walkVueElements(nodes, null, (node) => {
|
||||
switch (node.tag) {
|
||||
@@ -248,10 +249,10 @@ function extractSugarTags(nodes: TemplateChildNode[], id: string): { label: stri
|
||||
|
||||
label = extractElementText(node, id);
|
||||
return;
|
||||
case 'SearchKeyword':
|
||||
case 'SearchText':
|
||||
const content = extractElementText(node, id);
|
||||
if (content) {
|
||||
keywords.push(content);
|
||||
texts.push(content);
|
||||
}
|
||||
return;
|
||||
case 'SearchIcon':
|
||||
@@ -278,8 +279,8 @@ function extractSugarTags(nodes: TemplateChildNode[], id: string): { label: stri
|
||||
});
|
||||
|
||||
// デバッグ情報
|
||||
logger.info(`Extraction completed: label=${label}, keywords=[${keywords.join(', ')}, icon=${icon}]`);
|
||||
return { label: label ?? null, keywords, icon: icon ?? null };
|
||||
logger.info(`Extraction completed: label=${label}, text=[${texts.join(', ')}, icon=${icon}]`);
|
||||
return { label: label ?? null, texts, icon: icon ?? null };
|
||||
}
|
||||
|
||||
function getStringProp(attr: AttributeNode | DirectiveNode | null, id: string): string | null {
|
||||
@@ -351,33 +352,36 @@ function extractUsageInfoFromTemplateAst(
|
||||
parentId: parentId ?? undefined,
|
||||
label: '', // デフォルト値
|
||||
keywords: [],
|
||||
texts: [],
|
||||
};
|
||||
|
||||
// バインドプロパティを取得
|
||||
const path = getStringProp(findAttribute(node.props, 'path'), id)
|
||||
const icon = getStringProp(findAttribute(node.props, 'icon'), id)
|
||||
const label = getStringProp(findAttribute(node.props, 'label'), id)
|
||||
const inlining = getStringArrayProp(findAttribute(node.props, 'inlining'), id)
|
||||
const keywords = getStringArrayProp(findAttribute(node.props, 'keywords'), id)
|
||||
const path = getStringProp(findAttribute(node.props, 'path'), id);
|
||||
const icon = getStringProp(findAttribute(node.props, 'icon'), id);
|
||||
const label = getStringProp(findAttribute(node.props, 'label'), id);
|
||||
const inlining = getStringArrayProp(findAttribute(node.props, 'inlining'), id);
|
||||
const keywords = getStringArrayProp(findAttribute(node.props, 'keywords'), id);
|
||||
const texts = getStringArrayProp(findAttribute(node.props, 'texts'), id);
|
||||
|
||||
if (path) markerInfo.path = path;
|
||||
if (icon) markerInfo.icon = icon;
|
||||
if (label) markerInfo.label = label;
|
||||
if (inlining) markerInfo.inlining = inlining;
|
||||
if (keywords) markerInfo.keywords = keywords;
|
||||
if (texts) markerInfo.texts = texts;
|
||||
|
||||
//pathがない場合はファイルパスを設定
|
||||
// pathがない場合はファイルパスを設定
|
||||
if (markerInfo.path == null && parentId == null) {
|
||||
markerInfo.path = id.match(/.*(\/(admin|settings)\/[^\/]+)\.vue$/)?.[1];
|
||||
}
|
||||
|
||||
// SearchLabelとSearchKeywordを抽出 (AST全体を探索)
|
||||
// SearchLabelとSearchTextを抽出 (AST全体を探索)
|
||||
{
|
||||
const extracted = extractSugarTags(node.children, id);
|
||||
if (extracted.label && markerInfo.label) logger.warn(`Duplicate label found for ${markerId} at ${id}:${node.loc.start.line}`);
|
||||
if (extracted.icon && markerInfo.icon) logger.warn(`Duplicate icon found for ${markerId} at ${id}:${node.loc.start.line}`);
|
||||
markerInfo.label = extracted.label ?? markerInfo.label ?? '';
|
||||
markerInfo.keywords = [...extracted.keywords, ...markerInfo.keywords];
|
||||
markerInfo.texts = [...extracted.texts, ...markerInfo.texts];
|
||||
markerInfo.icon = extracted.icon ?? markerInfo.icon ?? undefined;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@analytics/google-analytics": "1.1.0",
|
||||
"@discordapp/twemoji": "15.1.0",
|
||||
"@discordapp/twemoji": "16.0.1",
|
||||
"@github/webauthn-json": "2.1.1",
|
||||
"@mcaptcha/vanilla-glue": "0.1.0-alpha-3",
|
||||
"@misskey-dev/browser-image-resizer": "2024.1.0",
|
||||
@@ -26,7 +26,7 @@
|
||||
"@rollup/pluginutils": "5.2.0",
|
||||
"@sentry/vue": "9.39.0",
|
||||
"@syuilo/aiscript": "0.19.0",
|
||||
"@twemoji/parser": "15.1.1",
|
||||
"@twemoji/parser": "16.0.0",
|
||||
"@vitejs/plugin-vue": "5.2.4",
|
||||
"@vue/compiler-sfc": "3.5.17",
|
||||
"aiscript-vscode": "github:aiscript-dev/aiscript-vscode#v0.1.15",
|
||||
@@ -54,7 +54,7 @@
|
||||
"json5": "2.2.3",
|
||||
"magic-string": "0.30.17",
|
||||
"matter-js": "0.20.0",
|
||||
"mfm-js": "0.24.0",
|
||||
"mfm-js": "0.25.0",
|
||||
"misskey-bubble-game": "workspace:*",
|
||||
"misskey-js": "workspace:*",
|
||||
"misskey-reversi": "workspace:*",
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import { computed, watch, version as vueVersion } from 'vue';
|
||||
import { compareVersions } from 'compare-versions';
|
||||
import { version, lang, updateLocale, locale, apiUrl } from '@@/js/config.js';
|
||||
import { version, lang, updateLocale, locale, apiUrl, isSafeMode } from '@@/js/config.js';
|
||||
import defaultLightTheme from '@@/themes/l-light.json5';
|
||||
import defaultDarkTheme from '@@/themes/d-green-lime.json5';
|
||||
import type { App } from 'vue';
|
||||
@@ -168,28 +168,35 @@ export async function common(createVue: () => Promise<App<Element>>) {
|
||||
|
||||
// NOTE: この処理は必ずクライアント更新チェック処理より後に来ること(テーマ再構築のため)
|
||||
watch(store.r.darkMode, (darkMode) => {
|
||||
applyTheme(darkMode
|
||||
? (prefer.s.darkTheme ?? defaultDarkTheme)
|
||||
: (prefer.s.lightTheme ?? defaultLightTheme),
|
||||
);
|
||||
}, { immediate: miLocalStorage.getItem('theme') == null });
|
||||
const theme = (() => {
|
||||
if (darkMode) {
|
||||
return isSafeMode ? defaultDarkTheme : (prefer.s.darkTheme ?? defaultDarkTheme);
|
||||
} else {
|
||||
return isSafeMode ? defaultLightTheme : (prefer.s.lightTheme ?? defaultLightTheme);
|
||||
}
|
||||
})();
|
||||
|
||||
applyTheme(theme);
|
||||
}, { immediate: isSafeMode || miLocalStorage.getItem('theme') == null });
|
||||
|
||||
window.document.documentElement.dataset.colorScheme = store.s.darkMode ? 'dark' : 'light';
|
||||
|
||||
const darkTheme = prefer.model('darkTheme');
|
||||
const lightTheme = prefer.model('lightTheme');
|
||||
if (!isSafeMode) {
|
||||
const darkTheme = prefer.model('darkTheme');
|
||||
const lightTheme = prefer.model('lightTheme');
|
||||
|
||||
watch(darkTheme, (theme) => {
|
||||
if (store.s.darkMode) {
|
||||
applyTheme(theme ?? defaultDarkTheme);
|
||||
}
|
||||
});
|
||||
watch(darkTheme, (theme) => {
|
||||
if (store.s.darkMode) {
|
||||
applyTheme(theme ?? defaultDarkTheme);
|
||||
}
|
||||
});
|
||||
|
||||
watch(lightTheme, (theme) => {
|
||||
if (!store.s.darkMode) {
|
||||
applyTheme(theme ?? defaultLightTheme);
|
||||
}
|
||||
});
|
||||
watch(lightTheme, (theme) => {
|
||||
if (!store.s.darkMode) {
|
||||
applyTheme(theme ?? defaultLightTheme);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//#region Sync dark mode
|
||||
if (prefer.s.syncDeviceDarkMode) {
|
||||
@@ -203,17 +210,19 @@ export async function common(createVue: () => Promise<App<Element>>) {
|
||||
});
|
||||
//#endregion
|
||||
|
||||
if (prefer.s.darkTheme && store.s.darkMode) {
|
||||
if (miLocalStorage.getItem('themeId') !== prefer.s.darkTheme.id) applyTheme(prefer.s.darkTheme);
|
||||
} else if (prefer.s.lightTheme && !store.s.darkMode) {
|
||||
if (miLocalStorage.getItem('themeId') !== prefer.s.lightTheme.id) applyTheme(prefer.s.lightTheme);
|
||||
}
|
||||
if (!isSafeMode) {
|
||||
if (prefer.s.darkTheme && store.s.darkMode) {
|
||||
if (miLocalStorage.getItem('themeId') !== prefer.s.darkTheme.id) applyTheme(prefer.s.darkTheme);
|
||||
} else if (prefer.s.lightTheme && !store.s.darkMode) {
|
||||
if (miLocalStorage.getItem('themeId') !== prefer.s.lightTheme.id) applyTheme(prefer.s.lightTheme);
|
||||
}
|
||||
|
||||
fetchInstanceMetaPromise.then(() => {
|
||||
// TODO: instance.defaultLightTheme/instance.defaultDarkThemeが不正な形式だった場合のケア
|
||||
if (prefer.s.lightTheme == null && instance.defaultLightTheme != null) prefer.commit('lightTheme', JSON.parse(instance.defaultLightTheme));
|
||||
if (prefer.s.darkTheme == null && instance.defaultDarkTheme != null) prefer.commit('darkTheme', JSON.parse(instance.defaultDarkTheme));
|
||||
});
|
||||
fetchInstanceMetaPromise.then(() => {
|
||||
// TODO: instance.defaultLightTheme/instance.defaultDarkThemeが不正な形式だった場合のケア
|
||||
if (prefer.s.lightTheme == null && instance.defaultLightTheme != null) prefer.commit('lightTheme', JSON.parse(instance.defaultLightTheme));
|
||||
if (prefer.s.darkTheme == null && instance.defaultDarkTheme != null) prefer.commit('darkTheme', JSON.parse(instance.defaultDarkTheme));
|
||||
});
|
||||
}
|
||||
|
||||
watch(prefer.r.overridedDeviceKind, (kind) => {
|
||||
updateDeviceKind(kind);
|
||||
|
||||
@@ -28,8 +28,8 @@ import { addCustomEmoji, removeCustomEmojis, updateCustomEmojis } from '@/custom
|
||||
import { prefer } from '@/preferences.js';
|
||||
import { launchPlugins } from '@/plugin.js';
|
||||
import { updateCurrentAccountPartial } from '@/accounts.js';
|
||||
import { signout } from '@/signout.js';
|
||||
import { migrateOldSettings } from '@/pref-migrate.js';
|
||||
import { unisonReload } from '@/utility/unison-reload.js';
|
||||
|
||||
export async function mainBoot() {
|
||||
const { isClientUpdated, lastVersion } = await common(async () => {
|
||||
@@ -391,6 +391,8 @@ export async function mainBoot() {
|
||||
}
|
||||
|
||||
// shortcut
|
||||
let safemodeRequestCount = 0;
|
||||
let safemodeRequestTimer: number | null = null;
|
||||
const keymap = {
|
||||
'p|n': () => {
|
||||
if ($i == null) return;
|
||||
@@ -402,6 +404,24 @@ export async function mainBoot() {
|
||||
's': () => {
|
||||
mainRouter.push('/search');
|
||||
},
|
||||
'g': {
|
||||
callback: () => {
|
||||
// mを5回押すとセーフモードに入る
|
||||
safemodeRequestCount++;
|
||||
if (safemodeRequestCount >= 5) {
|
||||
miLocalStorage.setItem('isSafeMode', 'true');
|
||||
unisonReload();
|
||||
} else {
|
||||
if (safemodeRequestTimer != null) {
|
||||
window.clearTimeout(safemodeRequestTimer);
|
||||
}
|
||||
safemodeRequestTimer = window.setTimeout(() => {
|
||||
safemodeRequestCount = 0;
|
||||
}, 300);
|
||||
}
|
||||
},
|
||||
allowRepeat: true,
|
||||
}
|
||||
} as const satisfies Keymap;
|
||||
window.document.addEventListener('keydown', makeHotkey(keymap), { passive: false });
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
@contextmenu.stop
|
||||
@keydown.stop
|
||||
>
|
||||
<button v-if="hide" :class="$style.hidden" @click="show">
|
||||
<button v-if="hide" :class="$style.hidden" @click="reveal">
|
||||
<div :class="$style.hiddenTextWrapper">
|
||||
<b v-if="audio.isSensitive" style="display: block;"><i class="ti ti-eye-exclamation"></i> {{ i18n.ts.sensitive }}{{ prefer.s.dataSaver.media ? ` (${i18n.ts.audio}${audio.size ? ' ' + bytes(audio.size) : ''})` : '' }}</b>
|
||||
<b v-else style="display: block;"><i class="ti ti-music"></i> {{ prefer.s.dataSaver.media && audio.size ? bytes(audio.size) : i18n.ts.audio }}</b>
|
||||
@@ -157,7 +157,7 @@ const audioEl = useTemplateRef('audioEl');
|
||||
// eslint-disable-next-line vue/no-setup-props-reactivity-loss
|
||||
const hide = ref((prefer.s.nsfw === 'force' || prefer.s.dataSaver.media) ? true : (props.audio.isSensitive && prefer.s.nsfw !== 'ignore'));
|
||||
|
||||
async function show() {
|
||||
async function reveal() {
|
||||
if (props.audio.isSensitive && prefer.s.confirmWhenRevealingSensitiveMedia) {
|
||||
const { canceled } = await os.confirm({
|
||||
type: 'question',
|
||||
|
||||
@@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<template>
|
||||
<div :class="$style.root">
|
||||
<MkMediaAudio v-if="media.type.startsWith('audio') && media.type !== 'audio/midi'" :audio="media"/>
|
||||
<div v-else-if="media.isSensitive && hide" :class="$style.sensitive" @click="show">
|
||||
<div v-else-if="media.isSensitive && hide" :class="$style.sensitive" @click="reveal">
|
||||
<span style="font-size: 1.6em;"><i class="ti ti-alert-triangle"></i></span>
|
||||
<b>{{ i18n.ts.sensitive }}</b>
|
||||
<span>{{ i18n.ts.clickToShow }}</span>
|
||||
@@ -37,7 +37,7 @@ const props = defineProps<{
|
||||
|
||||
const hide = ref(true);
|
||||
|
||||
async function show() {
|
||||
async function reveal() {
|
||||
if (props.media.isSensitive && prefer.s.confirmWhenRevealingSensitiveMedia) {
|
||||
const { canceled } = await os.confirm({
|
||||
type: 'question',
|
||||
|
||||
@@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div :class="[hide ? $style.hidden : $style.visible, (image.isSensitive && prefer.s.highlightSensitiveMedia) && $style.sensitive]" @click="onclick">
|
||||
<div :class="[hide ? $style.hidden : $style.visible, (image.isSensitive && prefer.s.highlightSensitiveMedia) && $style.sensitive]" @click="reveal">
|
||||
<component
|
||||
:is="disableImageLink ? 'div' : 'a'"
|
||||
v-bind="disableImageLink ? {
|
||||
@@ -96,10 +96,10 @@ const url = computed(() => (props.raw || prefer.s.loadRawImages)
|
||||
? props.image.url
|
||||
: prefer.s.disableShowingAnimatedImages
|
||||
? getStaticImageUrl(props.image.url)
|
||||
: props.image.thumbnailUrl,
|
||||
: props.image.thumbnailUrl!,
|
||||
);
|
||||
|
||||
async function onclick(ev: MouseEvent) {
|
||||
async function reveal(ev: MouseEvent) {
|
||||
if (!props.controls) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
@contextmenu.stop
|
||||
@keydown.stop
|
||||
>
|
||||
<button v-if="hide" :class="$style.hidden" @click="show">
|
||||
<button v-if="hide" :class="$style.hidden" @click="reveal">
|
||||
<div :class="$style.hiddenTextWrapper">
|
||||
<b v-if="video.isSensitive" style="display: block;"><i class="ti ti-eye-exclamation"></i> {{ i18n.ts.sensitive }}{{ prefer.s.dataSaver.media ? ` (${i18n.ts.video}${video.size ? ' ' + bytes(video.size) : ''})` : '' }}</b>
|
||||
<b v-else style="display: block;"><i class="ti ti-movie"></i> {{ prefer.s.dataSaver.media && video.size ? bytes(video.size) : i18n.ts.video }}</b>
|
||||
@@ -178,7 +178,7 @@ function hasFocus() {
|
||||
// eslint-disable-next-line vue/no-setup-props-reactivity-loss
|
||||
const hide = ref((prefer.s.nsfw === 'force' || prefer.s.dataSaver.media) ? true : (props.video.isSensitive && prefer.s.nsfw !== 'ignore'));
|
||||
|
||||
async function show() {
|
||||
async function reveal() {
|
||||
if (props.video.isSensitive && prefer.s.confirmWhenRevealingSensitiveMedia) {
|
||||
const { canceled } = await os.confirm({
|
||||
type: 'question',
|
||||
|
||||
@@ -52,9 +52,9 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
{{ item.label }}
|
||||
</template>
|
||||
<template v-else>
|
||||
<span style="opacity: 0.7; font-size: 90%;">{{ item.parentLabels.join(' > ') }}</span>
|
||||
<span style="opacity: 0.7; font-size: 90%; word-break: break-word;">{{ item.parentLabels.join(' > ') }}</span>
|
||||
<br>
|
||||
<span>{{ item.label }}</span>
|
||||
<span style="word-break: break-word;">{{ item.label }}</span>
|
||||
</template>
|
||||
</span>
|
||||
</MkA>
|
||||
@@ -95,7 +95,7 @@ export type SuperMenuDef = {
|
||||
<script lang="ts" setup>
|
||||
import { useTemplateRef, ref, watch, nextTick, computed } from 'vue';
|
||||
import { getScrollContainer } from '@@/js/scroll.js';
|
||||
import type { SearchIndexItem } from '@/utility/settings-search-index.js';
|
||||
import type { SearchIndexItem } from '@/utility/inapp-search.js';
|
||||
import MkInput from '@/components/MkInput.vue';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import { useRouter } from '@/router.js';
|
||||
@@ -165,12 +165,28 @@ watch(rawSearchQuery, (value) => {
|
||||
});
|
||||
};
|
||||
|
||||
for (const item of searchIndexItemById.values()) {
|
||||
if (
|
||||
compareStringIncludes(item.label, value) ||
|
||||
item.keywords.some((x) => compareStringIncludes(x, value))
|
||||
) {
|
||||
// label, keywords, texts の順に優先して表示
|
||||
|
||||
let items = Array.from(searchIndexItemById.values());
|
||||
|
||||
for (const item of items) {
|
||||
if (compareStringIncludes(item.label, value)) {
|
||||
addSearchResult(item);
|
||||
items = items.filter(i => i.id !== item.id);
|
||||
}
|
||||
}
|
||||
|
||||
for (const item of items) {
|
||||
if (item.keywords.some((x) => compareStringIncludes(x, value))) {
|
||||
addSearchResult(item);
|
||||
items = items.filter(i => i.id !== item.id);
|
||||
}
|
||||
}
|
||||
|
||||
for (const item of items) {
|
||||
if (item.texts.some((x) => compareStringIncludes(x, value))) {
|
||||
addSearchResult(item);
|
||||
items = items.filter(i => i.id !== item.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
<!--
|
||||
SPDX-FileCopyrightText: syuilo and misskey-project
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
-->
|
||||
|
||||
<template>
|
||||
<slot></slot>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
</script>
|
||||
|
||||
<style lang="scss" module>
|
||||
</style>
|
||||
@@ -31,7 +31,7 @@ import PageWithHeader from './global/PageWithHeader.vue';
|
||||
import PageWithAnimBg from './global/PageWithAnimBg.vue';
|
||||
import SearchMarker from './global/SearchMarker.vue';
|
||||
import SearchLabel from './global/SearchLabel.vue';
|
||||
import SearchKeyword from './global/SearchKeyword.vue';
|
||||
import SearchText from './global/SearchText.vue';
|
||||
import SearchIcon from './global/SearchIcon.vue';
|
||||
|
||||
import type { App } from 'vue';
|
||||
@@ -71,7 +71,7 @@ export const components = {
|
||||
PageWithAnimBg: PageWithAnimBg,
|
||||
SearchMarker: SearchMarker,
|
||||
SearchLabel: SearchLabel,
|
||||
SearchKeyword: SearchKeyword,
|
||||
SearchText: SearchText,
|
||||
SearchIcon: SearchIcon,
|
||||
};
|
||||
|
||||
@@ -105,7 +105,7 @@ declare module '@vue/runtime-core' {
|
||||
PageWithAnimBg: typeof PageWithAnimBg;
|
||||
SearchMarker: typeof SearchMarker;
|
||||
SearchLabel: typeof SearchLabel;
|
||||
SearchKeyword: typeof SearchKeyword;
|
||||
SearchText: typeof SearchText;
|
||||
SearchIcon: typeof SearchIcon;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ export type Keys = (
|
||||
'preferences' |
|
||||
'latestPreferencesUpdate' |
|
||||
'hidePreferencesRestoreSuggestion' |
|
||||
'isSafeMode' |
|
||||
`miux:${string}` |
|
||||
`ui:folder:${string}` |
|
||||
`themes:${string}` | // DEPRECATED
|
||||
|
||||
@@ -4,158 +4,161 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
-->
|
||||
|
||||
<template>
|
||||
<MkFolder>
|
||||
<template #icon><i class="ti ti-shield"></i></template>
|
||||
<template #label>{{ i18n.ts.botProtection }}</template>
|
||||
<template v-if="botProtectionForm.savedState.provider === 'hcaptcha'" #suffix>hCaptcha</template>
|
||||
<template v-else-if="botProtectionForm.savedState.provider === 'mcaptcha'" #suffix>mCaptcha</template>
|
||||
<template v-else-if="botProtectionForm.savedState.provider === 'recaptcha'" #suffix>reCAPTCHA</template>
|
||||
<template v-else-if="botProtectionForm.savedState.provider === 'turnstile'" #suffix>Turnstile</template>
|
||||
<template v-else-if="botProtectionForm.savedState.provider === 'testcaptcha'" #suffix>testCaptcha</template>
|
||||
<template v-else #suffix>{{ i18n.ts.none }} ({{ i18n.ts.notRecommended }})</template>
|
||||
<template #footer>
|
||||
<MkFormFooter :canSaving="canSaving" :form="botProtectionForm"/>
|
||||
</template>
|
||||
<SearchMarker markerId="botProtection" :keywords="['bot', 'protection', 'captcha', 'hcaptcha', 'mcaptcha', 'recaptcha', 'turnstile']">
|
||||
<MkFolder>
|
||||
<template #icon><SearchIcon><i class="ti ti-shield"></i></SearchIcon></template>
|
||||
<template #label><SearchLabel>{{ i18n.ts.botProtection }}</SearchLabel></template>
|
||||
<template v-if="botProtectionForm.savedState.provider === 'hcaptcha'" #suffix>hCaptcha</template>
|
||||
<template v-else-if="botProtectionForm.savedState.provider === 'mcaptcha'" #suffix>mCaptcha</template>
|
||||
<template v-else-if="botProtectionForm.savedState.provider === 'recaptcha'" #suffix>reCAPTCHA</template>
|
||||
<template v-else-if="botProtectionForm.savedState.provider === 'turnstile'" #suffix>Turnstile</template>
|
||||
<template v-else-if="botProtectionForm.savedState.provider === 'testcaptcha'" #suffix>testCaptcha</template>
|
||||
<template v-else #suffix>{{ i18n.ts.none }} ({{ i18n.ts.notRecommended }})</template>
|
||||
<template #footer>
|
||||
<MkFormFooter :canSaving="canSaving" :form="botProtectionForm"/>
|
||||
</template>
|
||||
|
||||
<div class="_gaps_m">
|
||||
<MkRadios v-model="botProtectionForm.state.provider">
|
||||
<option value="none">{{ i18n.ts.none }} ({{ i18n.ts.notRecommended }})</option>
|
||||
<option value="hcaptcha">hCaptcha</option>
|
||||
<option value="mcaptcha">mCaptcha</option>
|
||||
<option value="recaptcha">reCAPTCHA</option>
|
||||
<option value="turnstile">Turnstile</option>
|
||||
<option value="testcaptcha">testCaptcha</option>
|
||||
</MkRadios>
|
||||
<div class="_gaps_m">
|
||||
<MkRadios v-model="botProtectionForm.state.provider">
|
||||
<option value="none">{{ i18n.ts.none }} ({{ i18n.ts.notRecommended }})</option>
|
||||
<option value="hcaptcha">hCaptcha</option>
|
||||
<option value="mcaptcha">mCaptcha</option>
|
||||
<option value="recaptcha">reCAPTCHA</option>
|
||||
<option value="turnstile">Turnstile</option>
|
||||
<option value="testcaptcha">testCaptcha</option>
|
||||
</MkRadios>
|
||||
|
||||
<template v-if="botProtectionForm.state.provider === 'hcaptcha'">
|
||||
<MkInput v-model="botProtectionForm.state.hcaptchaSiteKey" debounce>
|
||||
<template #prefix><i class="ti ti-key"></i></template>
|
||||
<template #label>{{ i18n.ts.hcaptchaSiteKey }}</template>
|
||||
</MkInput>
|
||||
<MkInput v-model="botProtectionForm.state.hcaptchaSecretKey" debounce>
|
||||
<template #prefix><i class="ti ti-key"></i></template>
|
||||
<template #label>{{ i18n.ts.hcaptchaSecretKey }}</template>
|
||||
</MkInput>
|
||||
<FormSlot v-if="botProtectionForm.state.hcaptchaSiteKey">
|
||||
<template #label>{{ i18n.ts._captcha.verify }}</template>
|
||||
<MkCaptcha
|
||||
v-model="captchaResult"
|
||||
provider="hcaptcha"
|
||||
:sitekey="botProtectionForm.state.hcaptchaSiteKey"
|
||||
:secretKey="botProtectionForm.state.hcaptchaSecretKey"
|
||||
/>
|
||||
</FormSlot>
|
||||
<MkInfo>
|
||||
<div :class="$style.captchaInfoMsg">
|
||||
<div>{{ i18n.ts._captcha.testSiteKeyMessage }}</div>
|
||||
<div>
|
||||
<span>ref: </span><a href="https://docs.hcaptcha.com/#integration-testing-test-keys" target="_blank">hCaptcha Developer Guide</a>
|
||||
<template v-if="botProtectionForm.state.provider === 'hcaptcha'">
|
||||
<MkInput v-model="botProtectionForm.state.hcaptchaSiteKey" debounce>
|
||||
<template #prefix><i class="ti ti-key"></i></template>
|
||||
<template #label>{{ i18n.ts.hcaptchaSiteKey }}</template>
|
||||
</MkInput>
|
||||
<MkInput v-model="botProtectionForm.state.hcaptchaSecretKey" debounce>
|
||||
<template #prefix><i class="ti ti-key"></i></template>
|
||||
<template #label>{{ i18n.ts.hcaptchaSecretKey }}</template>
|
||||
</MkInput>
|
||||
<FormSlot v-if="botProtectionForm.state.hcaptchaSiteKey">
|
||||
<template #label>{{ i18n.ts._captcha.verify }}</template>
|
||||
<MkCaptcha
|
||||
v-model="captchaResult"
|
||||
provider="hcaptcha"
|
||||
:sitekey="botProtectionForm.state.hcaptchaSiteKey"
|
||||
:secretKey="botProtectionForm.state.hcaptchaSecretKey"
|
||||
/>
|
||||
</FormSlot>
|
||||
<MkInfo>
|
||||
<div :class="$style.captchaInfoMsg">
|
||||
<div>{{ i18n.ts._captcha.testSiteKeyMessage }}</div>
|
||||
<div>
|
||||
<span>ref: </span><a href="https://docs.hcaptcha.com/#integration-testing-test-keys" target="_blank">hCaptcha Developer Guide</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</MkInfo>
|
||||
</template>
|
||||
</MkInfo>
|
||||
</template>
|
||||
|
||||
<template v-else-if="botProtectionForm.state.provider === 'mcaptcha'">
|
||||
<MkInput v-model="botProtectionForm.state.mcaptchaSiteKey" debounce>
|
||||
<template #prefix><i class="ti ti-key"></i></template>
|
||||
<template #label>{{ i18n.ts.mcaptchaSiteKey }}</template>
|
||||
</MkInput>
|
||||
<MkInput v-model="botProtectionForm.state.mcaptchaSecretKey" debounce>
|
||||
<template #prefix><i class="ti ti-key"></i></template>
|
||||
<template #label>{{ i18n.ts.mcaptchaSecretKey }}</template>
|
||||
</MkInput>
|
||||
<MkInput v-model="botProtectionForm.state.mcaptchaInstanceUrl" debounce>
|
||||
<template #prefix><i class="ti ti-link"></i></template>
|
||||
<template #label>{{ i18n.ts.mcaptchaInstanceUrl }}</template>
|
||||
</MkInput>
|
||||
<FormSlot v-if="botProtectionForm.state.mcaptchaSiteKey && botProtectionForm.state.mcaptchaInstanceUrl">
|
||||
<template #label>{{ i18n.ts._captcha.verify }}</template>
|
||||
<MkCaptcha
|
||||
v-model="captchaResult"
|
||||
provider="mcaptcha"
|
||||
:sitekey="botProtectionForm.state.mcaptchaSiteKey"
|
||||
:secretKey="botProtectionForm.state.mcaptchaSecretKey"
|
||||
:instanceUrl="botProtectionForm.state.mcaptchaInstanceUrl"
|
||||
/>
|
||||
</FormSlot>
|
||||
</template>
|
||||
<template v-else-if="botProtectionForm.state.provider === 'mcaptcha'">
|
||||
<MkInput v-model="botProtectionForm.state.mcaptchaSiteKey" debounce>
|
||||
<template #prefix><i class="ti ti-key"></i></template>
|
||||
<template #label>{{ i18n.ts.mcaptchaSiteKey }}</template>
|
||||
</MkInput>
|
||||
<MkInput v-model="botProtectionForm.state.mcaptchaSecretKey" debounce>
|
||||
<template #prefix><i class="ti ti-key"></i></template>
|
||||
<template #label>{{ i18n.ts.mcaptchaSecretKey }}</template>
|
||||
</MkInput>
|
||||
<MkInput v-model="botProtectionForm.state.mcaptchaInstanceUrl" debounce>
|
||||
<template #prefix><i class="ti ti-link"></i></template>
|
||||
<template #label>{{ i18n.ts.mcaptchaInstanceUrl }}</template>
|
||||
</MkInput>
|
||||
<FormSlot v-if="botProtectionForm.state.mcaptchaSiteKey && botProtectionForm.state.mcaptchaInstanceUrl">
|
||||
<template #label>{{ i18n.ts._captcha.verify }}</template>
|
||||
<MkCaptcha
|
||||
v-model="captchaResult"
|
||||
provider="mcaptcha"
|
||||
:sitekey="botProtectionForm.state.mcaptchaSiteKey"
|
||||
:secretKey="botProtectionForm.state.mcaptchaSecretKey"
|
||||
:instanceUrl="botProtectionForm.state.mcaptchaInstanceUrl"
|
||||
/>
|
||||
</FormSlot>
|
||||
</template>
|
||||
|
||||
<template v-else-if="botProtectionForm.state.provider === 'recaptcha'">
|
||||
<MkInput v-model="botProtectionForm.state.recaptchaSiteKey" debounce>
|
||||
<template #prefix><i class="ti ti-key"></i></template>
|
||||
<template #label>{{ i18n.ts.recaptchaSiteKey }}</template>
|
||||
</MkInput>
|
||||
<MkInput v-model="botProtectionForm.state.recaptchaSecretKey" debounce>
|
||||
<template #prefix><i class="ti ti-key"></i></template>
|
||||
<template #label>{{ i18n.ts.recaptchaSecretKey }}</template>
|
||||
</MkInput>
|
||||
<FormSlot v-if="botProtectionForm.state.recaptchaSiteKey">
|
||||
<template #label>{{ i18n.ts._captcha.verify }}</template>
|
||||
<MkCaptcha
|
||||
v-model="captchaResult"
|
||||
provider="recaptcha"
|
||||
:sitekey="botProtectionForm.state.recaptchaSiteKey"
|
||||
:secretKey="botProtectionForm.state.recaptchaSecretKey"
|
||||
/>
|
||||
</FormSlot>
|
||||
<MkInfo>
|
||||
<div :class="$style.captchaInfoMsg">
|
||||
<div>{{ i18n.ts._captcha.testSiteKeyMessage }}</div>
|
||||
<div>
|
||||
<span>ref: </span>
|
||||
<a
|
||||
href="https://developers.google.com/recaptcha/docs/faq?hl=ja#id-like-to-run-automated-tests-with-recaptcha.-what-should-i-do"
|
||||
target="_blank"
|
||||
>reCAPTCHA FAQ</a>
|
||||
<template v-else-if="botProtectionForm.state.provider === 'recaptcha'">
|
||||
<MkInput v-model="botProtectionForm.state.recaptchaSiteKey" debounce>
|
||||
<template #prefix><i class="ti ti-key"></i></template>
|
||||
<template #label>{{ i18n.ts.recaptchaSiteKey }}</template>
|
||||
</MkInput>
|
||||
<MkInput v-model="botProtectionForm.state.recaptchaSecretKey" debounce>
|
||||
<template #prefix><i class="ti ti-key"></i></template>
|
||||
<template #label>{{ i18n.ts.recaptchaSecretKey }}</template>
|
||||
</MkInput>
|
||||
<FormSlot v-if="botProtectionForm.state.recaptchaSiteKey">
|
||||
<template #label>{{ i18n.ts._captcha.verify }}</template>
|
||||
<MkCaptcha
|
||||
v-model="captchaResult"
|
||||
provider="recaptcha"
|
||||
:sitekey="botProtectionForm.state.recaptchaSiteKey"
|
||||
:secretKey="botProtectionForm.state.recaptchaSecretKey"
|
||||
/>
|
||||
</FormSlot>
|
||||
<MkInfo>
|
||||
<div :class="$style.captchaInfoMsg">
|
||||
<div>{{ i18n.ts._captcha.testSiteKeyMessage }}</div>
|
||||
<div>
|
||||
<span>ref: </span>
|
||||
<a
|
||||
href="https://developers.google.com/recaptcha/docs/faq?hl=ja#id-like-to-run-automated-tests-with-recaptcha.-what-should-i-do"
|
||||
target="_blank"
|
||||
>reCAPTCHA FAQ</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</MkInfo>
|
||||
</template>
|
||||
</MkInfo>
|
||||
</template>
|
||||
|
||||
<template v-else-if="botProtectionForm.state.provider === 'turnstile'">
|
||||
<MkInput v-model="botProtectionForm.state.turnstileSiteKey" debounce>
|
||||
<template #prefix><i class="ti ti-key"></i></template>
|
||||
<template #label>{{ i18n.ts.turnstileSiteKey }}</template>
|
||||
</MkInput>
|
||||
<MkInput v-model="botProtectionForm.state.turnstileSecretKey" debounce>
|
||||
<template #prefix><i class="ti ti-key"></i></template>
|
||||
<template #label>{{ i18n.ts.turnstileSecretKey }}</template>
|
||||
</MkInput>
|
||||
<FormSlot v-if="botProtectionForm.state.turnstileSiteKey">
|
||||
<template #label>{{ i18n.ts._captcha.verify }}</template>
|
||||
<MkCaptcha
|
||||
v-model="captchaResult"
|
||||
provider="turnstile"
|
||||
:sitekey="botProtectionForm.state.turnstileSiteKey"
|
||||
:secretKey="botProtectionForm.state.turnstileSecretKey"
|
||||
/>
|
||||
</FormSlot>
|
||||
<MkInfo>
|
||||
<div :class="$style.captchaInfoMsg">
|
||||
<div>
|
||||
{{ i18n.ts._captcha.testSiteKeyMessage }}
|
||||
<template v-else-if="botProtectionForm.state.provider === 'turnstile'">
|
||||
<MkInput v-model="botProtectionForm.state.turnstileSiteKey" debounce>
|
||||
<template #prefix><i class="ti ti-key"></i></template>
|
||||
<template #label>{{ i18n.ts.turnstileSiteKey }}</template>
|
||||
</MkInput>
|
||||
<MkInput v-model="botProtectionForm.state.turnstileSecretKey" debounce>
|
||||
<template #prefix><i class="ti ti-key"></i></template>
|
||||
<template #label>{{ i18n.ts.turnstileSecretKey }}</template>
|
||||
</MkInput>
|
||||
<FormSlot v-if="botProtectionForm.state.turnstileSiteKey">
|
||||
<template #label>{{ i18n.ts._captcha.verify }}</template>
|
||||
<MkCaptcha
|
||||
v-model="captchaResult"
|
||||
provider="turnstile"
|
||||
:sitekey="botProtectionForm.state.turnstileSiteKey"
|
||||
:secretKey="botProtectionForm.state.turnstileSecretKey"
|
||||
/>
|
||||
</FormSlot>
|
||||
<MkInfo>
|
||||
<div :class="$style.captchaInfoMsg">
|
||||
<div>
|
||||
{{ i18n.ts._captcha.testSiteKeyMessage }}
|
||||
</div>
|
||||
<div>
|
||||
<span>ref: </span><a href="https://developers.cloudflare.com/turnstile/troubleshooting/testing/" target="_blank">Cloudflare Docs</a>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<span>ref: </span><a href="https://developers.cloudflare.com/turnstile/troubleshooting/testing/" target="_blank">Cloudflare Docs</a>
|
||||
</div>
|
||||
</div>
|
||||
</MkInfo>
|
||||
</template>
|
||||
</MkInfo>
|
||||
</template>
|
||||
|
||||
<template v-else-if="botProtectionForm.state.provider === 'testcaptcha'">
|
||||
<MkInfo warn><span v-html="i18n.ts.testCaptchaWarning"></span></MkInfo>
|
||||
<FormSlot>
|
||||
<template #label>{{ i18n.ts._captcha.verify }}</template>
|
||||
<MkCaptcha v-model="captchaResult" provider="testcaptcha" :sitekey="null"/>
|
||||
</FormSlot>
|
||||
</template>
|
||||
</div>
|
||||
</MkFolder>
|
||||
<template v-else-if="botProtectionForm.state.provider === 'testcaptcha'">
|
||||
<MkInfo warn><span v-html="i18n.ts.testCaptchaWarning"></span></MkInfo>
|
||||
<FormSlot>
|
||||
<template #label>{{ i18n.ts._captcha.verify }}</template>
|
||||
<MkCaptcha v-model="captchaResult" provider="testcaptcha" :sitekey="null"/>
|
||||
</FormSlot>
|
||||
</template>
|
||||
</div>
|
||||
</MkFolder>
|
||||
</SearchMarker>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, defineAsyncComponent, ref, watch } from 'vue';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import type { ApiWithDialogCustomErrors } from '@/os.js';
|
||||
import MkRadios from '@/components/MkRadios.vue';
|
||||
import MkInput from '@/components/MkInput.vue';
|
||||
import FormSlot from '@/components/form/slot.vue';
|
||||
@@ -167,7 +170,6 @@ import { useForm } from '@/composables/use-form.js';
|
||||
import MkFormFooter from '@/components/MkFormFooter.vue';
|
||||
import MkFolder from '@/components/MkFolder.vue';
|
||||
import MkInfo from '@/components/MkInfo.vue';
|
||||
import type { ApiWithDialogCustomErrors } from '@/os.js';
|
||||
|
||||
const MkCaptcha = defineAsyncComponent(() => import('@/components/MkCaptcha.vue'));
|
||||
|
||||
|
||||
@@ -6,89 +6,117 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<template>
|
||||
<PageWithHeader :tabs="headerTabs">
|
||||
<div class="_spacer" style="--MI_SPACER-w: 700px; --MI_SPACER-min: 16px; --MI_SPACER-max: 32px;">
|
||||
<FormSuspense :p="init">
|
||||
<SearchMarker path="/admin/branding" :label="i18n.ts.branding" :keywords="['branding']" icon="ti ti-paint">
|
||||
<div class="_gaps_m">
|
||||
<MkInput v-model="iconUrl" type="url">
|
||||
<template #prefix><i class="ti ti-link"></i></template>
|
||||
<template #label>{{ i18n.ts._serverSettings.iconUrl }}</template>
|
||||
</MkInput>
|
||||
<SearchMarker :keywords="['icon', 'image']">
|
||||
<MkInput v-model="iconUrl" type="url">
|
||||
<template #prefix><i class="ti ti-link"></i></template>
|
||||
<template #label><SearchLabel>{{ i18n.ts._serverSettings.iconUrl }}</SearchLabel></template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
|
||||
<MkInput v-model="app192IconUrl" type="url">
|
||||
<template #prefix><i class="ti ti-link"></i></template>
|
||||
<template #label>{{ i18n.ts._serverSettings.iconUrl }} (App/192px)</template>
|
||||
<template #caption>
|
||||
<div>{{ i18n.tsx._serverSettings.appIconDescription({ host: instance.name ?? host }) }}</div>
|
||||
<div>({{ i18n.ts._serverSettings.appIconUsageExample }})</div>
|
||||
<div>{{ i18n.ts._serverSettings.appIconStyleRecommendation }}</div>
|
||||
<div><strong>{{ i18n.tsx._serverSettings.appIconResolutionMustBe({ resolution: '192x192px' }) }}</strong></div>
|
||||
</template>
|
||||
</MkInput>
|
||||
<SearchMarker :keywords="['icon', 'image']">
|
||||
<MkInput v-model="app192IconUrl" type="url">
|
||||
<template #prefix><i class="ti ti-link"></i></template>
|
||||
<template #label><SearchLabel>{{ i18n.ts._serverSettings.iconUrl }} (App/192px)</SearchLabel></template>
|
||||
<template #caption>
|
||||
<div>{{ i18n.tsx._serverSettings.appIconDescription({ host: instance.name ?? host }) }}</div>
|
||||
<div>({{ i18n.ts._serverSettings.appIconUsageExample }})</div>
|
||||
<div>{{ i18n.ts._serverSettings.appIconStyleRecommendation }}</div>
|
||||
<div><strong>{{ i18n.tsx._serverSettings.appIconResolutionMustBe({ resolution: '192x192px' }) }}</strong></div>
|
||||
</template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
|
||||
<MkInput v-model="app512IconUrl" type="url">
|
||||
<template #prefix><i class="ti ti-link"></i></template>
|
||||
<template #label>{{ i18n.ts._serverSettings.iconUrl }} (App/512px)</template>
|
||||
<template #caption>
|
||||
<div>{{ i18n.tsx._serverSettings.appIconDescription({ host: instance.name ?? host }) }}</div>
|
||||
<div>({{ i18n.ts._serverSettings.appIconUsageExample }})</div>
|
||||
<div>{{ i18n.ts._serverSettings.appIconStyleRecommendation }}</div>
|
||||
<div><strong>{{ i18n.tsx._serverSettings.appIconResolutionMustBe({ resolution: '512x512px' }) }}</strong></div>
|
||||
</template>
|
||||
</MkInput>
|
||||
<SearchMarker :keywords="['icon', 'image']">
|
||||
<MkInput v-model="app512IconUrl" type="url">
|
||||
<template #prefix><i class="ti ti-link"></i></template>
|
||||
<template #label><SearchLabel>{{ i18n.ts._serverSettings.iconUrl }} (App/512px)</SearchLabel></template>
|
||||
<template #caption>
|
||||
<div>{{ i18n.tsx._serverSettings.appIconDescription({ host: instance.name ?? host }) }}</div>
|
||||
<div>({{ i18n.ts._serverSettings.appIconUsageExample }})</div>
|
||||
<div>{{ i18n.ts._serverSettings.appIconStyleRecommendation }}</div>
|
||||
<div><strong>{{ i18n.tsx._serverSettings.appIconResolutionMustBe({ resolution: '512x512px' }) }}</strong></div>
|
||||
</template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
|
||||
<MkInput v-model="bannerUrl" type="url">
|
||||
<template #prefix><i class="ti ti-link"></i></template>
|
||||
<template #label>{{ i18n.ts.bannerUrl }}</template>
|
||||
</MkInput>
|
||||
<SearchMarker :keywords="['banner', 'image']">
|
||||
<MkInput v-model="bannerUrl" type="url">
|
||||
<template #prefix><i class="ti ti-link"></i></template>
|
||||
<template #label><SearchLabel>{{ i18n.ts.bannerUrl }}</SearchLabel></template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
|
||||
<MkInput v-model="backgroundImageUrl" type="url">
|
||||
<template #prefix><i class="ti ti-link"></i></template>
|
||||
<template #label>{{ i18n.ts.backgroundImageUrl }}</template>
|
||||
</MkInput>
|
||||
<SearchMarker :keywords="['background', 'image']">
|
||||
<MkInput v-model="backgroundImageUrl" type="url">
|
||||
<template #prefix><i class="ti ti-link"></i></template>
|
||||
<template #label><SearchLabel>{{ i18n.ts.backgroundImageUrl }}</SearchLabel></template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
|
||||
<MkInput v-model="notFoundImageUrl" type="url">
|
||||
<template #prefix><i class="ti ti-link"></i></template>
|
||||
<template #label>{{ i18n.ts.notFoundDescription }}</template>
|
||||
</MkInput>
|
||||
<SearchMarker :keywords="['image']">
|
||||
<MkInput v-model="notFoundImageUrl" type="url">
|
||||
<template #prefix><i class="ti ti-link"></i></template>
|
||||
<template #label><SearchLabel>{{ i18n.ts.notFoundDescription }}</SearchLabel></template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
|
||||
<MkInput v-model="infoImageUrl" type="url">
|
||||
<template #prefix><i class="ti ti-link"></i></template>
|
||||
<template #label>{{ i18n.ts.nothing }}</template>
|
||||
</MkInput>
|
||||
<SearchMarker :keywords="['image']">
|
||||
<MkInput v-model="infoImageUrl" type="url">
|
||||
<template #prefix><i class="ti ti-link"></i></template>
|
||||
<template #label><SearchLabel>{{ i18n.ts.nothing }}</SearchLabel></template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
|
||||
<MkInput v-model="serverErrorImageUrl" type="url">
|
||||
<template #prefix><i class="ti ti-link"></i></template>
|
||||
<template #label>{{ i18n.ts.somethingHappened }}</template>
|
||||
</MkInput>
|
||||
<SearchMarker :keywords="['image']">
|
||||
<MkInput v-model="serverErrorImageUrl" type="url">
|
||||
<template #prefix><i class="ti ti-link"></i></template>
|
||||
<template #label><SearchLabel>{{ i18n.ts.somethingHappened }}</SearchLabel></template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
|
||||
<MkColorInput v-model="themeColor">
|
||||
<template #label>{{ i18n.ts.themeColor }}</template>
|
||||
</MkColorInput>
|
||||
<SearchMarker :keywords="['theme', 'color']">
|
||||
<MkColorInput v-model="themeColor">
|
||||
<template #label><SearchLabel>{{ i18n.ts.themeColor }}</SearchLabel></template>
|
||||
</MkColorInput>
|
||||
</SearchMarker>
|
||||
|
||||
<MkTextarea v-model="defaultLightTheme">
|
||||
<template #label>{{ i18n.ts.instanceDefaultLightTheme }}</template>
|
||||
<template #caption>{{ i18n.ts.instanceDefaultThemeDescription }}</template>
|
||||
</MkTextarea>
|
||||
<SearchMarker :keywords="['theme', 'default', 'light']">
|
||||
<MkTextarea v-model="defaultLightTheme">
|
||||
<template #label><SearchLabel>{{ i18n.ts.instanceDefaultLightTheme }}</SearchLabel></template>
|
||||
<template #caption>{{ i18n.ts.instanceDefaultThemeDescription }}</template>
|
||||
</MkTextarea>
|
||||
</SearchMarker>
|
||||
|
||||
<MkTextarea v-model="defaultDarkTheme">
|
||||
<template #label>{{ i18n.ts.instanceDefaultDarkTheme }}</template>
|
||||
<template #caption>{{ i18n.ts.instanceDefaultThemeDescription }}</template>
|
||||
</MkTextarea>
|
||||
<SearchMarker :keywords="['theme', 'default', 'dark']">
|
||||
<MkTextarea v-model="defaultDarkTheme">
|
||||
<template #label><SearchLabel>{{ i18n.ts.instanceDefaultDarkTheme }}</SearchLabel></template>
|
||||
<template #caption>{{ i18n.ts.instanceDefaultThemeDescription }}</template>
|
||||
</MkTextarea>
|
||||
</SearchMarker>
|
||||
|
||||
<MkInput v-model="repositoryUrl" type="url">
|
||||
<template #prefix><i class="ti ti-link"></i></template>
|
||||
<template #label>{{ i18n.ts.repositoryUrl }}</template>
|
||||
</MkInput>
|
||||
<SearchMarker>
|
||||
<MkInput v-model="repositoryUrl" type="url">
|
||||
<template #prefix><i class="ti ti-link"></i></template>
|
||||
<template #label><SearchLabel>{{ i18n.ts.repositoryUrl }}</SearchLabel></template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
|
||||
<MkInput v-model="feedbackUrl" type="url">
|
||||
<template #prefix><i class="ti ti-link"></i></template>
|
||||
<template #label>{{ i18n.ts.feedbackUrl }}</template>
|
||||
</MkInput>
|
||||
<SearchMarker>
|
||||
<MkInput v-model="feedbackUrl" type="url">
|
||||
<template #prefix><i class="ti ti-link"></i></template>
|
||||
<template #label><SearchLabel>{{ i18n.ts.feedbackUrl }}</SearchLabel></template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
|
||||
<MkTextarea v-model="manifestJsonOverride">
|
||||
<template #label>{{ i18n.ts._serverSettings.manifestJsonOverride }}</template>
|
||||
</MkTextarea>
|
||||
<SearchMarker>
|
||||
<MkTextarea v-model="manifestJsonOverride">
|
||||
<template #label><SearchLabel>{{ i18n.ts._serverSettings.manifestJsonOverride }}</SearchLabel></template>
|
||||
</MkTextarea>
|
||||
</SearchMarker>
|
||||
</div>
|
||||
</FormSuspense>
|
||||
</SearchMarker>
|
||||
</div>
|
||||
<template #footer>
|
||||
<div :class="$style.footer">
|
||||
@@ -106,7 +134,6 @@ import JSON5 from 'json5';
|
||||
import { host } from '@@/js/config.js';
|
||||
import MkInput from '@/components/MkInput.vue';
|
||||
import MkTextarea from '@/components/MkTextarea.vue';
|
||||
import FormSuspense from '@/components/form/suspense.vue';
|
||||
import * as os from '@/os.js';
|
||||
import { misskeyApi } from '@/utility/misskey-api.js';
|
||||
import { instance, fetchInstance } from '@/instance.js';
|
||||
@@ -115,38 +142,22 @@ import { definePage } from '@/page.js';
|
||||
import MkButton from '@/components/MkButton.vue';
|
||||
import MkColorInput from '@/components/MkColorInput.vue';
|
||||
|
||||
const iconUrl = ref<string | null>(null);
|
||||
const app192IconUrl = ref<string | null>(null);
|
||||
const app512IconUrl = ref<string | null>(null);
|
||||
const bannerUrl = ref<string | null>(null);
|
||||
const backgroundImageUrl = ref<string | null>(null);
|
||||
const themeColor = ref<string | null>(null);
|
||||
const defaultLightTheme = ref<string | null>(null);
|
||||
const defaultDarkTheme = ref<string | null>(null);
|
||||
const serverErrorImageUrl = ref<string | null>(null);
|
||||
const infoImageUrl = ref<string | null>(null);
|
||||
const notFoundImageUrl = ref<string | null>(null);
|
||||
const repositoryUrl = ref<string | null>(null);
|
||||
const feedbackUrl = ref<string | null>(null);
|
||||
const manifestJsonOverride = ref<string>('{}');
|
||||
const meta = await misskeyApi('admin/meta');
|
||||
|
||||
async function init() {
|
||||
const meta = await misskeyApi('admin/meta');
|
||||
iconUrl.value = meta.iconUrl;
|
||||
app192IconUrl.value = meta.app192IconUrl;
|
||||
app512IconUrl.value = meta.app512IconUrl;
|
||||
bannerUrl.value = meta.bannerUrl;
|
||||
backgroundImageUrl.value = meta.backgroundImageUrl;
|
||||
themeColor.value = meta.themeColor;
|
||||
defaultLightTheme.value = meta.defaultLightTheme;
|
||||
defaultDarkTheme.value = meta.defaultDarkTheme;
|
||||
serverErrorImageUrl.value = meta.serverErrorImageUrl;
|
||||
infoImageUrl.value = meta.infoImageUrl;
|
||||
notFoundImageUrl.value = meta.notFoundImageUrl;
|
||||
repositoryUrl.value = meta.repositoryUrl;
|
||||
feedbackUrl.value = meta.feedbackUrl;
|
||||
manifestJsonOverride.value = meta.manifestJsonOverride === '' ? '{}' : JSON.stringify(JSON.parse(meta.manifestJsonOverride), null, '\t');
|
||||
}
|
||||
const iconUrl = ref(meta.iconUrl);
|
||||
const app192IconUrl = ref(meta.app192IconUrl);
|
||||
const app512IconUrl = ref(meta.app512IconUrl);
|
||||
const bannerUrl = ref(meta.bannerUrl);
|
||||
const backgroundImageUrl = ref(meta.backgroundImageUrl);
|
||||
const themeColor = ref(meta.themeColor);
|
||||
const defaultLightTheme = ref(meta.defaultLightTheme);
|
||||
const defaultDarkTheme = ref(meta.defaultDarkTheme);
|
||||
const serverErrorImageUrl = ref(meta.serverErrorImageUrl);
|
||||
const infoImageUrl = ref(meta.infoImageUrl);
|
||||
const notFoundImageUrl = ref(meta.notFoundImageUrl);
|
||||
const repositoryUrl = ref(meta.repositoryUrl);
|
||||
const feedbackUrl = ref(meta.feedbackUrl);
|
||||
const manifestJsonOverride = ref(meta.manifestJsonOverride === '' ? '{}' : JSON.stringify(JSON.parse(meta.manifestJsonOverride), null, '\t'));
|
||||
|
||||
function save() {
|
||||
os.apiWithDialog('admin/update-meta', {
|
||||
|
||||
@@ -6,48 +6,67 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<template>
|
||||
<PageWithHeader :tabs="headerTabs">
|
||||
<div class="_spacer" style="--MI_SPACER-w: 700px; --MI_SPACER-min: 16px; --MI_SPACER-max: 32px;">
|
||||
<FormSuspense :p="init">
|
||||
<SearchMarker path="/admin/email-settings" :label="i18n.ts.emailServer" :keywords="['email']" icon="ti ti-mail">
|
||||
<div class="_gaps_m">
|
||||
<MkSwitch v-model="enableEmail">
|
||||
<template #label>{{ i18n.ts.enableEmail }} ({{ i18n.ts.recommended }})</template>
|
||||
<template #caption>{{ i18n.ts.emailConfigInfo }}</template>
|
||||
</MkSwitch>
|
||||
<SearchMarker>
|
||||
<MkSwitch v-model="enableEmail">
|
||||
<template #label><SearchLabel>{{ i18n.ts.enableEmail }}</SearchLabel> ({{ i18n.ts.recommended }})</template>
|
||||
<template #caption><SearchText>{{ i18n.ts.emailConfigInfo }}</SearchText></template>
|
||||
</MkSwitch>
|
||||
</SearchMarker>
|
||||
|
||||
<template v-if="enableEmail">
|
||||
<MkInput v-model="email" type="email">
|
||||
<template #label>{{ i18n.ts.emailAddress }}</template>
|
||||
</MkInput>
|
||||
<SearchMarker>
|
||||
<MkInput v-model="email" type="email">
|
||||
<template #label><SearchLabel>{{ i18n.ts.emailAddress }}</SearchLabel></template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
|
||||
<FormSection>
|
||||
<template #label>{{ i18n.ts.smtpConfig }}</template>
|
||||
<SearchMarker>
|
||||
<FormSection>
|
||||
<template #label><SearchLabel>{{ i18n.ts.smtpConfig }}</SearchLabel></template>
|
||||
|
||||
<div class="_gaps_m">
|
||||
<FormSplit :minWidth="280">
|
||||
<MkInput v-model="smtpHost">
|
||||
<template #label>{{ i18n.ts.smtpHost }}</template>
|
||||
</MkInput>
|
||||
<MkInput v-model="smtpPort" type="number">
|
||||
<template #label>{{ i18n.ts.smtpPort }}</template>
|
||||
</MkInput>
|
||||
</FormSplit>
|
||||
<FormSplit :minWidth="280">
|
||||
<MkInput v-model="smtpUser">
|
||||
<template #label>{{ i18n.ts.smtpUser }}</template>
|
||||
</MkInput>
|
||||
<MkInput v-model="smtpPass" type="password">
|
||||
<template #label>{{ i18n.ts.smtpPass }}</template>
|
||||
</MkInput>
|
||||
</FormSplit>
|
||||
<FormInfo>{{ i18n.ts.emptyToDisableSmtpAuth }}</FormInfo>
|
||||
<MkSwitch v-model="smtpSecure">
|
||||
<template #label>{{ i18n.ts.smtpSecure }}</template>
|
||||
<template #caption>{{ i18n.ts.smtpSecureInfo }}</template>
|
||||
</MkSwitch>
|
||||
</div>
|
||||
</FormSection>
|
||||
<div class="_gaps_m">
|
||||
<FormSplit :minWidth="280">
|
||||
<SearchMarker>
|
||||
<MkInput v-model="smtpHost">
|
||||
<template #label><SearchLabel>{{ i18n.ts.smtpHost }}</SearchLabel></template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
<SearchMarker>
|
||||
<MkInput v-model="smtpPort" type="number">
|
||||
<template #label><SearchLabel>{{ i18n.ts.smtpPort }}</SearchLabel></template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
</FormSplit>
|
||||
|
||||
<FormSplit :minWidth="280">
|
||||
<SearchMarker>
|
||||
<MkInput v-model="smtpUser">
|
||||
<template #label><SearchLabel>{{ i18n.ts.smtpUser }}</SearchLabel></template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
<SearchMarker>
|
||||
<MkInput v-model="smtpPass" type="password">
|
||||
<template #label><SearchLabel>{{ i18n.ts.smtpPass }}</SearchLabel></template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
</FormSplit>
|
||||
|
||||
<FormInfo>{{ i18n.ts.emptyToDisableSmtpAuth }}</FormInfo>
|
||||
|
||||
<SearchMarker>
|
||||
<MkSwitch v-model="smtpSecure">
|
||||
<template #label><SearchLabel>{{ i18n.ts.smtpSecure }}</SearchLabel></template>
|
||||
<template #caption><SearchText>{{ i18n.ts.smtpSecureInfo }}</SearchText></template>
|
||||
</MkSwitch>
|
||||
</SearchMarker>
|
||||
</div>
|
||||
</FormSection>
|
||||
</SearchMarker>
|
||||
</template>
|
||||
</div>
|
||||
</FormSuspense>
|
||||
</SearchMarker>
|
||||
</div>
|
||||
<template #footer>
|
||||
<div :class="$style.footer">
|
||||
@@ -67,7 +86,6 @@ import { ref, computed } from 'vue';
|
||||
import MkSwitch from '@/components/MkSwitch.vue';
|
||||
import MkInput from '@/components/MkInput.vue';
|
||||
import FormInfo from '@/components/MkInfo.vue';
|
||||
import FormSuspense from '@/components/form/suspense.vue';
|
||||
import FormSplit from '@/components/form/split.vue';
|
||||
import FormSection from '@/components/form/section.vue';
|
||||
import * as os from '@/os.js';
|
||||
@@ -77,24 +95,15 @@ import { i18n } from '@/i18n.js';
|
||||
import { definePage } from '@/page.js';
|
||||
import MkButton from '@/components/MkButton.vue';
|
||||
|
||||
const enableEmail = ref<boolean>(false);
|
||||
const email = ref<string | null>(null);
|
||||
const smtpSecure = ref<boolean>(false);
|
||||
const smtpHost = ref<string>('');
|
||||
const smtpPort = ref<number>(0);
|
||||
const smtpUser = ref<string>('');
|
||||
const smtpPass = ref<string>('');
|
||||
const meta = await misskeyApi('admin/meta');
|
||||
|
||||
async function init() {
|
||||
const meta = await misskeyApi('admin/meta');
|
||||
enableEmail.value = meta.enableEmail;
|
||||
email.value = meta.email;
|
||||
smtpSecure.value = meta.smtpSecure;
|
||||
smtpHost.value = meta.smtpHost;
|
||||
smtpPort.value = meta.smtpPort;
|
||||
smtpUser.value = meta.smtpUser;
|
||||
smtpPass.value = meta.smtpPass;
|
||||
}
|
||||
const enableEmail = ref(meta.enableEmail);
|
||||
const email = ref(meta.email);
|
||||
const smtpSecure = ref(meta.smtpSecure);
|
||||
const smtpHost = ref(meta.smtpHost);
|
||||
const smtpPort = ref(meta.smtpPort);
|
||||
const smtpUser = ref(meta.smtpUser);
|
||||
const smtpPass = ref(meta.smtpPass);
|
||||
|
||||
async function testEmail() {
|
||||
const { canceled, result: destination } = await os.inputText({
|
||||
|
||||
@@ -6,36 +6,49 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<template>
|
||||
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
|
||||
<div class="_spacer" style="--MI_SPACER-w: 700px; --MI_SPACER-min: 16px; --MI_SPACER-max: 32px;">
|
||||
<FormSuspense :p="init">
|
||||
<SearchMarker path="/admin/external-services" :label="i18n.ts.externalServices" :keywords="['external', 'services', 'thirdparty']" icon="ti ti-link">
|
||||
<div class="_gaps_m">
|
||||
<MkFolder>
|
||||
<template #label>Google Analytics<span class="_beta">{{ i18n.ts.beta }}</span></template>
|
||||
<SearchMarker v-slot="slotProps">
|
||||
<MkFolder :defaultOpen="slotProps.isParentOfTarget">
|
||||
<template #label><SearchLabel>Google Analytics</SearchLabel><span class="_beta">{{ i18n.ts.beta }}</span></template>
|
||||
|
||||
<div class="_gaps_m">
|
||||
<MkInput v-model="googleAnalyticsMeasurementId">
|
||||
<template #prefix><i class="ti ti-key"></i></template>
|
||||
<template #label>Measurement ID</template>
|
||||
</MkInput>
|
||||
<MkButton primary @click="save_googleAnalytics">Save</MkButton>
|
||||
</div>
|
||||
</MkFolder>
|
||||
<div class="_gaps_m">
|
||||
<SearchMarker>
|
||||
<MkInput v-model="googleAnalyticsMeasurementId">
|
||||
<template #prefix><i class="ti ti-key"></i></template>
|
||||
<template #label><SearchLabel>Measurement ID</SearchLabel></template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
|
||||
<MkFolder>
|
||||
<template #label>DeepL Translation</template>
|
||||
<MkButton primary @click="save_googleAnalytics">Save</MkButton>
|
||||
</div>
|
||||
</MkFolder>
|
||||
</SearchMarker>
|
||||
|
||||
<div class="_gaps_m">
|
||||
<MkInput v-model="deeplAuthKey">
|
||||
<template #prefix><i class="ti ti-key"></i></template>
|
||||
<template #label>DeepL Auth Key</template>
|
||||
</MkInput>
|
||||
<MkSwitch v-model="deeplIsPro">
|
||||
<template #label>Pro account</template>
|
||||
</MkSwitch>
|
||||
<MkButton primary @click="save_deepl">Save</MkButton>
|
||||
</div>
|
||||
</MkFolder>
|
||||
<SearchMarker v-slot="slotProps">
|
||||
<MkFolder :defaultOpen="slotProps.isParentOfTarget">
|
||||
<template #label><SearchLabel>DeepL Translation</SearchLabel></template>
|
||||
|
||||
<div class="_gaps_m">
|
||||
<SearchMarker>
|
||||
<MkInput v-model="deeplAuthKey">
|
||||
<template #prefix><i class="ti ti-key"></i></template>
|
||||
<template #label><SearchLabel>Auth Key</SearchLabel></template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
|
||||
<SearchMarker>
|
||||
<MkSwitch v-model="deeplIsPro">
|
||||
<template #label><SearchLabel>Pro account</SearchLabel></template>
|
||||
</MkSwitch>
|
||||
</SearchMarker>
|
||||
|
||||
<MkButton primary @click="save_deepl">Save</MkButton>
|
||||
</div>
|
||||
</MkFolder>
|
||||
</SearchMarker>
|
||||
</div>
|
||||
</FormSuspense>
|
||||
</SearchMarker>
|
||||
</div>
|
||||
</PageWithHeader>
|
||||
</template>
|
||||
@@ -45,7 +58,6 @@ import { ref, computed } from 'vue';
|
||||
import MkInput from '@/components/MkInput.vue';
|
||||
import MkButton from '@/components/MkButton.vue';
|
||||
import MkSwitch from '@/components/MkSwitch.vue';
|
||||
import FormSuspense from '@/components/form/suspense.vue';
|
||||
import * as os from '@/os.js';
|
||||
import { misskeyApi } from '@/utility/misskey-api.js';
|
||||
import { fetchInstance } from '@/instance.js';
|
||||
@@ -53,17 +65,11 @@ import { i18n } from '@/i18n.js';
|
||||
import { definePage } from '@/page.js';
|
||||
import MkFolder from '@/components/MkFolder.vue';
|
||||
|
||||
const deeplAuthKey = ref<string>('');
|
||||
const deeplIsPro = ref<boolean>(false);
|
||||
const meta = await misskeyApi('admin/meta');
|
||||
|
||||
const googleAnalyticsMeasurementId = ref<string>('');
|
||||
|
||||
async function init() {
|
||||
const meta = await misskeyApi('admin/meta');
|
||||
deeplAuthKey.value = meta.deeplAuthKey ?? '';
|
||||
deeplIsPro.value = meta.deeplIsPro;
|
||||
googleAnalyticsMeasurementId.value = meta.googleAnalyticsMeasurementId ?? '';
|
||||
}
|
||||
const deeplAuthKey = ref(meta.deeplAuthKey ?? '');
|
||||
const deeplIsPro = ref(meta.deeplIsPro);
|
||||
const googleAnalyticsMeasurementId = ref(meta.googleAnalyticsMeasurementId ?? '');
|
||||
|
||||
function save_deepl() {
|
||||
os.apiWithDialog('admin/update-meta', {
|
||||
|
||||
@@ -20,7 +20,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<MkInfo v-if="noEmailServer" warn>{{ i18n.ts.noEmailServerWarning }} <MkA to="/admin/email-settings" class="_link">{{ i18n.ts.configure }}</MkA></MkInfo>
|
||||
</div>
|
||||
|
||||
<MkSuperMenu :def="menuDef" :grid="narrow"></MkSuperMenu>
|
||||
<MkSuperMenu :def="menuDef" :searchIndex="searchIndex" :grid="narrow"></MkSuperMenu>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -44,6 +44,9 @@ import { misskeyApi } from '@/utility/misskey-api.js';
|
||||
import { lookupUser, lookupUserByEmail, lookupFile } from '@/utility/admin-lookup.js';
|
||||
import { definePage, provideMetadataReceiver, provideReactiveMetadata } from '@/page.js';
|
||||
import { useRouter } from '@/router.js';
|
||||
import { genSearchIndexes } from '@/utility/inapp-search.js';
|
||||
|
||||
const searchIndex = await import('search-index:admin').then(({ searchIndexes }) => genSearchIndexes(searchIndexes));
|
||||
|
||||
const isEmpty = (x: string | null) => x == null || x === '';
|
||||
|
||||
@@ -324,12 +327,6 @@ const headerActions = computed(() => []);
|
||||
const headerTabs = computed(() => []);
|
||||
|
||||
definePage(() => INFO.value);
|
||||
|
||||
defineExpose({
|
||||
header: {
|
||||
title: i18n.ts.controlPanel,
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -6,140 +6,162 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<template>
|
||||
<PageWithHeader :tabs="headerTabs">
|
||||
<div class="_spacer" style="--MI_SPACER-w: 700px; --MI_SPACER-min: 16px; --MI_SPACER-max: 32px;">
|
||||
<FormSuspense :p="init">
|
||||
<SearchMarker path="/admin/moderation" :label="i18n.ts.moderation" :keywords="['moderation']" icon="ti ti-shield" :inlining="['serverRules']">
|
||||
<div class="_gaps_m">
|
||||
<MkSwitch :modelValue="enableRegistration" @update:modelValue="onChange_enableRegistration">
|
||||
<template #label>{{ i18n.ts._serverSettings.openRegistration }}</template>
|
||||
<template #caption>
|
||||
<div>{{ i18n.ts._serverSettings.thisSettingWillAutomaticallyOffWhenModeratorsInactive }}</div>
|
||||
<div><i class="ti ti-alert-triangle" style="color: var(--MI_THEME-warn);"></i> {{ i18n.ts._serverSettings.openRegistrationWarning }}</div>
|
||||
</template>
|
||||
</MkSwitch>
|
||||
<SearchMarker :keywords="['open', 'registration']">
|
||||
<MkSwitch :modelValue="enableRegistration" @update:modelValue="onChange_enableRegistration">
|
||||
<template #label><SearchLabel>{{ i18n.ts._serverSettings.openRegistration }}</SearchLabel></template>
|
||||
<template #caption>
|
||||
<div><SearchText>{{ i18n.ts._serverSettings.thisSettingWillAutomaticallyOffWhenModeratorsInactive }}</SearchText></div>
|
||||
<div><i class="ti ti-alert-triangle" style="color: var(--MI_THEME-warn);"></i> <SearchText>{{ i18n.ts._serverSettings.openRegistrationWarning }}</SearchText></div>
|
||||
</template>
|
||||
</MkSwitch>
|
||||
</SearchMarker>
|
||||
|
||||
<MkSwitch v-model="emailRequiredForSignup" @change="onChange_emailRequiredForSignup">
|
||||
<template #label>{{ i18n.ts.emailRequiredForSignup }} ({{ i18n.ts.recommended }})</template>
|
||||
</MkSwitch>
|
||||
<SearchMarker :keywords="['email', 'required', 'signup']">
|
||||
<MkSwitch v-model="emailRequiredForSignup" @change="onChange_emailRequiredForSignup">
|
||||
<template #label><SearchLabel>{{ i18n.ts.emailRequiredForSignup }}</SearchLabel> ({{ i18n.ts.recommended }})</template>
|
||||
</MkSwitch>
|
||||
</SearchMarker>
|
||||
|
||||
<MkSelect v-model="ugcVisibilityForVisitor" @update:modelValue="onChange_ugcVisibilityForVisitor">
|
||||
<template #label>{{ i18n.ts._serverSettings.userGeneratedContentsVisibilityForVisitor }}</template>
|
||||
<option value="all">{{ i18n.ts._serverSettings._userGeneratedContentsVisibilityForVisitor.all }}</option>
|
||||
<option value="local">{{ i18n.ts._serverSettings._userGeneratedContentsVisibilityForVisitor.localOnly }} ({{ i18n.ts.recommended }})</option>
|
||||
<option value="none">{{ i18n.ts._serverSettings._userGeneratedContentsVisibilityForVisitor.none }}</option>
|
||||
<template #caption>
|
||||
<div>{{ i18n.ts._serverSettings.userGeneratedContentsVisibilityForVisitor_description }}</div>
|
||||
<div><i class="ti ti-alert-triangle" style="color: var(--MI_THEME-warn);"></i> {{ i18n.ts._serverSettings.userGeneratedContentsVisibilityForVisitor_description2 }}</div>
|
||||
</template>
|
||||
</MkSelect>
|
||||
<SearchMarker :keywords="['ugc', 'content', 'visibility', 'visitor', 'guest']">
|
||||
<MkSelect v-model="ugcVisibilityForVisitor" @update:modelValue="onChange_ugcVisibilityForVisitor">
|
||||
<template #label><SearchLabel>{{ i18n.ts._serverSettings.userGeneratedContentsVisibilityForVisitor }}</SearchLabel></template>
|
||||
<option value="all">{{ i18n.ts._serverSettings._userGeneratedContentsVisibilityForVisitor.all }}</option>
|
||||
<option value="local">{{ i18n.ts._serverSettings._userGeneratedContentsVisibilityForVisitor.localOnly }} ({{ i18n.ts.recommended }})</option>
|
||||
<option value="none">{{ i18n.ts._serverSettings._userGeneratedContentsVisibilityForVisitor.none }}</option>
|
||||
<template #caption>
|
||||
<div><SearchText>{{ i18n.ts._serverSettings.userGeneratedContentsVisibilityForVisitor_description }}</SearchText></div>
|
||||
<div><i class="ti ti-alert-triangle" style="color: var(--MI_THEME-warn);"></i> <SearchText>{{ i18n.ts._serverSettings.userGeneratedContentsVisibilityForVisitor_description2 }}</SearchText></div>
|
||||
</template>
|
||||
</MkSelect>
|
||||
</SearchMarker>
|
||||
|
||||
<FormLink to="/admin/server-rules">{{ i18n.ts.serverRules }}</FormLink>
|
||||
<XServerRules/>
|
||||
|
||||
<MkFolder>
|
||||
<template #icon><i class="ti ti-lock-star"></i></template>
|
||||
<template #label>{{ i18n.ts.preservedUsernames }}</template>
|
||||
<SearchMarker :keywords="['preserved', 'usernames']">
|
||||
<MkFolder>
|
||||
<template #icon><SearchIcon><i class="ti ti-lock-star"></i></SearchIcon></template>
|
||||
<template #label><SearchLabel>{{ i18n.ts.preservedUsernames }}</SearchLabel></template>
|
||||
|
||||
<div class="_gaps">
|
||||
<MkTextarea v-model="preservedUsernames">
|
||||
<template #caption>{{ i18n.ts.preservedUsernamesDescription }}</template>
|
||||
</MkTextarea>
|
||||
<MkButton primary @click="save_preservedUsernames">{{ i18n.ts.save }}</MkButton>
|
||||
</div>
|
||||
</MkFolder>
|
||||
<div class="_gaps">
|
||||
<MkTextarea v-model="preservedUsernames">
|
||||
<template #caption>{{ i18n.ts.preservedUsernamesDescription }}</template>
|
||||
</MkTextarea>
|
||||
<MkButton primary @click="save_preservedUsernames">{{ i18n.ts.save }}</MkButton>
|
||||
</div>
|
||||
</MkFolder>
|
||||
</SearchMarker>
|
||||
|
||||
<MkFolder>
|
||||
<template #icon><i class="ti ti-message-exclamation"></i></template>
|
||||
<template #label>{{ i18n.ts.sensitiveWords }}</template>
|
||||
<SearchMarker :keywords="['sensitive', 'words']">
|
||||
<MkFolder>
|
||||
<template #icon><SearchIcon><i class="ti ti-message-exclamation"></i></SearchIcon></template>
|
||||
<template #label><SearchLabel>{{ i18n.ts.sensitiveWords }}</SearchLabel></template>
|
||||
|
||||
<div class="_gaps">
|
||||
<MkTextarea v-model="sensitiveWords">
|
||||
<template #caption>{{ i18n.ts.sensitiveWordsDescription }}<br>{{ i18n.ts.sensitiveWordsDescription2 }}</template>
|
||||
</MkTextarea>
|
||||
<MkButton primary @click="save_sensitiveWords">{{ i18n.ts.save }}</MkButton>
|
||||
</div>
|
||||
</MkFolder>
|
||||
<div class="_gaps">
|
||||
<MkTextarea v-model="sensitiveWords">
|
||||
<template #caption>{{ i18n.ts.sensitiveWordsDescription }}<br>{{ i18n.ts.sensitiveWordsDescription2 }}</template>
|
||||
</MkTextarea>
|
||||
<MkButton primary @click="save_sensitiveWords">{{ i18n.ts.save }}</MkButton>
|
||||
</div>
|
||||
</MkFolder>
|
||||
</SearchMarker>
|
||||
|
||||
<MkFolder>
|
||||
<template #icon><i class="ti ti-message-x"></i></template>
|
||||
<template #label>{{ i18n.ts.prohibitedWords }}</template>
|
||||
<SearchMarker :keywords="['prohibited', 'words']">
|
||||
<MkFolder>
|
||||
<template #icon><SearchIcon><i class="ti ti-message-x"></i></SearchIcon></template>
|
||||
<template #label><SearchLabel>{{ i18n.ts.prohibitedWords }}</SearchLabel></template>
|
||||
|
||||
<div class="_gaps">
|
||||
<MkTextarea v-model="prohibitedWords">
|
||||
<template #caption>{{ i18n.ts.prohibitedWordsDescription }}<br>{{ i18n.ts.prohibitedWordsDescription2 }}</template>
|
||||
</MkTextarea>
|
||||
<MkButton primary @click="save_prohibitedWords">{{ i18n.ts.save }}</MkButton>
|
||||
</div>
|
||||
</MkFolder>
|
||||
<div class="_gaps">
|
||||
<MkTextarea v-model="prohibitedWords">
|
||||
<template #caption>{{ i18n.ts.prohibitedWordsDescription }}<br>{{ i18n.ts.prohibitedWordsDescription2 }}</template>
|
||||
</MkTextarea>
|
||||
<MkButton primary @click="save_prohibitedWords">{{ i18n.ts.save }}</MkButton>
|
||||
</div>
|
||||
</MkFolder>
|
||||
</SearchMarker>
|
||||
|
||||
<MkFolder>
|
||||
<template #icon><i class="ti ti-user-x"></i></template>
|
||||
<template #label>{{ i18n.ts.prohibitedWordsForNameOfUser }}</template>
|
||||
<SearchMarker :keywords="['prohibited', 'name', 'user']">
|
||||
<MkFolder>
|
||||
<template #icon><SearchIcon><i class="ti ti-user-x"></i></SearchIcon></template>
|
||||
<template #label><SearchLabel>{{ i18n.ts.prohibitedWordsForNameOfUser }}</SearchLabel></template>
|
||||
|
||||
<div class="_gaps">
|
||||
<MkTextarea v-model="prohibitedWordsForNameOfUser">
|
||||
<template #caption>{{ i18n.ts.prohibitedWordsForNameOfUserDescription }}<br>{{ i18n.ts.prohibitedWordsDescription2 }}</template>
|
||||
</MkTextarea>
|
||||
<MkButton primary @click="save_prohibitedWordsForNameOfUser">{{ i18n.ts.save }}</MkButton>
|
||||
</div>
|
||||
</MkFolder>
|
||||
<div class="_gaps">
|
||||
<MkTextarea v-model="prohibitedWordsForNameOfUser">
|
||||
<template #caption>{{ i18n.ts.prohibitedWordsForNameOfUserDescription }}<br>{{ i18n.ts.prohibitedWordsDescription2 }}</template>
|
||||
</MkTextarea>
|
||||
<MkButton primary @click="save_prohibitedWordsForNameOfUser">{{ i18n.ts.save }}</MkButton>
|
||||
</div>
|
||||
</MkFolder>
|
||||
</SearchMarker>
|
||||
|
||||
<MkFolder>
|
||||
<template #icon><i class="ti ti-eye-off"></i></template>
|
||||
<template #label>{{ i18n.ts.hiddenTags }}</template>
|
||||
<SearchMarker :keywords="['hidden', 'tags', 'hashtags']">
|
||||
<MkFolder>
|
||||
<template #icon><SearchIcon><i class="ti ti-eye-off"></i></SearchIcon></template>
|
||||
<template #label><SearchLabel>{{ i18n.ts.hiddenTags }}</SearchLabel></template>
|
||||
|
||||
<div class="_gaps">
|
||||
<MkTextarea v-model="hiddenTags">
|
||||
<template #caption>{{ i18n.ts.hiddenTagsDescription }}</template>
|
||||
</MkTextarea>
|
||||
<MkButton primary @click="save_hiddenTags">{{ i18n.ts.save }}</MkButton>
|
||||
</div>
|
||||
</MkFolder>
|
||||
<div class="_gaps">
|
||||
<MkTextarea v-model="hiddenTags">
|
||||
<template #caption>{{ i18n.ts.hiddenTagsDescription }}</template>
|
||||
</MkTextarea>
|
||||
<MkButton primary @click="save_hiddenTags">{{ i18n.ts.save }}</MkButton>
|
||||
</div>
|
||||
</MkFolder>
|
||||
</SearchMarker>
|
||||
|
||||
<MkFolder>
|
||||
<template #icon><i class="ti ti-eye-off"></i></template>
|
||||
<template #label>{{ i18n.ts.silencedInstances }}</template>
|
||||
<SearchMarker :keywords="['silenced', 'servers', 'hosts']">
|
||||
<MkFolder>
|
||||
<template #icon><SearchIcon><i class="ti ti-eye-off"></i></SearchIcon></template>
|
||||
<template #label><SearchLabel>{{ i18n.ts.silencedInstances }}</SearchLabel></template>
|
||||
|
||||
<div class="_gaps">
|
||||
<MkTextarea v-model="silencedHosts">
|
||||
<template #caption>{{ i18n.ts.silencedInstancesDescription }}</template>
|
||||
</MkTextarea>
|
||||
<MkButton primary @click="save_silencedHosts">{{ i18n.ts.save }}</MkButton>
|
||||
</div>
|
||||
</MkFolder>
|
||||
<div class="_gaps">
|
||||
<MkTextarea v-model="silencedHosts">
|
||||
<template #caption>{{ i18n.ts.silencedInstancesDescription }}</template>
|
||||
</MkTextarea>
|
||||
<MkButton primary @click="save_silencedHosts">{{ i18n.ts.save }}</MkButton>
|
||||
</div>
|
||||
</MkFolder>
|
||||
</SearchMarker>
|
||||
|
||||
<MkFolder>
|
||||
<template #icon><i class="ti ti-eye-off"></i></template>
|
||||
<template #label>{{ i18n.ts.mediaSilencedInstances }}</template>
|
||||
<SearchMarker :keywords="['media', 'silenced', 'servers', 'hosts']">
|
||||
<MkFolder>
|
||||
<template #icon><SearchIcon><i class="ti ti-eye-off"></i></SearchIcon></template>
|
||||
<template #label><SearchLabel>{{ i18n.ts.mediaSilencedInstances }}</SearchLabel></template>
|
||||
|
||||
<div class="_gaps">
|
||||
<MkTextarea v-model="mediaSilencedHosts">
|
||||
<template #caption>{{ i18n.ts.mediaSilencedInstancesDescription }}</template>
|
||||
</MkTextarea>
|
||||
<MkButton primary @click="save_mediaSilencedHosts">{{ i18n.ts.save }}</MkButton>
|
||||
</div>
|
||||
</MkFolder>
|
||||
<div class="_gaps">
|
||||
<MkTextarea v-model="mediaSilencedHosts">
|
||||
<template #caption>{{ i18n.ts.mediaSilencedInstancesDescription }}</template>
|
||||
</MkTextarea>
|
||||
<MkButton primary @click="save_mediaSilencedHosts">{{ i18n.ts.save }}</MkButton>
|
||||
</div>
|
||||
</MkFolder>
|
||||
</SearchMarker>
|
||||
|
||||
<MkFolder>
|
||||
<template #icon><i class="ti ti-ban"></i></template>
|
||||
<template #label>{{ i18n.ts.blockedInstances }}</template>
|
||||
<SearchMarker :keywords="['blocked', 'servers', 'hosts']">
|
||||
<MkFolder>
|
||||
<template #icon><SearchIcon><i class="ti ti-ban"></i></SearchIcon></template>
|
||||
<template #label><SearchLabel>{{ i18n.ts.blockedInstances }}</SearchLabel></template>
|
||||
|
||||
<div class="_gaps">
|
||||
<MkTextarea v-model="blockedHosts">
|
||||
<template #caption>{{ i18n.ts.blockedInstancesDescription }}</template>
|
||||
</MkTextarea>
|
||||
<MkButton primary @click="save_blockedHosts">{{ i18n.ts.save }}</MkButton>
|
||||
</div>
|
||||
</MkFolder>
|
||||
<div class="_gaps">
|
||||
<MkTextarea v-model="blockedHosts">
|
||||
<template #caption>{{ i18n.ts.blockedInstancesDescription }}</template>
|
||||
</MkTextarea>
|
||||
<MkButton primary @click="save_blockedHosts">{{ i18n.ts.save }}</MkButton>
|
||||
</div>
|
||||
</MkFolder>
|
||||
</SearchMarker>
|
||||
</div>
|
||||
</FormSuspense>
|
||||
</SearchMarker>
|
||||
</div>
|
||||
</PageWithHeader>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed } from 'vue';
|
||||
import XServerRules from './server-rules.vue';
|
||||
import MkSwitch from '@/components/MkSwitch.vue';
|
||||
import MkInput from '@/components/MkInput.vue';
|
||||
import MkTextarea from '@/components/MkTextarea.vue';
|
||||
import FormSuspense from '@/components/form/suspense.vue';
|
||||
import * as os from '@/os.js';
|
||||
import { misskeyApi } from '@/utility/misskey-api.js';
|
||||
import { fetchInstance } from '@/instance.js';
|
||||
@@ -150,32 +172,19 @@ import FormLink from '@/components/form/link.vue';
|
||||
import MkFolder from '@/components/MkFolder.vue';
|
||||
import MkSelect from '@/components/MkSelect.vue';
|
||||
|
||||
const enableRegistration = ref<boolean>(false);
|
||||
const emailRequiredForSignup = ref<boolean>(false);
|
||||
const ugcVisibilityForVisitor = ref<string>('all');
|
||||
const sensitiveWords = ref<string>('');
|
||||
const prohibitedWords = ref<string>('');
|
||||
const prohibitedWordsForNameOfUser = ref<string>('');
|
||||
const hiddenTags = ref<string>('');
|
||||
const preservedUsernames = ref<string>('');
|
||||
const blockedHosts = ref<string>('');
|
||||
const silencedHosts = ref<string>('');
|
||||
const mediaSilencedHosts = ref<string>('');
|
||||
const meta = await misskeyApi('admin/meta');
|
||||
|
||||
async function init() {
|
||||
const meta = await misskeyApi('admin/meta');
|
||||
enableRegistration.value = !meta.disableRegistration;
|
||||
emailRequiredForSignup.value = meta.emailRequiredForSignup;
|
||||
ugcVisibilityForVisitor.value = meta.ugcVisibilityForVisitor;
|
||||
sensitiveWords.value = meta.sensitiveWords.join('\n');
|
||||
prohibitedWords.value = meta.prohibitedWords.join('\n');
|
||||
prohibitedWordsForNameOfUser.value = meta.prohibitedWordsForNameOfUser.join('\n');
|
||||
hiddenTags.value = meta.hiddenTags.join('\n');
|
||||
preservedUsernames.value = meta.preservedUsernames.join('\n');
|
||||
blockedHosts.value = meta.blockedHosts.join('\n');
|
||||
silencedHosts.value = meta.silencedHosts?.join('\n') ?? '';
|
||||
mediaSilencedHosts.value = meta.mediaSilencedHosts.join('\n');
|
||||
}
|
||||
const enableRegistration = ref(!meta.disableRegistration);
|
||||
const emailRequiredForSignup = ref(meta.emailRequiredForSignup);
|
||||
const ugcVisibilityForVisitor = ref(meta.ugcVisibilityForVisitor);
|
||||
const sensitiveWords = ref(meta.sensitiveWords.join('\n'));
|
||||
const prohibitedWords = ref(meta.prohibitedWords.join('\n'));
|
||||
const prohibitedWordsForNameOfUser = ref(meta.prohibitedWordsForNameOfUser.join('\n'));
|
||||
const hiddenTags = ref(meta.hiddenTags.join('\n'));
|
||||
const preservedUsernames = ref(meta.preservedUsernames.join('\n'));
|
||||
const blockedHosts = ref(meta.blockedHosts.join('\n'));
|
||||
const silencedHosts = ref(meta.silencedHosts?.join('\n') ?? '');
|
||||
const mediaSilencedHosts = ref(meta.mediaSilencedHosts.join('\n'));
|
||||
|
||||
async function onChange_enableRegistration(value: boolean) {
|
||||
if (value) {
|
||||
|
||||
@@ -6,70 +6,94 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<template>
|
||||
<PageWithHeader :tabs="headerTabs">
|
||||
<div class="_spacer" style="--MI_SPACER-w: 700px; --MI_SPACER-min: 16px; --MI_SPACER-max: 32px;">
|
||||
<FormSuspense :p="init">
|
||||
<SearchMarker path="/admin/object-storage" :label="i18n.ts.objectStorage" :keywords="['objectStorage']" icon="ti ti-cloud">
|
||||
<div class="_gaps_m">
|
||||
<MkSwitch v-model="useObjectStorage">{{ i18n.ts.useObjectStorage }}</MkSwitch>
|
||||
<SearchMarker>
|
||||
<MkSwitch v-model="useObjectStorage"><SearchLabel>{{ i18n.ts.useObjectStorage }}</SearchLabel></MkSwitch>
|
||||
</SearchMarker>
|
||||
|
||||
<template v-if="useObjectStorage">
|
||||
<MkInput v-model="objectStorageBaseUrl" :placeholder="'https://example.com'" type="url">
|
||||
<template #label>{{ i18n.ts.objectStorageBaseUrl }}</template>
|
||||
<template #caption>{{ i18n.ts.objectStorageBaseUrlDesc }}</template>
|
||||
</MkInput>
|
||||
<SearchMarker>
|
||||
<MkInput v-model="objectStorageBaseUrl" :placeholder="'https://example.com'" type="url">
|
||||
<template #label><SearchLabel>{{ i18n.ts.objectStorageBaseUrl }}</SearchLabel></template>
|
||||
<template #caption><SearchText>{{ i18n.ts.objectStorageBaseUrlDesc }}</SearchText></template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
|
||||
<MkInput v-model="objectStorageBucket">
|
||||
<template #label>{{ i18n.ts.objectStorageBucket }}</template>
|
||||
<template #caption>{{ i18n.ts.objectStorageBucketDesc }}</template>
|
||||
</MkInput>
|
||||
<SearchMarker>
|
||||
<MkInput v-model="objectStorageBucket">
|
||||
<template #label><SearchLabel>{{ i18n.ts.objectStorageBucket }}</SearchLabel></template>
|
||||
<template #caption><SearchText>{{ i18n.ts.objectStorageBucketDesc }}</SearchText></template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
|
||||
<MkInput v-model="objectStoragePrefix">
|
||||
<template #label>{{ i18n.ts.objectStoragePrefix }}</template>
|
||||
<template #caption>{{ i18n.ts.objectStoragePrefixDesc }}</template>
|
||||
</MkInput>
|
||||
<SearchMarker>
|
||||
<MkInput v-model="objectStoragePrefix">
|
||||
<template #label><SearchLabel>{{ i18n.ts.objectStoragePrefix }}</SearchLabel></template>
|
||||
<template #caption><SearchText>{{ i18n.ts.objectStoragePrefixDesc }}</SearchText></template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
|
||||
<MkInput v-model="objectStorageEndpoint" :placeholder="'example.com'">
|
||||
<template #label>{{ i18n.ts.objectStorageEndpoint }}</template>
|
||||
<template #prefix>https://</template>
|
||||
<template #caption>{{ i18n.ts.objectStorageEndpointDesc }}</template>
|
||||
</MkInput>
|
||||
<SearchMarker>
|
||||
<MkInput v-model="objectStorageEndpoint" :placeholder="'example.com'">
|
||||
<template #label><SearchLabel>{{ i18n.ts.objectStorageEndpoint }}</SearchLabel></template>
|
||||
<template #prefix>https://</template>
|
||||
<template #caption><SearchText>{{ i18n.ts.objectStorageEndpointDesc }}</SearchText></template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
|
||||
<MkInput v-model="objectStorageRegion">
|
||||
<template #label>{{ i18n.ts.objectStorageRegion }}</template>
|
||||
<template #caption>{{ i18n.ts.objectStorageRegionDesc }}</template>
|
||||
</MkInput>
|
||||
<SearchMarker>
|
||||
<MkInput v-model="objectStorageRegion">
|
||||
<template #label><SearchLabel>{{ i18n.ts.objectStorageRegion }}</SearchLabel></template>
|
||||
<template #caption><SearchText>{{ i18n.ts.objectStorageRegionDesc }}</SearchText></template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
|
||||
<FormSplit :minWidth="280">
|
||||
<MkInput v-model="objectStorageAccessKey">
|
||||
<template #prefix><i class="ti ti-key"></i></template>
|
||||
<template #label>Access key</template>
|
||||
</MkInput>
|
||||
<SearchMarker>
|
||||
<MkInput v-model="objectStorageAccessKey">
|
||||
<template #prefix><i class="ti ti-key"></i></template>
|
||||
<template #label><SearchLabel>Access key</SearchLabel></template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
|
||||
<MkInput v-model="objectStorageSecretKey" type="password">
|
||||
<template #prefix><i class="ti ti-key"></i></template>
|
||||
<template #label>Secret key</template>
|
||||
</MkInput>
|
||||
<SearchMarker>
|
||||
<MkInput v-model="objectStorageSecretKey" type="password">
|
||||
<template #prefix><i class="ti ti-key"></i></template>
|
||||
<template #label><SearchLabel>Secret key</SearchLabel></template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
</FormSplit>
|
||||
|
||||
<MkSwitch v-model="objectStorageUseSSL">
|
||||
<template #label>{{ i18n.ts.objectStorageUseSSL }}</template>
|
||||
<template #caption>{{ i18n.ts.objectStorageUseSSLDesc }}</template>
|
||||
</MkSwitch>
|
||||
<SearchMarker>
|
||||
<MkSwitch v-model="objectStorageUseSSL">
|
||||
<template #label><SearchLabel>{{ i18n.ts.objectStorageUseSSL }}</SearchLabel></template>
|
||||
<template #caption><SearchText>{{ i18n.ts.objectStorageUseSSLDesc }}</SearchText></template>
|
||||
</MkSwitch>
|
||||
</SearchMarker>
|
||||
|
||||
<MkSwitch v-model="objectStorageUseProxy">
|
||||
<template #label>{{ i18n.ts.objectStorageUseProxy }}</template>
|
||||
<template #caption>{{ i18n.ts.objectStorageUseProxyDesc }}</template>
|
||||
</MkSwitch>
|
||||
<SearchMarker>
|
||||
<MkSwitch v-model="objectStorageUseProxy">
|
||||
<template #label><SearchLabel>{{ i18n.ts.objectStorageUseProxy }}</SearchLabel></template>
|
||||
<template #caption><SearchText>{{ i18n.ts.objectStorageUseProxyDesc }}</SearchText></template>
|
||||
</MkSwitch>
|
||||
</SearchMarker>
|
||||
|
||||
<MkSwitch v-model="objectStorageSetPublicRead">
|
||||
<template #label>{{ i18n.ts.objectStorageSetPublicRead }}</template>
|
||||
</MkSwitch>
|
||||
<SearchMarker>
|
||||
<MkSwitch v-model="objectStorageSetPublicRead">
|
||||
<template #label><SearchLabel>{{ i18n.ts.objectStorageSetPublicRead }}</SearchLabel></template>
|
||||
</MkSwitch>
|
||||
</SearchMarker>
|
||||
|
||||
<MkSwitch v-model="objectStorageS3ForcePathStyle">
|
||||
<template #label>s3ForcePathStyle</template>
|
||||
<template #caption>{{ i18n.ts.s3ForcePathStyleDesc }}</template>
|
||||
</MkSwitch>
|
||||
<SearchMarker>
|
||||
<MkSwitch v-model="objectStorageS3ForcePathStyle">
|
||||
<template #label><SearchLabel>s3ForcePathStyle</SearchLabel></template>
|
||||
<template #caption><SearchText>{{ i18n.ts.s3ForcePathStyleDesc }}</SearchText></template>
|
||||
</MkSwitch>
|
||||
</SearchMarker>
|
||||
</template>
|
||||
</div>
|
||||
</FormSuspense>
|
||||
</SearchMarker>
|
||||
</div>
|
||||
<template #footer>
|
||||
<div :class="$style.footer">
|
||||
@@ -94,36 +118,21 @@ import { i18n } from '@/i18n.js';
|
||||
import { definePage } from '@/page.js';
|
||||
import MkButton from '@/components/MkButton.vue';
|
||||
|
||||
const useObjectStorage = ref<boolean>(false);
|
||||
const objectStorageBaseUrl = ref<string | null>(null);
|
||||
const objectStorageBucket = ref<string | null>(null);
|
||||
const objectStoragePrefix = ref<string | null>(null);
|
||||
const objectStorageEndpoint = ref<string | null>(null);
|
||||
const objectStorageRegion = ref<string | null>(null);
|
||||
const objectStoragePort = ref<number | null>(null);
|
||||
const objectStorageAccessKey = ref<string | null>(null);
|
||||
const objectStorageSecretKey = ref<string | null>(null);
|
||||
const objectStorageUseSSL = ref<boolean>(false);
|
||||
const objectStorageUseProxy = ref<boolean>(false);
|
||||
const objectStorageSetPublicRead = ref<boolean>(false);
|
||||
const objectStorageS3ForcePathStyle = ref<boolean>(true);
|
||||
const meta = await misskeyApi('admin/meta');
|
||||
|
||||
async function init() {
|
||||
const meta = await misskeyApi('admin/meta');
|
||||
useObjectStorage.value = meta.useObjectStorage;
|
||||
objectStorageBaseUrl.value = meta.objectStorageBaseUrl;
|
||||
objectStorageBucket.value = meta.objectStorageBucket;
|
||||
objectStoragePrefix.value = meta.objectStoragePrefix;
|
||||
objectStorageEndpoint.value = meta.objectStorageEndpoint;
|
||||
objectStorageRegion.value = meta.objectStorageRegion;
|
||||
objectStoragePort.value = meta.objectStoragePort;
|
||||
objectStorageAccessKey.value = meta.objectStorageAccessKey;
|
||||
objectStorageSecretKey.value = meta.objectStorageSecretKey;
|
||||
objectStorageUseSSL.value = meta.objectStorageUseSSL;
|
||||
objectStorageUseProxy.value = meta.objectStorageUseProxy;
|
||||
objectStorageSetPublicRead.value = meta.objectStorageSetPublicRead;
|
||||
objectStorageS3ForcePathStyle.value = meta.objectStorageS3ForcePathStyle;
|
||||
}
|
||||
const useObjectStorage = ref(meta.useObjectStorage);
|
||||
const objectStorageBaseUrl = ref(meta.objectStorageBaseUrl);
|
||||
const objectStorageBucket = ref(meta.objectStorageBucket);
|
||||
const objectStoragePrefix = ref(meta.objectStoragePrefix);
|
||||
const objectStorageEndpoint = ref(meta.objectStorageEndpoint);
|
||||
const objectStorageRegion = ref(meta.objectStorageRegion);
|
||||
const objectStoragePort = ref(meta.objectStoragePort);
|
||||
const objectStorageAccessKey = ref(meta.objectStorageAccessKey);
|
||||
const objectStorageSecretKey = ref(meta.objectStorageSecretKey);
|
||||
const objectStorageUseSSL = ref(meta.objectStorageUseSSL);
|
||||
const objectStorageUseProxy = ref(meta.objectStorageUseProxy);
|
||||
const objectStorageSetPublicRead = ref(meta.objectStorageSetPublicRead);
|
||||
const objectStorageS3ForcePathStyle = ref(meta.objectStorageS3ForcePathStyle);
|
||||
|
||||
function save() {
|
||||
os.apiWithDialog('admin/update-meta', {
|
||||
|
||||
@@ -6,131 +6,163 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<template>
|
||||
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
|
||||
<div class="_spacer" style="--MI_SPACER-w: 700px; --MI_SPACER-min: 16px; --MI_SPACER-max: 32px;">
|
||||
<div class="_gaps">
|
||||
<div class="_panel" style="padding: 16px;">
|
||||
<MkSwitch v-model="enableServerMachineStats" @change="onChange_enableServerMachineStats">
|
||||
<template #label>{{ i18n.ts.enableServerMachineStats }}</template>
|
||||
<template #caption>{{ i18n.ts.turnOffToImprovePerformance }}</template>
|
||||
</MkSwitch>
|
||||
</div>
|
||||
|
||||
<div class="_panel" style="padding: 16px;">
|
||||
<MkSwitch v-model="enableIdenticonGeneration" @change="onChange_enableIdenticonGeneration">
|
||||
<template #label>{{ i18n.ts.enableIdenticonGeneration }}</template>
|
||||
<template #caption>{{ i18n.ts.turnOffToImprovePerformance }}</template>
|
||||
</MkSwitch>
|
||||
</div>
|
||||
|
||||
<div class="_panel" style="padding: 16px;">
|
||||
<MkSwitch v-model="enableChartsForRemoteUser" @change="onChange_enableChartsForRemoteUser">
|
||||
<template #label>{{ i18n.ts.enableChartsForRemoteUser }}</template>
|
||||
<template #caption>{{ i18n.ts.turnOffToImprovePerformance }}</template>
|
||||
</MkSwitch>
|
||||
</div>
|
||||
|
||||
<div class="_panel" style="padding: 16px;">
|
||||
<MkSwitch v-model="enableStatsForFederatedInstances" @change="onChange_enableStatsForFederatedInstances">
|
||||
<template #label>{{ i18n.ts.enableStatsForFederatedInstances }}</template>
|
||||
<template #caption>{{ i18n.ts.turnOffToImprovePerformance }}</template>
|
||||
</MkSwitch>
|
||||
</div>
|
||||
|
||||
<div class="_panel" style="padding: 16px;">
|
||||
<MkSwitch v-model="enableChartsForFederatedInstances" @change="onChange_enableChartsForFederatedInstances">
|
||||
<template #label>{{ i18n.ts.enableChartsForFederatedInstances }}</template>
|
||||
<template #caption>{{ i18n.ts.turnOffToImprovePerformance }}</template>
|
||||
</MkSwitch>
|
||||
</div>
|
||||
|
||||
<MkFolder :defaultOpen="true">
|
||||
<template #icon><i class="ti ti-bolt"></i></template>
|
||||
<template #label>Misskey® Fan-out Timeline Technology™ (FTT)</template>
|
||||
<template v-if="fttForm.savedState.enableFanoutTimeline" #suffix>Enabled</template>
|
||||
<template v-else #suffix>Disabled</template>
|
||||
<template v-if="fttForm.modified.value" #footer>
|
||||
<MkFormFooter :form="fttForm"/>
|
||||
</template>
|
||||
|
||||
<div class="_gaps">
|
||||
<MkSwitch v-model="fttForm.state.enableFanoutTimeline">
|
||||
<template #label>{{ i18n.ts.enable }}<span v-if="fttForm.modifiedStates.enableFanoutTimeline" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption>
|
||||
<div>{{ i18n.ts._serverSettings.fanoutTimelineDescription }}</div>
|
||||
<div><MkLink target="_blank" url="https://misskey-hub.net/docs/for-admin/features/ftt/">{{ i18n.ts.details }}</MkLink></div>
|
||||
</template>
|
||||
</MkSwitch>
|
||||
|
||||
<template v-if="fttForm.state.enableFanoutTimeline">
|
||||
<MkSwitch v-model="fttForm.state.enableFanoutTimelineDbFallback">
|
||||
<template #label>{{ i18n.ts._serverSettings.fanoutTimelineDbFallback }}<span v-if="fttForm.modifiedStates.enableFanoutTimelineDbFallback" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption>{{ i18n.ts._serverSettings.fanoutTimelineDbFallbackDescription }}</template>
|
||||
<SearchMarker path="/admin/performance" :label="i18n.ts.performance" :keywords="['performance']" icon="ti ti-bolt">
|
||||
<div class="_gaps">
|
||||
<SearchMarker>
|
||||
<div class="_panel" style="padding: 16px;">
|
||||
<MkSwitch v-model="enableServerMachineStats" @change="onChange_enableServerMachineStats">
|
||||
<template #label><SearchLabel>{{ i18n.ts.enableServerMachineStats }}</SearchLabel></template>
|
||||
<template #caption>{{ i18n.ts.turnOffToImprovePerformance }}</template>
|
||||
</MkSwitch>
|
||||
</div>
|
||||
</SearchMarker>
|
||||
|
||||
<MkInput v-model="fttForm.state.perLocalUserUserTimelineCacheMax" type="number">
|
||||
<template #label>perLocalUserUserTimelineCacheMax<span v-if="fttForm.modifiedStates.perLocalUserUserTimelineCacheMax" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
</MkInput>
|
||||
<SearchMarker>
|
||||
<div class="_panel" style="padding: 16px;">
|
||||
<MkSwitch v-model="enableIdenticonGeneration" @change="onChange_enableIdenticonGeneration">
|
||||
<template #label><SearchLabel>{{ i18n.ts.enableIdenticonGeneration }}</SearchLabel></template>
|
||||
<template #caption>{{ i18n.ts.turnOffToImprovePerformance }}</template>
|
||||
</MkSwitch>
|
||||
</div>
|
||||
</SearchMarker>
|
||||
|
||||
<MkInput v-model="fttForm.state.perRemoteUserUserTimelineCacheMax" type="number">
|
||||
<template #label>perRemoteUserUserTimelineCacheMax<span v-if="fttForm.modifiedStates.perRemoteUserUserTimelineCacheMax" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
</MkInput>
|
||||
<SearchMarker>
|
||||
<div class="_panel" style="padding: 16px;">
|
||||
<MkSwitch v-model="enableChartsForRemoteUser" @change="onChange_enableChartsForRemoteUser">
|
||||
<template #label><SearchLabel>{{ i18n.ts.enableChartsForRemoteUser }}</SearchLabel></template>
|
||||
<template #caption>{{ i18n.ts.turnOffToImprovePerformance }}</template>
|
||||
</MkSwitch>
|
||||
</div>
|
||||
</SearchMarker>
|
||||
|
||||
<MkInput v-model="fttForm.state.perUserHomeTimelineCacheMax" type="number">
|
||||
<template #label>perUserHomeTimelineCacheMax<span v-if="fttForm.modifiedStates.perUserHomeTimelineCacheMax" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
</MkInput>
|
||||
<SearchMarker>
|
||||
<div class="_panel" style="padding: 16px;">
|
||||
<MkSwitch v-model="enableStatsForFederatedInstances" @change="onChange_enableStatsForFederatedInstances">
|
||||
<template #label><SearchLabel>{{ i18n.ts.enableStatsForFederatedInstances }}</SearchLabel></template>
|
||||
<template #caption>{{ i18n.ts.turnOffToImprovePerformance }}</template>
|
||||
</MkSwitch>
|
||||
</div>
|
||||
</SearchMarker>
|
||||
|
||||
<MkInput v-model="fttForm.state.perUserListTimelineCacheMax" type="number">
|
||||
<template #label>perUserListTimelineCacheMax<span v-if="fttForm.modifiedStates.perUserListTimelineCacheMax" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
</MkInput>
|
||||
</template>
|
||||
</div>
|
||||
</MkFolder>
|
||||
<SearchMarker>
|
||||
<div class="_panel" style="padding: 16px;">
|
||||
<MkSwitch v-model="enableChartsForFederatedInstances" @change="onChange_enableChartsForFederatedInstances">
|
||||
<template #label><SearchLabel>{{ i18n.ts.enableChartsForFederatedInstances }}</SearchLabel></template>
|
||||
<template #caption>{{ i18n.ts.turnOffToImprovePerformance }}</template>
|
||||
</MkSwitch>
|
||||
</div>
|
||||
</SearchMarker>
|
||||
|
||||
<MkFolder :defaultOpen="true">
|
||||
<template #icon><i class="ti ti-bolt"></i></template>
|
||||
<template #label>Misskey® Reactions Boost Technology™ (RBT)<span class="_beta">{{ i18n.ts.beta }}</span></template>
|
||||
<template v-if="rbtForm.savedState.enableReactionsBuffering" #suffix>Enabled</template>
|
||||
<template v-else #suffix>Disabled</template>
|
||||
<template v-if="rbtForm.modified.value" #footer>
|
||||
<MkFormFooter :form="rbtForm"/>
|
||||
</template>
|
||||
<SearchMarker>
|
||||
<MkFolder :defaultOpen="true">
|
||||
<template #icon><SearchIcon><i class="ti ti-bolt"></i></SearchIcon></template>
|
||||
<template #label><SearchLabel>Misskey® Fan-out Timeline Technology™ (FTT)</SearchLabel></template>
|
||||
<template v-if="fttForm.savedState.enableFanoutTimeline" #suffix>Enabled</template>
|
||||
<template v-else #suffix>Disabled</template>
|
||||
<template v-if="fttForm.modified.value" #footer>
|
||||
<MkFormFooter :form="fttForm"/>
|
||||
</template>
|
||||
|
||||
<div class="_gaps_m">
|
||||
<MkSwitch v-model="rbtForm.state.enableReactionsBuffering">
|
||||
<template #label>{{ i18n.ts.enable }}<span v-if="rbtForm.modifiedStates.enableReactionsBuffering" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption>{{ i18n.ts._serverSettings.reactionsBufferingDescription }}</template>
|
||||
</MkSwitch>
|
||||
</div>
|
||||
</MkFolder>
|
||||
<div class="_gaps">
|
||||
<SearchMarker>
|
||||
<MkSwitch v-model="fttForm.state.enableFanoutTimeline">
|
||||
<template #label><SearchLabel>{{ i18n.ts.enable }}</SearchLabel><span v-if="fttForm.modifiedStates.enableFanoutTimeline" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption>
|
||||
<div><SearchText>{{ i18n.ts._serverSettings.fanoutTimelineDescription }}</SearchText></div>
|
||||
<div><MkLink target="_blank" url="https://misskey-hub.net/docs/for-admin/features/ftt/">{{ i18n.ts.details }}</MkLink></div>
|
||||
</template>
|
||||
</MkSwitch>
|
||||
</SearchMarker>
|
||||
|
||||
<MkFolder :defaultOpen="true">
|
||||
<template #icon><i class="ti ti-recycle"></i></template>
|
||||
<template #label>Remote Notes Cleaning (仮)</template>
|
||||
<template v-if="remoteNotesCleaningForm.savedState.enableRemoteNotesCleaning" #suffix>Enabled</template>
|
||||
<template v-else #suffix>Disabled</template>
|
||||
<template v-if="remoteNotesCleaningForm.modified.value" #footer>
|
||||
<MkFormFooter :form="remoteNotesCleaningForm"/>
|
||||
</template>
|
||||
<template v-if="fttForm.state.enableFanoutTimeline">
|
||||
<SearchMarker :keywords="['db', 'database', 'fallback']">
|
||||
<MkSwitch v-model="fttForm.state.enableFanoutTimelineDbFallback">
|
||||
<template #label><SearchLabel>{{ i18n.ts._serverSettings.fanoutTimelineDbFallback }}</SearchLabel><span v-if="fttForm.modifiedStates.enableFanoutTimelineDbFallback" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption><SearchText>{{ i18n.ts._serverSettings.fanoutTimelineDbFallbackDescription }}</SearchText></template>
|
||||
</MkSwitch>
|
||||
</SearchMarker>
|
||||
|
||||
<div class="_gaps_m">
|
||||
<MkSwitch v-model="remoteNotesCleaningForm.state.enableRemoteNotesCleaning">
|
||||
<template #label>{{ i18n.ts.enable }}<span v-if="remoteNotesCleaningForm.modifiedStates.enableRemoteNotesCleaning" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption>{{ i18n.ts._serverSettings.remoteNotesCleaning_description }}</template>
|
||||
</MkSwitch>
|
||||
<SearchMarker>
|
||||
<MkInput v-model="fttForm.state.perLocalUserUserTimelineCacheMax" type="number">
|
||||
<template #label><SearchLabel>perLocalUserUserTimelineCacheMax</SearchLabel><span v-if="fttForm.modifiedStates.perLocalUserUserTimelineCacheMax" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
|
||||
<template v-if="remoteNotesCleaningForm.state.enableRemoteNotesCleaning">
|
||||
<MkInput v-model="remoteNotesCleaningForm.state.remoteNotesCleaningExpiryDaysForEachNotes" type="number">
|
||||
<template #label>{{ i18n.ts._serverSettings.remoteNotesCleaningExpiryDaysForEachNotes }} ({{ i18n.ts.inDays }})<span v-if="remoteNotesCleaningForm.modifiedStates.remoteNotesCleaningExpiryDaysForEachNotes" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #suffix>{{ i18n.ts._time.day }}</template>
|
||||
</MkInput>
|
||||
<SearchMarker>
|
||||
<MkInput v-model="fttForm.state.perRemoteUserUserTimelineCacheMax" type="number">
|
||||
<template #label><SearchLabel>perRemoteUserUserTimelineCacheMax</SearchLabel><span v-if="fttForm.modifiedStates.perRemoteUserUserTimelineCacheMax" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
|
||||
<MkInput v-model="remoteNotesCleaningForm.state.remoteNotesCleaningMaxProcessingDurationInMinutes" type="number">
|
||||
<template #label>{{ i18n.ts._serverSettings.remoteNotesCleaningMaxProcessingDuration }} ({{ i18n.ts.inMinutes }})<span v-if="remoteNotesCleaningForm.modifiedStates.remoteNotesCleaningMaxProcessingDurationInMinutes" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #suffix>{{ i18n.ts._time.minute }}</template>
|
||||
</MkInput>
|
||||
</template>
|
||||
</div>
|
||||
</MkFolder>
|
||||
</div>
|
||||
<SearchMarker>
|
||||
<MkInput v-model="fttForm.state.perUserHomeTimelineCacheMax" type="number">
|
||||
<template #label><SearchLabel>perUserHomeTimelineCacheMax</SearchLabel><span v-if="fttForm.modifiedStates.perUserHomeTimelineCacheMax" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
|
||||
<SearchMarker>
|
||||
<MkInput v-model="fttForm.state.perUserListTimelineCacheMax" type="number">
|
||||
<template #label><SearchLabel>perUserListTimelineCacheMax</SearchLabel><span v-if="fttForm.modifiedStates.perUserListTimelineCacheMax" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
</template>
|
||||
</div>
|
||||
</MkFolder>
|
||||
</SearchMarker>
|
||||
|
||||
<SearchMarker>
|
||||
<MkFolder :defaultOpen="true">
|
||||
<template #icon><SearchIcon><i class="ti ti-bolt"></i></SearchIcon></template>
|
||||
<template #label><SearchLabel>Misskey® Reactions Boost Technology™ (RBT)</SearchLabel><span class="_beta">{{ i18n.ts.beta }}</span></template>
|
||||
<template v-if="rbtForm.savedState.enableReactionsBuffering" #suffix>Enabled</template>
|
||||
<template v-else #suffix>Disabled</template>
|
||||
<template v-if="rbtForm.modified.value" #footer>
|
||||
<MkFormFooter :form="rbtForm"/>
|
||||
</template>
|
||||
|
||||
<div class="_gaps_m">
|
||||
<SearchMarker>
|
||||
<MkSwitch v-model="rbtForm.state.enableReactionsBuffering">
|
||||
<template #label><SearchLabel>{{ i18n.ts.enable }}</SearchLabel><span v-if="rbtForm.modifiedStates.enableReactionsBuffering" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption><SearchText>{{ i18n.ts._serverSettings.reactionsBufferingDescription }}</SearchText></template>
|
||||
</MkSwitch>
|
||||
</SearchMarker>
|
||||
</div>
|
||||
</MkFolder>
|
||||
</SearchMarker>
|
||||
|
||||
<SearchMarker>
|
||||
<MkFolder :defaultOpen="true">
|
||||
<template #icon><SearchIcon><i class="ti ti-recycle"></i></SearchIcon></template>
|
||||
<template #label><SearchLabel>Remote Notes Cleaning (仮)</SearchLabel></template>
|
||||
<template v-if="remoteNotesCleaningForm.savedState.enableRemoteNotesCleaning" #suffix>Enabled</template>
|
||||
<template v-else #suffix>Disabled</template>
|
||||
<template v-if="remoteNotesCleaningForm.modified.value" #footer>
|
||||
<MkFormFooter :form="remoteNotesCleaningForm"/>
|
||||
</template>
|
||||
|
||||
<div class="_gaps_m">
|
||||
<MkSwitch v-model="remoteNotesCleaningForm.state.enableRemoteNotesCleaning">
|
||||
<template #label><SearchLabel>{{ i18n.ts.enable }}</SearchLabel><span v-if="remoteNotesCleaningForm.modifiedStates.enableRemoteNotesCleaning" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption><SearchText>{{ i18n.ts._serverSettings.remoteNotesCleaning_description }}</SearchText></template>
|
||||
</MkSwitch>
|
||||
|
||||
<template v-if="remoteNotesCleaningForm.state.enableRemoteNotesCleaning">
|
||||
<MkInput v-model="remoteNotesCleaningForm.state.remoteNotesCleaningExpiryDaysForEachNotes" type="number">
|
||||
<template #label><SearchLabel>{{ i18n.ts._serverSettings.remoteNotesCleaningExpiryDaysForEachNotes }}</SearchLabel> ({{ i18n.ts.inDays }})<span v-if="remoteNotesCleaningForm.modifiedStates.remoteNotesCleaningExpiryDaysForEachNotes" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #suffix>{{ i18n.ts._time.day }}</template>
|
||||
</MkInput>
|
||||
|
||||
<MkInput v-model="remoteNotesCleaningForm.state.remoteNotesCleaningMaxProcessingDurationInMinutes" type="number">
|
||||
<template #label><SearchLabel>{{ i18n.ts._serverSettings.remoteNotesCleaningMaxProcessingDuration }}</SearchLabel> ({{ i18n.ts.inMinutes }})<span v-if="remoteNotesCleaningForm.modifiedStates.remoteNotesCleaningMaxProcessingDurationInMinutes" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #suffix>{{ i18n.ts._time.minute }}</template>
|
||||
</MkInput>
|
||||
</template>
|
||||
</div>
|
||||
</MkFolder>
|
||||
</SearchMarker>
|
||||
</div>
|
||||
</SearchMarker>
|
||||
</div>
|
||||
</PageWithHeader>
|
||||
</template>
|
||||
@@ -243,7 +275,7 @@ const headerActions = computed(() => []);
|
||||
const headerTabs = computed(() => []);
|
||||
|
||||
definePage(() => ({
|
||||
title: i18n.ts.other,
|
||||
icon: 'ti ti-adjustments',
|
||||
title: i18n.ts.performance,
|
||||
icon: 'ti ti-bolt',
|
||||
}));
|
||||
</script>
|
||||
|
||||
@@ -6,18 +6,20 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<template>
|
||||
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
|
||||
<div class="_spacer" style="--MI_SPACER-w: 800px;">
|
||||
<div class="_gaps">
|
||||
<div v-for="relay in relays" :key="relay.inbox" class="relaycxt _panel" style="padding: 16px;">
|
||||
<div>{{ relay.inbox }}</div>
|
||||
<div style="margin: 8px 0;">
|
||||
<i v-if="relay.status === 'accepted'" class="ti ti-check" :class="$style.icon" style="color: var(--MI_THEME-success);"></i>
|
||||
<i v-else-if="relay.status === 'rejected'" class="ti ti-ban" :class="$style.icon" style="color: var(--MI_THEME-error);"></i>
|
||||
<i v-else class="ti ti-clock" :class="$style.icon"></i>
|
||||
<span>{{ i18n.ts._relayStatus[relay.status] }}</span>
|
||||
<SearchMarker path="/admin/relays" :label="i18n.ts.relays" :keywords="['relays']" icon="ti ti-planet">
|
||||
<div class="_gaps">
|
||||
<div v-for="relay in relays" :key="relay.inbox" class="relaycxt _panel" style="padding: 16px;">
|
||||
<div>{{ relay.inbox }}</div>
|
||||
<div style="margin: 8px 0;">
|
||||
<i v-if="relay.status === 'accepted'" class="ti ti-check" :class="$style.icon" style="color: var(--MI_THEME-success);"></i>
|
||||
<i v-else-if="relay.status === 'rejected'" class="ti ti-ban" :class="$style.icon" style="color: var(--MI_THEME-error);"></i>
|
||||
<i v-else class="ti ti-clock" :class="$style.icon"></i>
|
||||
<span>{{ i18n.ts._relayStatus[relay.status] }}</span>
|
||||
</div>
|
||||
<MkButton class="button" inline danger @click="remove(relay.inbox)"><i class="ti ti-trash"></i> {{ i18n.ts.remove }}</MkButton>
|
||||
</div>
|
||||
<MkButton class="button" inline danger @click="remove(relay.inbox)"><i class="ti ti-trash"></i> {{ i18n.ts.remove }}</MkButton>
|
||||
</div>
|
||||
</div>
|
||||
</SearchMarker>
|
||||
</div>
|
||||
</PageWithHeader>
|
||||
</template>
|
||||
|
||||
@@ -6,115 +6,153 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<template>
|
||||
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
|
||||
<div class="_spacer" style="--MI_SPACER-w: 700px; --MI_SPACER-min: 16px; --MI_SPACER-max: 32px;">
|
||||
<div class="_gaps_m">
|
||||
<XBotProtection/>
|
||||
<SearchMarker path="/admin/security" :label="i18n.ts.security" :keywords="['security']" icon="ti ti-lock" :inlining="['botProtection']">
|
||||
<div class="_gaps_m">
|
||||
<XBotProtection/>
|
||||
|
||||
<MkFolder>
|
||||
<template #icon><i class="ti ti-eye-off"></i></template>
|
||||
<template #label>{{ i18n.ts.sensitiveMediaDetection }}</template>
|
||||
<template v-if="sensitiveMediaDetectionForm.savedState.sensitiveMediaDetection === 'all'" #suffix>{{ i18n.ts.all }}</template>
|
||||
<template v-else-if="sensitiveMediaDetectionForm.savedState.sensitiveMediaDetection === 'local'" #suffix>{{ i18n.ts.localOnly }}</template>
|
||||
<template v-else-if="sensitiveMediaDetectionForm.savedState.sensitiveMediaDetection === 'remote'" #suffix>{{ i18n.ts.remoteOnly }}</template>
|
||||
<template v-else #suffix>{{ i18n.ts.none }}</template>
|
||||
<template v-if="sensitiveMediaDetectionForm.modified.value" #footer>
|
||||
<MkFormFooter :form="sensitiveMediaDetectionForm"/>
|
||||
</template>
|
||||
<SearchMarker v-slot="slotProps" :keywords="['sensitive', 'media', 'detection']">
|
||||
<MkFolder :defaultOpen="slotProps.isParentOfTarget">
|
||||
<template #icon><SearchIcon><i class="ti ti-eye-off"></i></SearchIcon></template>
|
||||
<template #label><SearchLabel>{{ i18n.ts.sensitiveMediaDetection }}</SearchLabel></template>
|
||||
<template v-if="sensitiveMediaDetectionForm.savedState.sensitiveMediaDetection === 'all'" #suffix>{{ i18n.ts.all }}</template>
|
||||
<template v-else-if="sensitiveMediaDetectionForm.savedState.sensitiveMediaDetection === 'local'" #suffix>{{ i18n.ts.localOnly }}</template>
|
||||
<template v-else-if="sensitiveMediaDetectionForm.savedState.sensitiveMediaDetection === 'remote'" #suffix>{{ i18n.ts.remoteOnly }}</template>
|
||||
<template v-else #suffix>{{ i18n.ts.none }}</template>
|
||||
<template v-if="sensitiveMediaDetectionForm.modified.value" #footer>
|
||||
<MkFormFooter :form="sensitiveMediaDetectionForm"/>
|
||||
</template>
|
||||
|
||||
<div class="_gaps_m">
|
||||
<span>{{ i18n.ts._sensitiveMediaDetection.description }}</span>
|
||||
<div class="_gaps_m">
|
||||
<div><SearchText>{{ i18n.ts._sensitiveMediaDetection.description }}</SearchText></div>
|
||||
|
||||
<MkRadios v-model="sensitiveMediaDetectionForm.state.sensitiveMediaDetection">
|
||||
<option value="none">{{ i18n.ts.none }}</option>
|
||||
<option value="all">{{ i18n.ts.all }}</option>
|
||||
<option value="local">{{ i18n.ts.localOnly }}</option>
|
||||
<option value="remote">{{ i18n.ts.remoteOnly }}</option>
|
||||
</MkRadios>
|
||||
<MkRadios v-model="sensitiveMediaDetectionForm.state.sensitiveMediaDetection">
|
||||
<option value="none">{{ i18n.ts.none }}</option>
|
||||
<option value="all">{{ i18n.ts.all }}</option>
|
||||
<option value="local">{{ i18n.ts.localOnly }}</option>
|
||||
<option value="remote">{{ i18n.ts.remoteOnly }}</option>
|
||||
</MkRadios>
|
||||
|
||||
<MkRange v-model="sensitiveMediaDetectionForm.state.sensitiveMediaDetectionSensitivity" :min="0" :max="4" :step="1" :textConverter="(v) => `${v + 1}`">
|
||||
<template #label>{{ i18n.ts._sensitiveMediaDetection.sensitivity }}</template>
|
||||
<template #caption>{{ i18n.ts._sensitiveMediaDetection.sensitivityDescription }}</template>
|
||||
</MkRange>
|
||||
<SearchMarker :keywords="['sensitivity']">
|
||||
<MkRange v-model="sensitiveMediaDetectionForm.state.sensitiveMediaDetectionSensitivity" :min="0" :max="4" :step="1" :textConverter="(v) => `${v + 1}`">
|
||||
<template #label><SearchLabel>{{ i18n.ts._sensitiveMediaDetection.sensitivity }}</SearchLabel></template>
|
||||
<template #caption><SearchText>{{ i18n.ts._sensitiveMediaDetection.sensitivityDescription }}</SearchText></template>
|
||||
</MkRange>
|
||||
</SearchMarker>
|
||||
|
||||
<MkSwitch v-model="sensitiveMediaDetectionForm.state.enableSensitiveMediaDetectionForVideos">
|
||||
<template #label>{{ i18n.ts._sensitiveMediaDetection.analyzeVideos }}<span class="_beta">{{ i18n.ts.beta }}</span></template>
|
||||
<template #caption>{{ i18n.ts._sensitiveMediaDetection.analyzeVideosDescription }}</template>
|
||||
</MkSwitch>
|
||||
<SearchMarker :keywords="['video', 'analyze']">
|
||||
<MkSwitch v-model="sensitiveMediaDetectionForm.state.enableSensitiveMediaDetectionForVideos">
|
||||
<template #label><SearchLabel>{{ i18n.ts._sensitiveMediaDetection.analyzeVideos }}</SearchLabel><span class="_beta">{{ i18n.ts.beta }}</span></template>
|
||||
<template #caption><SearchText>{{ i18n.ts._sensitiveMediaDetection.analyzeVideosDescription }}</SearchText></template>
|
||||
</MkSwitch>
|
||||
</SearchMarker>
|
||||
|
||||
<MkSwitch v-model="sensitiveMediaDetectionForm.state.setSensitiveFlagAutomatically">
|
||||
<template #label>{{ i18n.ts._sensitiveMediaDetection.setSensitiveFlagAutomatically }} ({{ i18n.ts.notRecommended }})</template>
|
||||
<template #caption>{{ i18n.ts._sensitiveMediaDetection.setSensitiveFlagAutomaticallyDescription }}</template>
|
||||
</MkSwitch>
|
||||
<SearchMarker :keywords="['flag', 'automatically']">
|
||||
<MkSwitch v-model="sensitiveMediaDetectionForm.state.setSensitiveFlagAutomatically">
|
||||
<template #label><SearchLabel>{{ i18n.ts._sensitiveMediaDetection.setSensitiveFlagAutomatically }}</SearchLabel> ({{ i18n.ts.notRecommended }})</template>
|
||||
<template #caption><SearchText>{{ i18n.ts._sensitiveMediaDetection.setSensitiveFlagAutomaticallyDescription }}</SearchText></template>
|
||||
</MkSwitch>
|
||||
</SearchMarker>
|
||||
|
||||
<!-- 現状 false positive が多すぎて実用に耐えない
|
||||
<!-- 現状 false positive が多すぎて実用に耐えない
|
||||
<MkSwitch v-model="disallowUploadWhenPredictedAsPorn">
|
||||
<template #label>{{ i18n.ts._sensitiveMediaDetection.disallowUploadWhenPredictedAsPorn }}</template>
|
||||
</MkSwitch>
|
||||
-->
|
||||
</div>
|
||||
</MkFolder>
|
||||
</div>
|
||||
</MkFolder>
|
||||
</SearchMarker>
|
||||
|
||||
<MkFolder>
|
||||
<template #label>Active Email Validation</template>
|
||||
<template v-if="emailValidationForm.savedState.enableActiveEmailValidation" #suffix>Enabled</template>
|
||||
<template v-else #suffix>Disabled</template>
|
||||
<template v-if="emailValidationForm.modified.value" #footer>
|
||||
<MkFormFooter :form="emailValidationForm"/>
|
||||
</template>
|
||||
<SearchMarker v-slot="slotProps" :keywords="['email', 'validation']">
|
||||
<MkFolder :defaultOpen="slotProps.isParentOfTarget">
|
||||
<template #label><SearchLabel>Active Email Validation</SearchLabel></template>
|
||||
<template v-if="emailValidationForm.savedState.enableActiveEmailValidation" #suffix>Enabled</template>
|
||||
<template v-else #suffix>Disabled</template>
|
||||
<template v-if="emailValidationForm.modified.value" #footer>
|
||||
<MkFormFooter :form="emailValidationForm"/>
|
||||
</template>
|
||||
|
||||
<div class="_gaps_m">
|
||||
<span>{{ i18n.ts.activeEmailValidationDescription }}</span>
|
||||
<MkSwitch v-model="emailValidationForm.state.enableActiveEmailValidation">
|
||||
<template #label>Enable</template>
|
||||
</MkSwitch>
|
||||
<MkSwitch v-model="emailValidationForm.state.enableVerifymailApi">
|
||||
<template #label>Use Verifymail.io API</template>
|
||||
</MkSwitch>
|
||||
<MkInput v-model="emailValidationForm.state.verifymailAuthKey">
|
||||
<template #prefix><i class="ti ti-key"></i></template>
|
||||
<template #label>Verifymail.io API Auth Key</template>
|
||||
</MkInput>
|
||||
<MkSwitch v-model="emailValidationForm.state.enableTruemailApi">
|
||||
<template #label>Use TrueMail API</template>
|
||||
</MkSwitch>
|
||||
<MkInput v-model="emailValidationForm.state.truemailInstance">
|
||||
<template #prefix><i class="ti ti-key"></i></template>
|
||||
<template #label>TrueMail API Instance</template>
|
||||
</MkInput>
|
||||
<MkInput v-model="emailValidationForm.state.truemailAuthKey">
|
||||
<template #prefix><i class="ti ti-key"></i></template>
|
||||
<template #label>TrueMail API Auth Key</template>
|
||||
</MkInput>
|
||||
</div>
|
||||
</MkFolder>
|
||||
<div class="_gaps_m">
|
||||
<div><SearchText>{{ i18n.ts.activeEmailValidationDescription }}</SearchText></div>
|
||||
|
||||
<MkFolder>
|
||||
<template #label>Banned Email Domains</template>
|
||||
<template v-if="bannedEmailDomainsForm.modified.value" #footer>
|
||||
<MkFormFooter :form="bannedEmailDomainsForm"/>
|
||||
</template>
|
||||
<SearchMarker>
|
||||
<MkSwitch v-model="emailValidationForm.state.enableActiveEmailValidation">
|
||||
<template #label><SearchLabel>Enable</SearchLabel></template>
|
||||
</MkSwitch>
|
||||
</SearchMarker>
|
||||
|
||||
<div class="_gaps_m">
|
||||
<MkTextarea v-model="bannedEmailDomainsForm.state.bannedEmailDomains">
|
||||
<template #label>Banned Email Domains List</template>
|
||||
</MkTextarea>
|
||||
</div>
|
||||
</MkFolder>
|
||||
<SearchMarker>
|
||||
<MkSwitch v-model="emailValidationForm.state.enableVerifymailApi">
|
||||
<template #label><SearchLabel>Use Verifymail.io API</SearchLabel></template>
|
||||
</MkSwitch>
|
||||
</SearchMarker>
|
||||
|
||||
<MkFolder>
|
||||
<template #label>Log IP address</template>
|
||||
<template v-if="ipLoggingForm.savedState.enableIpLogging" #suffix>Enabled</template>
|
||||
<template v-else #suffix>Disabled</template>
|
||||
<template v-if="ipLoggingForm.modified.value" #footer>
|
||||
<MkFormFooter :form="ipLoggingForm"/>
|
||||
</template>
|
||||
<SearchMarker>
|
||||
<MkInput v-model="emailValidationForm.state.verifymailAuthKey">
|
||||
<template #prefix><i class="ti ti-key"></i></template>
|
||||
<template #label><SearchLabel>Verifymail.io API Auth Key</SearchLabel></template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
|
||||
<div class="_gaps_m">
|
||||
<MkSwitch v-model="ipLoggingForm.state.enableIpLogging">
|
||||
<template #label>Enable</template>
|
||||
</MkSwitch>
|
||||
</div>
|
||||
</MkFolder>
|
||||
</div>
|
||||
<SearchMarker>
|
||||
<MkSwitch v-model="emailValidationForm.state.enableTruemailApi">
|
||||
<template #label><SearchLabel>Use TrueMail API</SearchLabel></template>
|
||||
</MkSwitch>
|
||||
</SearchMarker>
|
||||
|
||||
<SearchMarker>
|
||||
<MkInput v-model="emailValidationForm.state.truemailInstance">
|
||||
<template #prefix><i class="ti ti-key"></i></template>
|
||||
<template #label><SearchLabel>TrueMail API Instance</SearchLabel></template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
|
||||
<SearchMarker>
|
||||
<MkInput v-model="emailValidationForm.state.truemailAuthKey">
|
||||
<template #prefix><i class="ti ti-key"></i></template>
|
||||
<template #label><SearchLabel>TrueMail API Auth Key</SearchLabel></template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
</div>
|
||||
</MkFolder>
|
||||
</SearchMarker>
|
||||
|
||||
<SearchMarker v-slot="slotProps" :keywords="['banned', 'email', 'domains', 'blacklist']">
|
||||
<MkFolder :defaultOpen="slotProps.isParentOfTarget">
|
||||
<template #label><SearchLabel>Banned Email Domains</SearchLabel></template>
|
||||
<template v-if="bannedEmailDomainsForm.modified.value" #footer>
|
||||
<MkFormFooter :form="bannedEmailDomainsForm"/>
|
||||
</template>
|
||||
|
||||
<div class="_gaps_m">
|
||||
<SearchMarker>
|
||||
<MkTextarea v-model="bannedEmailDomainsForm.state.bannedEmailDomains">
|
||||
<template #label><SearchLabel>Banned Email Domains List</SearchLabel></template>
|
||||
</MkTextarea>
|
||||
</SearchMarker>
|
||||
</div>
|
||||
</MkFolder>
|
||||
</SearchMarker>
|
||||
|
||||
<SearchMarker v-slot="slotProps" :keywords="['log', 'ipAddress']">
|
||||
<MkFolder :defaultOpen="slotProps.isParentOfTarget">
|
||||
<template #label><SearchLabel>Log IP address</SearchLabel></template>
|
||||
<template v-if="ipLoggingForm.savedState.enableIpLogging" #suffix>Enabled</template>
|
||||
<template v-else #suffix>Disabled</template>
|
||||
<template v-if="ipLoggingForm.modified.value" #footer>
|
||||
<MkFormFooter :form="ipLoggingForm"/>
|
||||
</template>
|
||||
|
||||
<div class="_gaps_m">
|
||||
<SearchMarker>
|
||||
<MkSwitch v-model="ipLoggingForm.state.enableIpLogging">
|
||||
<template #label><SearchLabel>Enable</SearchLabel></template>
|
||||
</MkSwitch>
|
||||
</SearchMarker>
|
||||
</div>
|
||||
</MkFolder>
|
||||
</SearchMarker>
|
||||
</div>
|
||||
</SearchMarker>
|
||||
</div>
|
||||
</PageWithHeader>
|
||||
</template>
|
||||
|
||||
@@ -4,10 +4,14 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
-->
|
||||
|
||||
<template>
|
||||
<PageWithHeader :tabs="headerTabs">
|
||||
<div class="_spacer" style="--MI_SPACER-w: 700px; --MI_SPACER-min: 16px; --MI_SPACER-max: 32px;">
|
||||
<SearchMarker markerId="serverRules" :keywords="['rules']">
|
||||
<MkFolder>
|
||||
<template #icon><SearchIcon><i class="ti ti-checkbox"></i></SearchIcon></template>
|
||||
<template #label><SearchLabel>{{ i18n.ts.serverRules }}</SearchLabel></template>
|
||||
|
||||
<div class="_gaps_m">
|
||||
<div>{{ i18n.ts._serverRules.description }}</div>
|
||||
<div><SearchText>{{ i18n.ts._serverRules.description }}</SearchText></div>
|
||||
|
||||
<Sortable
|
||||
v-model="serverRules"
|
||||
class="_gaps_m"
|
||||
@@ -33,8 +37,8 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<MkButton primary rounded @click="save"><i class="ti ti-check"></i> {{ i18n.ts.save }}</MkButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</PageWithHeader>
|
||||
</MkFolder>
|
||||
</SearchMarker>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
@@ -42,9 +46,9 @@ import { defineAsyncComponent, ref, computed } from 'vue';
|
||||
import * as os from '@/os.js';
|
||||
import { fetchInstance, instance } from '@/instance.js';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import { definePage } from '@/page.js';
|
||||
import MkButton from '@/components/MkButton.vue';
|
||||
import MkInput from '@/components/MkInput.vue';
|
||||
import MkFolder from '@/components/MkFolder.vue';
|
||||
|
||||
const Sortable = defineAsyncComponent(() => import('vuedraggable').then(x => x.default));
|
||||
|
||||
@@ -60,13 +64,6 @@ const save = async () => {
|
||||
const remove = (index: number): void => {
|
||||
serverRules.value.splice(index, 1);
|
||||
};
|
||||
|
||||
const headerTabs = computed(() => []);
|
||||
|
||||
definePage(() => ({
|
||||
title: i18n.ts.serverRules,
|
||||
icon: 'ti ti-checkbox',
|
||||
}));
|
||||
</script>
|
||||
|
||||
<style lang="scss" module>
|
||||
|
||||
@@ -6,292 +6,369 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<template>
|
||||
<PageWithHeader :tabs="headerTabs">
|
||||
<div class="_spacer" style="--MI_SPACER-w: 700px; --MI_SPACER-min: 16px; --MI_SPACER-max: 32px;">
|
||||
<div class="_gaps_m">
|
||||
<MkFolder :defaultOpen="true">
|
||||
<template #icon><i class="ti ti-info-circle"></i></template>
|
||||
<template #label>{{ i18n.ts.info }}</template>
|
||||
<template v-if="infoForm.modified.value" #footer>
|
||||
<MkFormFooter :form="infoForm"/>
|
||||
</template>
|
||||
|
||||
<div class="_gaps">
|
||||
<MkInput v-model="infoForm.state.name">
|
||||
<template #label>{{ i18n.ts.instanceName }}<span v-if="infoForm.modifiedStates.name" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
</MkInput>
|
||||
|
||||
<MkInput v-model="infoForm.state.shortName">
|
||||
<template #label>{{ i18n.ts._serverSettings.shortName }} ({{ i18n.ts.optional }})<span v-if="infoForm.modifiedStates.shortName" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption>{{ i18n.ts._serverSettings.shortNameDescription }}</template>
|
||||
</MkInput>
|
||||
|
||||
<MkTextarea v-model="infoForm.state.description">
|
||||
<template #label>{{ i18n.ts.instanceDescription }}<span v-if="infoForm.modifiedStates.description" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
</MkTextarea>
|
||||
|
||||
<FormSplit :minWidth="300">
|
||||
<MkInput v-model="infoForm.state.maintainerName">
|
||||
<template #label>{{ i18n.ts.maintainerName }}<span v-if="infoForm.modifiedStates.maintainerName" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
</MkInput>
|
||||
|
||||
<MkInput v-model="infoForm.state.maintainerEmail" type="email">
|
||||
<template #label>{{ i18n.ts.maintainerEmail }}<span v-if="infoForm.modifiedStates.maintainerEmail" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #prefix><i class="ti ti-mail"></i></template>
|
||||
</MkInput>
|
||||
</FormSplit>
|
||||
|
||||
<MkInput v-model="infoForm.state.tosUrl" type="url">
|
||||
<template #label>{{ i18n.ts.tosUrl }}<span v-if="infoForm.modifiedStates.tosUrl" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #prefix><i class="ti ti-link"></i></template>
|
||||
</MkInput>
|
||||
|
||||
<MkInput v-model="infoForm.state.privacyPolicyUrl" type="url">
|
||||
<template #label>{{ i18n.ts.privacyPolicyUrl }}<span v-if="infoForm.modifiedStates.privacyPolicyUrl" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #prefix><i class="ti ti-link"></i></template>
|
||||
</MkInput>
|
||||
|
||||
<MkInput v-model="infoForm.state.inquiryUrl" type="url">
|
||||
<template #label>{{ i18n.ts._serverSettings.inquiryUrl }}<span v-if="infoForm.modifiedStates.inquiryUrl" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption>{{ i18n.ts._serverSettings.inquiryUrlDescription }}</template>
|
||||
<template #prefix><i class="ti ti-link"></i></template>
|
||||
</MkInput>
|
||||
|
||||
<MkInput v-model="infoForm.state.repositoryUrl" type="url">
|
||||
<template #label>{{ i18n.ts.repositoryUrl }}<span v-if="infoForm.modifiedStates.repositoryUrl" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption>{{ i18n.ts.repositoryUrlDescription }}</template>
|
||||
<template #prefix><i class="ti ti-link"></i></template>
|
||||
</MkInput>
|
||||
|
||||
<MkInfo v-if="!instance.providesTarball && !infoForm.state.repositoryUrl" warn>
|
||||
{{ i18n.ts.repositoryUrlOrTarballRequired }}
|
||||
</MkInfo>
|
||||
|
||||
<MkInput v-model="infoForm.state.impressumUrl" type="url">
|
||||
<template #label>{{ i18n.ts.impressumUrl }}<span v-if="infoForm.modifiedStates.impressumUrl" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption>{{ i18n.ts.impressumDescription }}</template>
|
||||
<template #prefix><i class="ti ti-link"></i></template>
|
||||
</MkInput>
|
||||
</div>
|
||||
</MkFolder>
|
||||
|
||||
<MkFolder>
|
||||
<template #icon><i class="ti ti-user-star"></i></template>
|
||||
<template #label>{{ i18n.ts.pinnedUsers }}</template>
|
||||
<template v-if="pinnedUsersForm.modified.value" #footer>
|
||||
<MkFormFooter :form="pinnedUsersForm"/>
|
||||
</template>
|
||||
|
||||
<MkTextarea v-model="pinnedUsersForm.state.pinnedUsers">
|
||||
<template #label>{{ i18n.ts.pinnedUsers }}<span v-if="pinnedUsersForm.modifiedStates.pinnedUsers" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption>{{ i18n.ts.pinnedUsersDescription }}</template>
|
||||
</MkTextarea>
|
||||
</MkFolder>
|
||||
|
||||
<MkFolder>
|
||||
<template #icon><i class="ti ti-world-cog"></i></template>
|
||||
<template #label>ServiceWorker</template>
|
||||
<template v-if="serviceWorkerForm.modified.value" #footer>
|
||||
<MkFormFooter :form="serviceWorkerForm"/>
|
||||
</template>
|
||||
|
||||
<div class="_gaps">
|
||||
<MkSwitch v-model="serviceWorkerForm.state.enableServiceWorker">
|
||||
<template #label>{{ i18n.ts.enableServiceworker }}<span v-if="serviceWorkerForm.modifiedStates.enableServiceWorker" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption>{{ i18n.ts.serviceworkerInfo }}</template>
|
||||
</MkSwitch>
|
||||
|
||||
<template v-if="serviceWorkerForm.state.enableServiceWorker">
|
||||
<MkInput v-model="serviceWorkerForm.state.swPublicKey">
|
||||
<template #label>Public key<span v-if="serviceWorkerForm.modifiedStates.swPublicKey" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #prefix><i class="ti ti-key"></i></template>
|
||||
</MkInput>
|
||||
|
||||
<MkInput v-model="serviceWorkerForm.state.swPrivateKey">
|
||||
<template #label>Private key<span v-if="serviceWorkerForm.modifiedStates.swPrivateKey" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #prefix><i class="ti ti-key"></i></template>
|
||||
</MkInput>
|
||||
</template>
|
||||
</div>
|
||||
</MkFolder>
|
||||
|
||||
<MkFolder>
|
||||
<template #icon><i class="ti ti-ad"></i></template>
|
||||
<template #label>{{ i18n.ts._ad.adsSettings }}</template>
|
||||
<template v-if="adForm.modified.value" #footer>
|
||||
<MkFormFooter :form="adForm"/>
|
||||
</template>
|
||||
|
||||
<div class="_gaps">
|
||||
<div class="_gaps_s">
|
||||
<MkInput v-model="adForm.state.notesPerOneAd" :min="0" type="number">
|
||||
<template #label>{{ i18n.ts._ad.notesPerOneAd }}<span v-if="adForm.modifiedStates.notesPerOneAd" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption>{{ i18n.ts._ad.setZeroToDisable }}</template>
|
||||
</MkInput>
|
||||
<MkInfo v-if="adForm.state.notesPerOneAd > 0 && adForm.state.notesPerOneAd < 20" :warn="true">
|
||||
{{ i18n.ts._ad.adsTooClose }}
|
||||
</MkInfo>
|
||||
</div>
|
||||
</div>
|
||||
</MkFolder>
|
||||
|
||||
<MkFolder>
|
||||
<template #icon><i class="ti ti-world-search"></i></template>
|
||||
<template #label>{{ i18n.ts._urlPreviewSetting.title }}</template>
|
||||
<template v-if="urlPreviewForm.modified.value" #footer>
|
||||
<MkFormFooter :form="urlPreviewForm"/>
|
||||
</template>
|
||||
|
||||
<div class="_gaps">
|
||||
<MkSwitch v-model="urlPreviewForm.state.urlPreviewEnabled">
|
||||
<template #label>{{ i18n.ts._urlPreviewSetting.enable }}<span v-if="urlPreviewForm.modifiedStates.urlPreviewEnabled" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
</MkSwitch>
|
||||
|
||||
<template v-if="urlPreviewForm.state.urlPreviewEnabled">
|
||||
<MkSwitch v-model="urlPreviewForm.state.urlPreviewAllowRedirect">
|
||||
<template #label>{{ i18n.ts._urlPreviewSetting.allowRedirect }}<span v-if="urlPreviewForm.modifiedStates.urlPreviewAllowRedirect" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption>{{ i18n.ts._urlPreviewSetting.allowRedirectDescription }}</template>
|
||||
</MkSwitch>
|
||||
|
||||
<MkSwitch v-model="urlPreviewForm.state.urlPreviewRequireContentLength">
|
||||
<template #label>{{ i18n.ts._urlPreviewSetting.requireContentLength }}<span v-if="urlPreviewForm.modifiedStates.urlPreviewRequireContentLength" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption>{{ i18n.ts._urlPreviewSetting.requireContentLengthDescription }}</template>
|
||||
</MkSwitch>
|
||||
|
||||
<MkInput v-model="urlPreviewForm.state.urlPreviewMaximumContentLength" type="number">
|
||||
<template #label>{{ i18n.ts._urlPreviewSetting.maximumContentLength }}<span v-if="urlPreviewForm.modifiedStates.urlPreviewMaximumContentLength" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption>{{ i18n.ts._urlPreviewSetting.maximumContentLengthDescription }}</template>
|
||||
</MkInput>
|
||||
|
||||
<MkInput v-model="urlPreviewForm.state.urlPreviewTimeout" type="number">
|
||||
<template #label>{{ i18n.ts._urlPreviewSetting.timeout }}<span v-if="urlPreviewForm.modifiedStates.urlPreviewTimeout" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption>{{ i18n.ts._urlPreviewSetting.timeoutDescription }}</template>
|
||||
</MkInput>
|
||||
|
||||
<MkInput v-model="urlPreviewForm.state.urlPreviewUserAgent" type="text">
|
||||
<template #label>{{ i18n.ts._urlPreviewSetting.userAgent }}<span v-if="urlPreviewForm.modifiedStates.urlPreviewUserAgent" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption>{{ i18n.ts._urlPreviewSetting.userAgentDescription }}</template>
|
||||
</MkInput>
|
||||
|
||||
<div>
|
||||
<MkInput v-model="urlPreviewForm.state.urlPreviewSummaryProxyUrl" type="text">
|
||||
<template #label>{{ i18n.ts._urlPreviewSetting.summaryProxy }}<span v-if="urlPreviewForm.modifiedStates.urlPreviewSummaryProxyUrl" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption>[{{ i18n.ts.notUsePleaseLeaveBlank }}] {{ i18n.ts._urlPreviewSetting.summaryProxyDescription }}</template>
|
||||
</MkInput>
|
||||
|
||||
<div :class="$style.subCaption">
|
||||
{{ i18n.ts._urlPreviewSetting.summaryProxyDescription2 }}
|
||||
<ul style="padding-left: 20px; margin: 4px 0">
|
||||
<li>{{ i18n.ts._urlPreviewSetting.timeout }} / key:timeout</li>
|
||||
<li>{{ i18n.ts._urlPreviewSetting.maximumContentLength }} / key:contentLengthLimit</li>
|
||||
<li>{{ i18n.ts._urlPreviewSetting.requireContentLength }} / key:contentLengthRequired</li>
|
||||
<li>{{ i18n.ts._urlPreviewSetting.userAgent }} / key:userAgent</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</MkFolder>
|
||||
|
||||
<MkFolder>
|
||||
<template #icon><i class="ti ti-planet"></i></template>
|
||||
<template #label>{{ i18n.ts.federation }}</template>
|
||||
<template v-if="federationForm.savedState.federation === 'all'" #suffix>{{ i18n.ts.all }}</template>
|
||||
<template v-else-if="federationForm.savedState.federation === 'specified'" #suffix>{{ i18n.ts.specifyHost }}</template>
|
||||
<template v-else-if="federationForm.savedState.federation === 'none'" #suffix>{{ i18n.ts.none }}</template>
|
||||
<template v-if="federationForm.modified.value" #footer>
|
||||
<MkFormFooter :form="federationForm"/>
|
||||
</template>
|
||||
|
||||
<div class="_gaps">
|
||||
<MkRadios v-model="federationForm.state.federation">
|
||||
<template #label>{{ i18n.ts.behavior }}<span v-if="federationForm.modifiedStates.federation" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<option value="all">{{ i18n.ts.all }}</option>
|
||||
<option value="specified">{{ i18n.ts.specifyHost }}</option>
|
||||
<option value="none">{{ i18n.ts.none }}</option>
|
||||
</MkRadios>
|
||||
|
||||
<MkTextarea v-if="federationForm.state.federation === 'specified'" v-model="federationForm.state.federationHosts">
|
||||
<template #label>{{ i18n.ts.federationAllowedHosts }}<span v-if="federationForm.modifiedStates.federationHosts" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption>{{ i18n.ts.federationAllowedHostsDescription }}</template>
|
||||
</MkTextarea>
|
||||
|
||||
<MkFolder>
|
||||
<template #icon><i class="ti ti-list"></i></template>
|
||||
<template #label><SearchLabel>{{ i18n.ts._serverSettings.deliverSuspendedSoftware }}</SearchLabel></template>
|
||||
<template #footer>
|
||||
<div class="_buttons">
|
||||
<MkButton @click="federationForm.state.deliverSuspendedSoftware.push({software: '', versionRange: ''})"><i class="ti ti-plus"></i> {{ i18n.ts.add }}</MkButton>
|
||||
</div>
|
||||
<SearchMarker path="/admin/settings" :label="i18n.ts.general" :keywords="['general', 'settings']" icon="ti ti-settings">
|
||||
<div class="_gaps_m">
|
||||
<SearchMarker v-slot="slotProps" :keywords="['information', 'meta']">
|
||||
<MkFolder :defaultOpen="true">
|
||||
<template #icon><SearchIcon><i class="ti ti-info-circle"></i></SearchIcon></template>
|
||||
<template #label><SearchLabel>{{ i18n.ts.info }}</SearchLabel></template>
|
||||
<template v-if="infoForm.modified.value" #footer>
|
||||
<MkFormFooter :form="infoForm"/>
|
||||
</template>
|
||||
|
||||
<div :class="$style.metadataRoot" class="_gaps_s">
|
||||
<MkInfo>{{ i18n.ts._serverSettings.deliverSuspendedSoftwareDescription }}</MkInfo>
|
||||
<div v-for="(element, index) in federationForm.state.deliverSuspendedSoftware" :key="index" v-panel :class="$style.fieldDragItem">
|
||||
<button class="_button" :class="$style.dragItemRemove" @click="federationForm.state.deliverSuspendedSoftware.splice(index, 1)"><i class="ti ti-x"></i></button>
|
||||
<div :class="$style.dragItemForm">
|
||||
<FormSplit :minWidth="200">
|
||||
<MkInput v-model="element.software" small :placeholder="i18n.ts.softwareName">
|
||||
</MkInput>
|
||||
<MkInput v-model="element.versionRange" small :placeholder="i18n.ts.version">
|
||||
</MkInput>
|
||||
</FormSplit>
|
||||
</div>
|
||||
<div class="_gaps">
|
||||
<SearchMarker :keywords="['name']">
|
||||
<MkInput v-model="infoForm.state.name">
|
||||
<template #label><SearchLabel>{{ i18n.ts.instanceName }}</SearchLabel><span v-if="infoForm.modifiedStates.name" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
|
||||
<SearchMarker :keywords="['shortName']">
|
||||
<MkInput v-model="infoForm.state.shortName">
|
||||
<template #label><SearchLabel>{{ i18n.ts._serverSettings.shortName }}</SearchLabel> ({{ i18n.ts.optional }})<span v-if="infoForm.modifiedStates.shortName" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption><SearchText>{{ i18n.ts._serverSettings.shortNameDescription }}</SearchText></template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
|
||||
<SearchMarker :keywords="['description']">
|
||||
<MkTextarea v-model="infoForm.state.description">
|
||||
<template #label><SearchLabel>{{ i18n.ts.instanceDescription }}</SearchLabel><span v-if="infoForm.modifiedStates.description" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
</MkTextarea>
|
||||
</SearchMarker>
|
||||
|
||||
<FormSplit :minWidth="300">
|
||||
<SearchMarker :keywords="['maintainer', 'name']">
|
||||
<MkInput v-model="infoForm.state.maintainerName">
|
||||
<template #label><SearchLabel>{{ i18n.ts.maintainerName }}</SearchLabel><span v-if="infoForm.modifiedStates.maintainerName" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
|
||||
<SearchMarker :keywords="['maintainer', 'email', 'contact']">
|
||||
<MkInput v-model="infoForm.state.maintainerEmail" type="email">
|
||||
<template #label><SearchLabel>{{ i18n.ts.maintainerEmail }}</SearchLabel><span v-if="infoForm.modifiedStates.maintainerEmail" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #prefix><i class="ti ti-mail"></i></template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
</FormSplit>
|
||||
|
||||
<SearchMarker :keywords="['tos', 'termsOfService']">
|
||||
<MkInput v-model="infoForm.state.tosUrl" type="url">
|
||||
<template #label><SearchLabel>{{ i18n.ts.tosUrl }}</SearchLabel><span v-if="infoForm.modifiedStates.tosUrl" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #prefix><i class="ti ti-link"></i></template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
|
||||
<SearchMarker :keywords="['privacyPolicy']">
|
||||
<MkInput v-model="infoForm.state.privacyPolicyUrl" type="url">
|
||||
<template #label><SearchLabel>{{ i18n.ts.privacyPolicyUrl }}</SearchLabel><span v-if="infoForm.modifiedStates.privacyPolicyUrl" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #prefix><i class="ti ti-link"></i></template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
|
||||
<SearchMarker :keywords="['inquiry', 'contact']">
|
||||
<MkInput v-model="infoForm.state.inquiryUrl" type="url">
|
||||
<template #label><SearchLabel>{{ i18n.ts._serverSettings.inquiryUrl }}</SearchLabel><span v-if="infoForm.modifiedStates.inquiryUrl" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption><SearchText>{{ i18n.ts._serverSettings.inquiryUrlDescription }}</SearchText></template>
|
||||
<template #prefix><i class="ti ti-link"></i></template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
|
||||
<SearchMarker :keywords="['repository', 'url']">
|
||||
<MkInput v-model="infoForm.state.repositoryUrl" type="url">
|
||||
<template #label><SearchLabel>{{ i18n.ts.repositoryUrl }}</SearchLabel><span v-if="infoForm.modifiedStates.repositoryUrl" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption><SearchText>{{ i18n.ts.repositoryUrlDescription }}</SearchText></template>
|
||||
<template #prefix><i class="ti ti-link"></i></template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
|
||||
<MkInfo v-if="!instance.providesTarball && !infoForm.state.repositoryUrl" warn>
|
||||
{{ i18n.ts.repositoryUrlOrTarballRequired }}
|
||||
</MkInfo>
|
||||
|
||||
<SearchMarker :keywords="['impressum', 'legalNotice']">
|
||||
<MkInput v-model="infoForm.state.impressumUrl" type="url">
|
||||
<template #label><SearchLabel>{{ i18n.ts.impressumUrl }}</SearchLabel><span v-if="infoForm.modifiedStates.impressumUrl" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption><SearchText>{{ i18n.ts.impressumDescription }}</SearchText></template>
|
||||
<template #prefix><i class="ti ti-link"></i></template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
</div>
|
||||
</MkFolder>
|
||||
</SearchMarker>
|
||||
|
||||
<SearchMarker v-slot="slotProps" :keywords="['pinned', 'users']">
|
||||
<MkFolder :defaultOpen="slotProps.isParentOfTarget">
|
||||
<template #icon><SearchIcon><i class="ti ti-user-star"></i></SearchIcon></template>
|
||||
<template #label><SearchLabel>{{ i18n.ts.pinnedUsers }}</SearchLabel></template>
|
||||
<template v-if="pinnedUsersForm.modified.value" #footer>
|
||||
<MkFormFooter :form="pinnedUsersForm"/>
|
||||
</template>
|
||||
|
||||
<MkTextarea v-model="pinnedUsersForm.state.pinnedUsers">
|
||||
<template #label>{{ i18n.ts.pinnedUsers }}<span v-if="pinnedUsersForm.modifiedStates.pinnedUsers" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption><SearchText>{{ i18n.ts.pinnedUsersDescription }}</SearchText></template>
|
||||
</MkTextarea>
|
||||
</MkFolder>
|
||||
</SearchMarker>
|
||||
|
||||
<SearchMarker v-slot="slotProps" :keywords="['serviceWorker']">
|
||||
<MkFolder :defaultOpen="slotProps.isParentOfTarget">
|
||||
<template #icon><SearchIcon><i class="ti ti-world-cog"></i></SearchIcon></template>
|
||||
<template #label><SearchLabel>ServiceWorker</SearchLabel></template>
|
||||
<template v-if="serviceWorkerForm.modified.value" #footer>
|
||||
<MkFormFooter :form="serviceWorkerForm"/>
|
||||
</template>
|
||||
|
||||
<div class="_gaps">
|
||||
<SearchMarker>
|
||||
<MkSwitch v-model="serviceWorkerForm.state.enableServiceWorker">
|
||||
<template #label><SearchLabel>{{ i18n.ts.enableServiceworker }}</SearchLabel><span v-if="serviceWorkerForm.modifiedStates.enableServiceWorker" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption><SearchText>{{ i18n.ts.serviceworkerInfo }}</SearchText></template>
|
||||
</MkSwitch>
|
||||
</SearchMarker>
|
||||
|
||||
<template v-if="serviceWorkerForm.state.enableServiceWorker">
|
||||
<SearchMarker>
|
||||
<MkInput v-model="serviceWorkerForm.state.swPublicKey">
|
||||
<template #label><SearchLabel>Public key</SearchLabel><span v-if="serviceWorkerForm.modifiedStates.swPublicKey" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #prefix><i class="ti ti-key"></i></template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
|
||||
<SearchMarker>
|
||||
<MkInput v-model="serviceWorkerForm.state.swPrivateKey">
|
||||
<template #label><SearchLabel>Private key</SearchLabel><span v-if="serviceWorkerForm.modifiedStates.swPrivateKey" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #prefix><i class="ti ti-key"></i></template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
</template>
|
||||
</div>
|
||||
</MkFolder>
|
||||
</SearchMarker>
|
||||
|
||||
<SearchMarker v-slot="slotProps" :keywords="['ads']">
|
||||
<MkFolder :defaultOpen="slotProps.isParentOfTarget">
|
||||
<template #icon><SearchIcon><i class="ti ti-ad"></i></SearchIcon></template>
|
||||
<template #label><SearchLabel>{{ i18n.ts._ad.adsSettings }}</SearchLabel></template>
|
||||
<template v-if="adForm.modified.value" #footer>
|
||||
<MkFormFooter :form="adForm"/>
|
||||
</template>
|
||||
|
||||
<div class="_gaps">
|
||||
<div class="_gaps_s">
|
||||
<SearchMarker>
|
||||
<MkInput v-model="adForm.state.notesPerOneAd" :min="0" type="number">
|
||||
<template #label><SearchLabel>{{ i18n.ts._ad.notesPerOneAd }}</SearchLabel><span v-if="adForm.modifiedStates.notesPerOneAd" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption>{{ i18n.ts._ad.setZeroToDisable }}</template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
|
||||
<MkInfo v-if="adForm.state.notesPerOneAd > 0 && adForm.state.notesPerOneAd < 20" :warn="true">
|
||||
{{ i18n.ts._ad.adsTooClose }}
|
||||
</MkInfo>
|
||||
</div>
|
||||
</div>
|
||||
</MkFolder>
|
||||
</SearchMarker>
|
||||
|
||||
<MkSwitch v-model="federationForm.state.signToActivityPubGet">
|
||||
<template #label>{{ i18n.ts._serverSettings.signToActivityPubGet }}<span v-if="federationForm.modifiedStates.signToActivityPubGet" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption>{{ i18n.ts._serverSettings.signToActivityPubGet_description }}</template>
|
||||
</MkSwitch>
|
||||
|
||||
<MkSwitch v-model="federationForm.state.proxyRemoteFiles">
|
||||
<template #label>{{ i18n.ts._serverSettings.proxyRemoteFiles }}<span v-if="federationForm.modifiedStates.proxyRemoteFiles" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption>{{ i18n.ts._serverSettings.proxyRemoteFiles_description }}</template>
|
||||
</MkSwitch>
|
||||
|
||||
<MkSwitch v-model="federationForm.state.allowExternalApRedirect">
|
||||
<template #label>{{ i18n.ts._serverSettings.allowExternalApRedirect }}<span v-if="federationForm.modifiedStates.allowExternalApRedirect" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption>
|
||||
<div>{{ i18n.ts._serverSettings.allowExternalApRedirect_description }}</div>
|
||||
<div>{{ i18n.ts.needToRestartServerToApply }}</div>
|
||||
<SearchMarker v-slot="slotProps" :keywords="['url', 'preview']">
|
||||
<MkFolder :defaultOpen="slotProps.isParentOfTarget">
|
||||
<template #icon><SearchIcon><i class="ti ti-world-search"></i></SearchIcon></template>
|
||||
<template #label><SearchLabel>{{ i18n.ts._urlPreviewSetting.title }}</SearchLabel></template>
|
||||
<template v-if="urlPreviewForm.modified.value" #footer>
|
||||
<MkFormFooter :form="urlPreviewForm"/>
|
||||
</template>
|
||||
</MkSwitch>
|
||||
|
||||
<MkSwitch v-model="federationForm.state.cacheRemoteFiles">
|
||||
<template #label>{{ i18n.ts.cacheRemoteFiles }}<span v-if="federationForm.modifiedStates.cacheRemoteFiles" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption>{{ i18n.ts.cacheRemoteFilesDescription }}{{ i18n.ts.youCanCleanRemoteFilesCache }}</template>
|
||||
</MkSwitch>
|
||||
<div class="_gaps">
|
||||
<SearchMarker>
|
||||
<MkSwitch v-model="urlPreviewForm.state.urlPreviewEnabled">
|
||||
<template #label><SearchLabel>{{ i18n.ts._urlPreviewSetting.enable }}</SearchLabel><span v-if="urlPreviewForm.modifiedStates.urlPreviewEnabled" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
</MkSwitch>
|
||||
</SearchMarker>
|
||||
|
||||
<template v-if="federationForm.state.cacheRemoteFiles">
|
||||
<MkSwitch v-model="federationForm.state.cacheRemoteSensitiveFiles">
|
||||
<template #label>{{ i18n.ts.cacheRemoteSensitiveFiles }}<span v-if="federationForm.modifiedStates.cacheRemoteSensitiveFiles" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption>{{ i18n.ts.cacheRemoteSensitiveFilesDescription }}</template>
|
||||
</MkSwitch>
|
||||
</template>
|
||||
</div>
|
||||
</MkFolder>
|
||||
<template v-if="urlPreviewForm.state.urlPreviewEnabled">
|
||||
<SearchMarker :keywords="['allow', 'redirect']">
|
||||
<MkSwitch v-model="urlPreviewForm.state.urlPreviewAllowRedirect">
|
||||
<template #label><SearchLabel>{{ i18n.ts._urlPreviewSetting.allowRedirect }}</SearchLabel><span v-if="urlPreviewForm.modifiedStates.urlPreviewAllowRedirect" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption>{{ i18n.ts._urlPreviewSetting.allowRedirectDescription }}</template>
|
||||
</MkSwitch>
|
||||
</SearchMarker>
|
||||
|
||||
<MkFolder>
|
||||
<template #icon><i class="ti ti-ghost"></i></template>
|
||||
<template #label>{{ i18n.ts.proxyAccount }}</template>
|
||||
<template v-if="proxyAccountForm.modified.value" #footer>
|
||||
<MkFormFooter :form="proxyAccountForm"/>
|
||||
</template>
|
||||
<SearchMarker :keywords="['contentLength']">
|
||||
<MkSwitch v-model="urlPreviewForm.state.urlPreviewRequireContentLength">
|
||||
<template #label><SearchLabel>{{ i18n.ts._urlPreviewSetting.requireContentLength }}</SearchLabel><span v-if="urlPreviewForm.modifiedStates.urlPreviewRequireContentLength" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption>{{ i18n.ts._urlPreviewSetting.requireContentLengthDescription }}</template>
|
||||
</MkSwitch>
|
||||
</SearchMarker>
|
||||
|
||||
<div class="_gaps">
|
||||
<MkInfo>{{ i18n.ts.proxyAccountDescription }}</MkInfo>
|
||||
<SearchMarker :keywords="['contentLength']">
|
||||
<MkInput v-model="urlPreviewForm.state.urlPreviewMaximumContentLength" type="number">
|
||||
<template #label><SearchLabel>{{ i18n.ts._urlPreviewSetting.maximumContentLength }}</SearchLabel><span v-if="urlPreviewForm.modifiedStates.urlPreviewMaximumContentLength" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption>{{ i18n.ts._urlPreviewSetting.maximumContentLengthDescription }}</template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
|
||||
<MkTextarea v-model="proxyAccountForm.state.description" :max="500" tall mfmAutocomplete :mfmPreview="true">
|
||||
<template #label>{{ i18n.ts._profile.description }}</template>
|
||||
<template #caption>{{ i18n.ts._profile.youCanIncludeHashtags }}</template>
|
||||
</MkTextarea>
|
||||
</div>
|
||||
</MkFolder>
|
||||
<SearchMarker :keywords="['timeout']">
|
||||
<MkInput v-model="urlPreviewForm.state.urlPreviewTimeout" type="number">
|
||||
<template #label><SearchLabel>{{ i18n.ts._urlPreviewSetting.timeout }}</SearchLabel><span v-if="urlPreviewForm.modifiedStates.urlPreviewTimeout" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption>{{ i18n.ts._urlPreviewSetting.timeoutDescription }}</template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
|
||||
<MkButton primary @click="openSetupWizard">
|
||||
Open setup wizard
|
||||
</MkButton>
|
||||
</div>
|
||||
<SearchMarker :keywords="['userAgent']">
|
||||
<MkInput v-model="urlPreviewForm.state.urlPreviewUserAgent" type="text">
|
||||
<template #label><SearchLabel>{{ i18n.ts._urlPreviewSetting.userAgent }}</SearchLabel><span v-if="urlPreviewForm.modifiedStates.urlPreviewUserAgent" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption>{{ i18n.ts._urlPreviewSetting.userAgentDescription }}</template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
|
||||
<div>
|
||||
<SearchMarker :keywords="['proxy']">
|
||||
<MkInput v-model="urlPreviewForm.state.urlPreviewSummaryProxyUrl" type="text">
|
||||
<template #label><SearchLabel>{{ i18n.ts._urlPreviewSetting.summaryProxy }}</SearchLabel><span v-if="urlPreviewForm.modifiedStates.urlPreviewSummaryProxyUrl" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption>[{{ i18n.ts.notUsePleaseLeaveBlank }}] {{ i18n.ts._urlPreviewSetting.summaryProxyDescription }}</template>
|
||||
</MkInput>
|
||||
</SearchMarker>
|
||||
|
||||
<div :class="$style.subCaption">
|
||||
{{ i18n.ts._urlPreviewSetting.summaryProxyDescription2 }}
|
||||
<ul style="padding-left: 20px; margin: 4px 0">
|
||||
<li>{{ i18n.ts._urlPreviewSetting.timeout }} / key:timeout</li>
|
||||
<li>{{ i18n.ts._urlPreviewSetting.maximumContentLength }} / key:contentLengthLimit</li>
|
||||
<li>{{ i18n.ts._urlPreviewSetting.requireContentLength }} / key:contentLengthRequired</li>
|
||||
<li>{{ i18n.ts._urlPreviewSetting.userAgent }} / key:userAgent</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</MkFolder>
|
||||
</SearchMarker>
|
||||
|
||||
<SearchMarker v-slot="slotProps" :keywords="['federation']">
|
||||
<MkFolder :defaultOpen="slotProps.isParentOfTarget">
|
||||
<template #icon><SearchIcon><i class="ti ti-planet"></i></SearchIcon></template>
|
||||
<template #label><SearchLabel>{{ i18n.ts.federation }}</SearchLabel></template>
|
||||
<template v-if="federationForm.savedState.federation === 'all'" #suffix>{{ i18n.ts.all }}</template>
|
||||
<template v-else-if="federationForm.savedState.federation === 'specified'" #suffix>{{ i18n.ts.specifyHost }}</template>
|
||||
<template v-else-if="federationForm.savedState.federation === 'none'" #suffix>{{ i18n.ts.none }}</template>
|
||||
<template v-if="federationForm.modified.value" #footer>
|
||||
<MkFormFooter :form="federationForm"/>
|
||||
</template>
|
||||
|
||||
<div class="_gaps">
|
||||
<SearchMarker>
|
||||
<MkRadios v-model="federationForm.state.federation">
|
||||
<template #label><SearchLabel>{{ i18n.ts.behavior }}</SearchLabel><span v-if="federationForm.modifiedStates.federation" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<option value="all">{{ i18n.ts.all }}</option>
|
||||
<option value="specified">{{ i18n.ts.specifyHost }}</option>
|
||||
<option value="none">{{ i18n.ts.none }}</option>
|
||||
</MkRadios>
|
||||
</SearchMarker>
|
||||
|
||||
<SearchMarker :keywords="['hosts']">
|
||||
<MkTextarea v-if="federationForm.state.federation === 'specified'" v-model="federationForm.state.federationHosts">
|
||||
<template #label><SearchLabel>{{ i18n.ts.federationAllowedHosts }}</SearchLabel><span v-if="federationForm.modifiedStates.federationHosts" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption>{{ i18n.ts.federationAllowedHostsDescription }}</template>
|
||||
</MkTextarea>
|
||||
</SearchMarker>
|
||||
|
||||
<SearchMarker :keywords="['suspended', 'software']">
|
||||
<MkFolder>
|
||||
<template #icon><i class="ti ti-list"></i></template>
|
||||
<template #label><SearchLabel>{{ i18n.ts._serverSettings.deliverSuspendedSoftware }}</SearchLabel></template>
|
||||
<template #footer>
|
||||
<div class="_buttons">
|
||||
<MkButton @click="federationForm.state.deliverSuspendedSoftware.push({software: '', versionRange: ''})"><i class="ti ti-plus"></i> {{ i18n.ts.add }}</MkButton>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div :class="$style.metadataRoot" class="_gaps_s">
|
||||
<MkInfo>{{ i18n.ts._serverSettings.deliverSuspendedSoftwareDescription }}</MkInfo>
|
||||
<div v-for="(element, index) in federationForm.state.deliverSuspendedSoftware" :key="index" v-panel :class="$style.fieldDragItem">
|
||||
<button class="_button" :class="$style.dragItemRemove" @click="federationForm.state.deliverSuspendedSoftware.splice(index, 1)"><i class="ti ti-x"></i></button>
|
||||
<div :class="$style.dragItemForm">
|
||||
<FormSplit :minWidth="200">
|
||||
<MkInput v-model="element.software" small :placeholder="i18n.ts.softwareName">
|
||||
</MkInput>
|
||||
<MkInput v-model="element.versionRange" small :placeholder="i18n.ts.version">
|
||||
</MkInput>
|
||||
</FormSplit>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</MkFolder>
|
||||
</SearchMarker>
|
||||
|
||||
<SearchMarker :keywords="['sign', 'get']">
|
||||
<MkSwitch v-model="federationForm.state.signToActivityPubGet">
|
||||
<template #label><SearchLabel>{{ i18n.ts._serverSettings.signToActivityPubGet }}</SearchLabel><span v-if="federationForm.modifiedStates.signToActivityPubGet" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption><SearchText>{{ i18n.ts._serverSettings.signToActivityPubGet_description }}</SearchText></template>
|
||||
</MkSwitch>
|
||||
</SearchMarker>
|
||||
|
||||
<SearchMarker :keywords="['proxy', 'remote', 'files']">
|
||||
<MkSwitch v-model="federationForm.state.proxyRemoteFiles">
|
||||
<template #label><SearchLabel>{{ i18n.ts._serverSettings.proxyRemoteFiles }}</SearchLabel><span v-if="federationForm.modifiedStates.proxyRemoteFiles" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption><SearchText>{{ i18n.ts._serverSettings.proxyRemoteFiles_description }}</SearchText></template>
|
||||
</MkSwitch>
|
||||
</SearchMarker>
|
||||
|
||||
<SearchMarker :keywords="['allow', 'external', 'redirect']">
|
||||
<MkSwitch v-model="federationForm.state.allowExternalApRedirect">
|
||||
<template #label><SearchLabel>{{ i18n.ts._serverSettings.allowExternalApRedirect }}</SearchLabel><span v-if="federationForm.modifiedStates.allowExternalApRedirect" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption>
|
||||
<div><SearchText>{{ i18n.ts._serverSettings.allowExternalApRedirect_description }}</SearchText></div>
|
||||
<div>{{ i18n.ts.needToRestartServerToApply }}</div>
|
||||
</template>
|
||||
</MkSwitch>
|
||||
</SearchMarker>
|
||||
|
||||
<SearchMarker :keywords="['cache', 'remote', 'files']">
|
||||
<MkSwitch v-model="federationForm.state.cacheRemoteFiles">
|
||||
<template #label><SearchLabel>{{ i18n.ts.cacheRemoteFiles }}</SearchLabel><span v-if="federationForm.modifiedStates.cacheRemoteFiles" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption><SearchText>{{ i18n.ts.cacheRemoteFilesDescription }}</SearchText>{{ i18n.ts.youCanCleanRemoteFilesCache }}</template>
|
||||
</MkSwitch>
|
||||
</SearchMarker>
|
||||
|
||||
<template v-if="federationForm.state.cacheRemoteFiles">
|
||||
<SearchMarker :keywords="['cache', 'remote', 'sensitive', 'files']">
|
||||
<MkSwitch v-model="federationForm.state.cacheRemoteSensitiveFiles">
|
||||
<template #label><SearchLabel>{{ i18n.ts.cacheRemoteSensitiveFiles }}</SearchLabel><span v-if="federationForm.modifiedStates.cacheRemoteSensitiveFiles" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption><SearchText>{{ i18n.ts.cacheRemoteSensitiveFilesDescription }}</SearchText></template>
|
||||
</MkSwitch>
|
||||
</SearchMarker>
|
||||
</template>
|
||||
</div>
|
||||
</MkFolder>
|
||||
</SearchMarker>
|
||||
|
||||
<SearchMarker v-slot="slotProps" :keywords="['proxy', 'account']">
|
||||
<MkFolder :defaultOpen="slotProps.isParentOfTarget">
|
||||
<template #icon><SearchIcon><i class="ti ti-ghost"></i></SearchIcon></template>
|
||||
<template #label><SearchLabel>{{ i18n.ts.proxyAccount }}</SearchLabel></template>
|
||||
<template v-if="proxyAccountForm.modified.value" #footer>
|
||||
<MkFormFooter :form="proxyAccountForm"/>
|
||||
</template>
|
||||
|
||||
<div class="_gaps">
|
||||
<MkInfo>{{ i18n.ts.proxyAccountDescription }}</MkInfo>
|
||||
|
||||
<SearchMarker :keywords="['description']">
|
||||
<MkTextarea v-model="proxyAccountForm.state.description" :max="500" tall mfmAutocomplete :mfmPreview="true">
|
||||
<template #label><SearchLabel>{{ i18n.ts._profile.description }}</SearchLabel></template>
|
||||
<template #caption>{{ i18n.ts._profile.youCanIncludeHashtags }}</template>
|
||||
</MkTextarea>
|
||||
</SearchMarker>
|
||||
</div>
|
||||
</MkFolder>
|
||||
</SearchMarker>
|
||||
|
||||
<MkButton primary @click="openSetupWizard">
|
||||
Open setup wizard
|
||||
</MkButton>
|
||||
</div>
|
||||
</SearchMarker>
|
||||
</div>
|
||||
</PageWithHeader>
|
||||
</template>
|
||||
|
||||
@@ -6,17 +6,21 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<template>
|
||||
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
|
||||
<div class="_spacer" style="--MI_SPACER-w: 900px;">
|
||||
<div class="_gaps_m">
|
||||
<MkButton primary @click="onCreateWebhookClicked">
|
||||
<i class="ti ti-plus"></i> {{ i18n.ts._webhookSettings.createWebhook }}
|
||||
</MkButton>
|
||||
<SearchMarker path="/admin/system-webhook" label="SystemWebhook" :keywords="['webhook']" icon="ti ti-webhook">
|
||||
<div class="_gaps_m">
|
||||
<SearchMarker>
|
||||
<MkButton primary @click="onCreateWebhookClicked">
|
||||
<i class="ti ti-plus"></i> <SearchLabel>{{ i18n.ts._webhookSettings.createWebhook }}</SearchLabel>
|
||||
</MkButton>
|
||||
</SearchMarker>
|
||||
|
||||
<FormSection>
|
||||
<div class="_gaps">
|
||||
<XItem v-for="item in webhooks" :key="item.id" :entity="item" @edit="onEditButtonClicked" @delete="onDeleteButtonClicked"/>
|
||||
</div>
|
||||
</FormSection>
|
||||
</div>
|
||||
<FormSection>
|
||||
<div class="_gaps">
|
||||
<XItem v-for="item in webhooks" :key="item.id" :entity="item" @edit="onEditButtonClicked" @delete="onDeleteButtonClicked"/>
|
||||
</div>
|
||||
</FormSection>
|
||||
</div>
|
||||
</SearchMarker>
|
||||
</div>
|
||||
</PageWithHeader>
|
||||
</template>
|
||||
|
||||
@@ -366,6 +366,7 @@ definePage(() => ({
|
||||
|
||||
> .items {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
padding: 16px;
|
||||
|
||||
@@ -20,7 +20,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<MkFolder :defaultOpen="true">
|
||||
<template #icon><i class="ti ti-shield-lock"></i></template>
|
||||
<template #label><SearchLabel>{{ i18n.ts.totp }}</SearchLabel></template>
|
||||
<template #caption><SearchKeyword>{{ i18n.ts.totpDescription }}</SearchKeyword></template>
|
||||
<template #caption><SearchText>{{ i18n.ts.totpDescription }}</SearchText></template>
|
||||
<template #suffix><i v-if="$i.twoFactorEnabled" class="ti ti-check" style="color: var(--MI_THEME-success)"></i></template>
|
||||
|
||||
<div v-if="$i.twoFactorEnabled" class="_gaps_s">
|
||||
@@ -74,7 +74,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<SearchMarker :keywords="['password', 'less', 'key', 'passkey', 'login', 'signin']">
|
||||
<MkSwitch :disabled="!$i.twoFactorEnabled || $i.securityKeysList.length === 0" :modelValue="usePasswordLessLogin" @update:modelValue="v => updatePasswordLessLogin(v)">
|
||||
<template #label><SearchLabel>{{ i18n.ts.passwordLessLogin }}</SearchLabel></template>
|
||||
<template #caption><SearchKeyword>{{ i18n.ts.passwordLessLoginDescription }}</SearchKeyword></template>
|
||||
<template #caption><SearchText>{{ i18n.ts.passwordLessLoginDescription }}</SearchText></template>
|
||||
</MkSwitch>
|
||||
</SearchMarker>
|
||||
</div>
|
||||
|
||||
@@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<SearchMarker path="/settings/account-data" :label="i18n.ts._settings.accountData" :keywords="['import', 'export', 'data', 'archive']" icon="ti ti-package">
|
||||
<div class="_gaps_m">
|
||||
<MkFeatureBanner icon="/client-assets/package_3d.png" color="#ff9100">
|
||||
<SearchKeyword>{{ i18n.ts._settings.accountDataBanner }}</SearchKeyword>
|
||||
<SearchText>{{ i18n.ts._settings.accountDataBanner }}</SearchText>
|
||||
</MkFeatureBanner>
|
||||
|
||||
<div class="_gaps_s">
|
||||
|
||||
@@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<SearchMarker path="/settings/connect" :label="i18n.ts._settings.serviceConnection" :keywords="['app', 'service', 'connect', 'webhook', 'api', 'token']" icon="ti ti-link">
|
||||
<div class="_gaps_m">
|
||||
<MkFeatureBanner icon="/client-assets/link_3d.png" color="#ff0088">
|
||||
<SearchKeyword>{{ i18n.ts._settings.serviceConnectionBanner }}</SearchKeyword>
|
||||
<SearchText>{{ i18n.ts._settings.serviceConnectionBanner }}</SearchText>
|
||||
</MkFeatureBanner>
|
||||
|
||||
<SearchMarker :keywords="['api', 'app', 'token', 'accessToken']">
|
||||
|
||||
@@ -7,6 +7,8 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<div class="_gaps_m">
|
||||
<FormInfo warn>{{ i18n.ts.customCssWarn }}</FormInfo>
|
||||
|
||||
<FormInfo v-if="isSafeMode" warn>{{ i18n.ts.customCssIsDisabledBecauseSafeMode }}</FormInfo>
|
||||
|
||||
<MkCodeEditor v-model="localCustomCss" manualSave lang="css">
|
||||
<template #label>CSS</template>
|
||||
</MkCodeEditor>
|
||||
@@ -17,6 +19,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
import { ref, watch, computed } from 'vue';
|
||||
import MkCodeEditor from '@/components/MkCodeEditor.vue';
|
||||
import FormInfo from '@/components/MkInfo.vue';
|
||||
import { isSafeMode } from '@@/js/config.js';
|
||||
import * as os from '@/os.js';
|
||||
import { unisonReload } from '@/utility/unison-reload.js';
|
||||
import { i18n } from '@/i18n.js';
|
||||
|
||||
@@ -97,8 +97,8 @@ import { i18n } from '@/i18n.js';
|
||||
import { definePage } from '@/page.js';
|
||||
import { prefer } from '@/preferences.js';
|
||||
import MkPreferenceContainer from '@/components/MkPreferenceContainer.vue';
|
||||
import { reloadAsk } from '@/utility/reload-ask.js';
|
||||
import { selectFile } from '@/utility/drive.js';
|
||||
import { suggestReload } from '@/utility/reload-suggest.js';
|
||||
|
||||
const navWindow = prefer.model('deck.navWindow');
|
||||
const useSimpleUiForNonRootPages = prefer.model('deck.useSimpleUiForNonRootPages');
|
||||
@@ -109,8 +109,8 @@ const menuPosition = prefer.model('deck.menuPosition');
|
||||
const navbarPosition = prefer.model('deck.navbarPosition');
|
||||
const wallpaper = prefer.model('deck.wallpaper');
|
||||
|
||||
watch(wallpaper, async () => {
|
||||
await reloadAsk({ reason: i18n.ts.reloadToApplySetting, unison: true });
|
||||
watch(wallpaper, () => {
|
||||
suggestReload();
|
||||
});
|
||||
|
||||
function setWallpaper(ev: MouseEvent) {
|
||||
|
||||
@@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<SearchMarker path="/settings/drive" :label="i18n.ts.drive" :keywords="['drive']" icon="ti ti-cloud">
|
||||
<div class="_gaps_m">
|
||||
<MkFeatureBanner icon="/client-assets/cloud_3d.png" color="#0059ff">
|
||||
<SearchKeyword>{{ i18n.ts._settings.driveBanner }}</SearchKeyword>
|
||||
<SearchText>{{ i18n.ts._settings.driveBanner }}</SearchText>
|
||||
</MkFeatureBanner>
|
||||
|
||||
<SearchMarker :keywords="['capacity', 'usage']">
|
||||
@@ -60,7 +60,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<MkPreferenceContainer k="keepOriginalFilename">
|
||||
<MkSwitch v-model="keepOriginalFilename">
|
||||
<template #label><SearchLabel>{{ i18n.ts.keepOriginalFilename }}</SearchLabel></template>
|
||||
<template #caption><SearchKeyword>{{ i18n.ts.keepOriginalFilenameDescription }}</SearchKeyword></template>
|
||||
<template #caption><SearchText>{{ i18n.ts.keepOriginalFilenameDescription }}</SearchText></template>
|
||||
</MkSwitch>
|
||||
</MkPreferenceContainer>
|
||||
</SearchMarker>
|
||||
@@ -74,7 +74,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<SearchMarker :keywords="['auto', 'nsfw', 'sensitive', 'media', 'file']">
|
||||
<MkSwitch v-model="autoSensitive" @update:modelValue="saveProfile()">
|
||||
<template #label><SearchLabel>{{ i18n.ts.enableAutoSensitive }}</SearchLabel><span class="_beta">{{ i18n.ts.beta }}</span></template>
|
||||
<template #caption><SearchKeyword>{{ i18n.ts.enableAutoSensitiveDescription }}</SearchKeyword></template>
|
||||
<template #caption><SearchText>{{ i18n.ts.enableAutoSensitiveDescription }}</SearchText></template>
|
||||
</MkSwitch>
|
||||
</SearchMarker>
|
||||
</div>
|
||||
|
||||
@@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<div>{{ i18n.ts._preferencesBackup.autoPreferencesBackupIsNotEnabledForThisDevice }}</div>
|
||||
<div><button class="_textButton" @click="enableAutoBackup">{{ i18n.ts.enable }}</button> | <button class="_textButton" @click="skipAutoBackup">{{ i18n.ts.skip }}</button></div>
|
||||
</MkInfo>
|
||||
<MkSuperMenu :def="menuDef" :grid="narrow" :searchIndex="SETTING_INDEX"></MkSuperMenu>
|
||||
<MkSuperMenu :def="menuDef" :grid="narrow" :searchIndex="searchIndex"></MkSuperMenu>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="!(narrow && currentPage?.route.name == null)" class="main">
|
||||
@@ -42,12 +42,12 @@ import { instance } from '@/instance.js';
|
||||
import { definePage, provideMetadataReceiver, provideReactiveMetadata } from '@/page.js';
|
||||
import * as os from '@/os.js';
|
||||
import { useRouter } from '@/router.js';
|
||||
import { searchIndexes } from '@/utility/settings-search-index.js';
|
||||
import { enableAutoBackup, getPreferencesProfileMenu } from '@/preferences/utility.js';
|
||||
import { store } from '@/store.js';
|
||||
import { signout } from '@/signout.js';
|
||||
import { genSearchIndexes } from '@/utility/inapp-search.js';
|
||||
|
||||
const SETTING_INDEX = searchIndexes; // TODO: lazy load
|
||||
const searchIndex = await import('search-index:settings').then(({ searchIndexes }) => genSearchIndexes(searchIndexes));
|
||||
|
||||
const indexInfo = {
|
||||
title: i18n.ts.settings,
|
||||
|
||||
@@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<SearchMarker path="/settings/mute-block" :label="i18n.ts.muteAndBlock" icon="ti ti-ban" :keywords="['mute', 'block']">
|
||||
<div class="_gaps_m">
|
||||
<MkFeatureBanner icon="/client-assets/prohibited_3d.png" color="#ff2600">
|
||||
<SearchKeyword>{{ i18n.ts._settings.muteAndBlockBanner }}</SearchKeyword>
|
||||
<SearchText>{{ i18n.ts._settings.muteAndBlockBanner }}</SearchText>
|
||||
</MkFeatureBanner>
|
||||
|
||||
<div class="_gaps_s">
|
||||
@@ -189,10 +189,10 @@ import { ensureSignin } from '@/i.js';
|
||||
import MkInfo from '@/components/MkInfo.vue';
|
||||
import MkFolder from '@/components/MkFolder.vue';
|
||||
import MkSwitch from '@/components/MkSwitch.vue';
|
||||
import { reloadAsk } from '@/utility/reload-ask.js';
|
||||
import { prefer } from '@/preferences.js';
|
||||
import MkFeatureBanner from '@/components/MkFeatureBanner.vue';
|
||||
import { Paginator } from '@/utility/paginator.js';
|
||||
import { suggestReload } from '@/utility/reload-suggest.js';
|
||||
|
||||
const $i = ensureSignin();
|
||||
|
||||
@@ -216,8 +216,8 @@ const showSoftWordMutedWord = prefer.model('showSoftWordMutedWord');
|
||||
|
||||
watch([
|
||||
showSoftWordMutedWord,
|
||||
], async () => {
|
||||
await reloadAsk({ reason: i18n.ts.reloadToApplySetting, unison: true });
|
||||
], () => {
|
||||
suggestReload();
|
||||
});
|
||||
|
||||
async function unrenoteMute(user, ev) {
|
||||
|
||||
@@ -64,7 +64,6 @@ import MkPreferenceContainer from '@/components/MkPreferenceContainer.vue';
|
||||
import * as os from '@/os.js';
|
||||
import { navbarItemDef } from '@/navbar.js';
|
||||
import { store } from '@/store.js';
|
||||
import { reloadAsk } from '@/utility/reload-ask.js';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import { definePage } from '@/page.js';
|
||||
import { prefer } from '@/preferences.js';
|
||||
|
||||
@@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<SearchMarker path="/settings/notifications" :label="i18n.ts.notifications" :keywords="['notifications']" icon="ti ti-bell">
|
||||
<div class="_gaps_m">
|
||||
<MkFeatureBanner icon="/client-assets/bell_3d.png" color="#ffff00">
|
||||
<SearchKeyword>{{ i18n.ts._settings.notificationsBanner }}</SearchKeyword>
|
||||
<SearchText>{{ i18n.ts._settings.notificationsBanner }}</SearchText>
|
||||
</MkFeatureBanner>
|
||||
|
||||
<FormSection first>
|
||||
|
||||
@@ -75,7 +75,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<div class="_gaps_m">
|
||||
<FormInfo warn>{{ i18n.ts._accountDelete.mayTakeTime }}</FormInfo>
|
||||
<FormInfo>{{ i18n.ts._accountDelete.sendEmail }}</FormInfo>
|
||||
<MkButton v-if="!$i.isDeleted" danger @click="deleteAccount"><SearchKeyword>{{ i18n.ts._accountDelete.requestAccountDelete }}</SearchKeyword></MkButton>
|
||||
<MkButton v-if="!$i.isDeleted" danger @click="deleteAccount"><SearchText>{{ i18n.ts._accountDelete.requestAccountDelete }}</SearchText></MkButton>
|
||||
<MkButton v-else disabled>{{ i18n.ts._accountDelete.inProgress }}</MkButton>
|
||||
</div>
|
||||
</MkFolder>
|
||||
@@ -155,13 +155,13 @@ import { misskeyApi } from '@/utility/misskey-api.js';
|
||||
import { ensureSignin } from '@/i.js';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import { definePage } from '@/page.js';
|
||||
import { reloadAsk } from '@/utility/reload-ask.js';
|
||||
import FormSection from '@/components/form/section.vue';
|
||||
import { prefer } from '@/preferences.js';
|
||||
import MkRolePreview from '@/components/MkRolePreview.vue';
|
||||
import { signout } from '@/signout.js';
|
||||
import { migrateOldSettings } from '@/pref-migrate.js';
|
||||
import { hideAllTips as _hideAllTips, resetAllTips as _resetAllTips } from '@/tips.js';
|
||||
import { suggestReload } from '@/utility/reload-suggest.js';
|
||||
|
||||
const $i = ensureSignin();
|
||||
|
||||
@@ -172,8 +172,8 @@ const devMode = prefer.model('devMode');
|
||||
const stackingRouterView = prefer.model('experimental.stackingRouterView');
|
||||
const enableFolderPageView = prefer.model('experimental.enableFolderPageView');
|
||||
|
||||
watch(skipNoteRender, async () => {
|
||||
await reloadAsk({ reason: i18n.ts.reloadToApplySetting, unison: true });
|
||||
watch(skipNoteRender, () => {
|
||||
suggestReload();
|
||||
});
|
||||
|
||||
async function deleteAccount() {
|
||||
|
||||
@@ -7,10 +7,12 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<SearchMarker path="/settings/plugin" :label="i18n.ts.plugins" :keywords="['plugin', 'addon', 'extension']" icon="ti ti-plug">
|
||||
<div class="_gaps_m">
|
||||
<MkFeatureBanner icon="/client-assets/electric_plug_3d.png" color="#ffbb00">
|
||||
<SearchKeyword>{{ i18n.ts._settings.pluginBanner }}</SearchKeyword>
|
||||
<SearchText>{{ i18n.ts._settings.pluginBanner }}</SearchText>
|
||||
</MkFeatureBanner>
|
||||
|
||||
<FormLink to="/settings/plugin/install"><template #icon><i class="ti ti-download"></i></template>{{ i18n.ts._plugin.install }}</FormLink>
|
||||
<MkInfo v-if="isSafeMode" warn>{{ i18n.ts.pluginsAreDisabledBecauseSafeMode }}</MkInfo>
|
||||
|
||||
<FormLink v-else to="/settings/plugin/install"><template #icon><i class="ti ti-download"></i></template>{{ i18n.ts._plugin.install }}</FormLink>
|
||||
|
||||
<FormSection>
|
||||
<template #label>{{ i18n.ts.manage }}</template>
|
||||
@@ -103,10 +105,12 @@ import MkCode from '@/components/MkCode.vue';
|
||||
import MkFolder from '@/components/MkFolder.vue';
|
||||
import MkKeyValue from '@/components/MkKeyValue.vue';
|
||||
import MkFeatureBanner from '@/components/MkFeatureBanner.vue';
|
||||
import MkInfo from '@/components/MkInfo.vue';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import { definePage } from '@/page.js';
|
||||
import { changePluginActive, configPlugin, pluginLogs, uninstallPlugin, reloadPlugin } from '@/plugin.js';
|
||||
import { prefer } from '@/preferences.js';
|
||||
import { isSafeMode } from '@@/js/config.js';
|
||||
import * as os from '@/os.js';
|
||||
|
||||
const plugins = prefer.r.plugins;
|
||||
|
||||
@@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<SearchMarker path="/settings/preferences" :label="i18n.ts.preferences" :keywords="['general', 'preferences']" icon="ti ti-adjustments">
|
||||
<div class="_gaps_m">
|
||||
<MkFeatureBanner icon="/client-assets/gear_3d.png" color="#00ff9d">
|
||||
<SearchKeyword>{{ i18n.ts._settings.preferencesBanner }}</SearchKeyword>
|
||||
<SearchText>{{ i18n.ts._settings.preferencesBanner }}</SearchText>
|
||||
</MkFeatureBanner>
|
||||
|
||||
<div class="_gaps_s">
|
||||
@@ -44,7 +44,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<SearchMarker :keywords="['realtimemode']">
|
||||
<MkSwitch v-model="realtimeMode">
|
||||
<template #label><i class="ti ti-bolt"></i> <SearchLabel>{{ i18n.ts.realtimeMode }}</SearchLabel></template>
|
||||
<template #caption><SearchKeyword>{{ i18n.ts._settings.realtimeMode_description }}</SearchKeyword></template>
|
||||
<template #caption><SearchText>{{ i18n.ts._settings.realtimeMode_description }}</SearchText></template>
|
||||
</MkSwitch>
|
||||
</SearchMarker>
|
||||
|
||||
@@ -53,7 +53,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<MkPreferenceContainer k="pollingInterval">
|
||||
<MkRange v-model="pollingInterval" :min="1" :max="3" :step="1" easing :showTicks="true" :textConverter="(v) => v === 1 ? i18n.ts.low : v === 2 ? i18n.ts.middle : v === 3 ? i18n.ts.high : ''">
|
||||
<template #label><SearchLabel>{{ i18n.ts._settings.contentsUpdateFrequency }}</SearchLabel></template>
|
||||
<template #caption><SearchKeyword>{{ i18n.ts._settings.contentsUpdateFrequency_description }}</SearchKeyword><br><SearchKeyword>{{ i18n.ts._settings.contentsUpdateFrequency_description2 }}</SearchKeyword></template>
|
||||
<template #caption><SearchText>{{ i18n.ts._settings.contentsUpdateFrequency_description }}</SearchText><br><SearchText>{{ i18n.ts._settings.contentsUpdateFrequency_description2 }}</SearchText></template>
|
||||
<template #prefix><i class="ti ti-player-play"></i></template>
|
||||
<template #suffix><i class="ti ti-player-track-next"></i></template>
|
||||
</MkRange>
|
||||
@@ -165,7 +165,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<MkPreferenceContainer k="collapseRenotes">
|
||||
<MkSwitch v-model="collapseRenotes">
|
||||
<template #label><SearchLabel>{{ i18n.ts.collapseRenotes }}</SearchLabel></template>
|
||||
<template #caption><SearchKeyword>{{ i18n.ts.collapseRenotesDescription }}</SearchKeyword></template>
|
||||
<template #caption><SearchText>{{ i18n.ts.collapseRenotesDescription }}</SearchText></template>
|
||||
</MkSwitch>
|
||||
</MkPreferenceContainer>
|
||||
</SearchMarker>
|
||||
@@ -449,7 +449,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
<div class="_gaps_m">
|
||||
<MkFeatureBanner icon="/client-assets/mens_room_3d.png" color="#0011ff">
|
||||
<SearchKeyword>{{ i18n.ts._settings.accessibilityBanner }}</SearchKeyword>
|
||||
<SearchText>{{ i18n.ts._settings.accessibilityBanner }}</SearchText>
|
||||
</MkFeatureBanner>
|
||||
|
||||
<div class="_gaps_s">
|
||||
@@ -489,7 +489,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<MkPreferenceContainer k="enablePullToRefresh">
|
||||
<MkSwitch v-model="enablePullToRefresh">
|
||||
<template #label><SearchLabel>{{ i18n.ts._settings.enablePullToRefresh }}</SearchLabel></template>
|
||||
<template #caption><SearchKeyword>{{ i18n.ts._settings.enablePullToRefresh_description }}</SearchKeyword></template>
|
||||
<template #caption><SearchText>{{ i18n.ts._settings.enablePullToRefresh_description }}</SearchText></template>
|
||||
</MkSwitch>
|
||||
</MkPreferenceContainer>
|
||||
</SearchMarker>
|
||||
@@ -571,7 +571,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<MkPreferenceContainer k="animation">
|
||||
<MkSwitch :modelValue="!reduceAnimation" @update:modelValue="v => reduceAnimation = !v">
|
||||
<template #label><SearchLabel>{{ i18n.ts._settings.uiAnimations }}</SearchLabel></template>
|
||||
<template #caption><SearchKeyword>{{ i18n.ts.turnOffToImprovePerformance }}</SearchKeyword></template>
|
||||
<template #caption><SearchText>{{ i18n.ts.turnOffToImprovePerformance }}</SearchText></template>
|
||||
</MkSwitch>
|
||||
</MkPreferenceContainer>
|
||||
</SearchMarker>
|
||||
@@ -580,7 +580,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<MkPreferenceContainer k="useBlurEffect">
|
||||
<MkSwitch v-model="useBlurEffect">
|
||||
<template #label><SearchLabel>{{ i18n.ts.useBlurEffect }}</SearchLabel></template>
|
||||
<template #caption><SearchKeyword>{{ i18n.ts.turnOffToImprovePerformance }}</SearchKeyword></template>
|
||||
<template #caption><SearchText>{{ i18n.ts.turnOffToImprovePerformance }}</SearchText></template>
|
||||
</MkSwitch>
|
||||
</MkPreferenceContainer>
|
||||
</SearchMarker>
|
||||
@@ -589,7 +589,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<MkPreferenceContainer k="useBlurEffectForModal">
|
||||
<MkSwitch v-model="useBlurEffectForModal">
|
||||
<template #label><SearchLabel>{{ i18n.ts.useBlurEffectForModal }}</SearchLabel></template>
|
||||
<template #caption><SearchKeyword>{{ i18n.ts.turnOffToImprovePerformance }}</SearchKeyword></template>
|
||||
<template #caption><SearchText>{{ i18n.ts.turnOffToImprovePerformance }}</SearchText></template>
|
||||
</MkSwitch>
|
||||
</MkPreferenceContainer>
|
||||
</SearchMarker>
|
||||
@@ -598,7 +598,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<MkPreferenceContainer k="enableHighQualityImagePlaceholders">
|
||||
<MkSwitch v-model="enableHighQualityImagePlaceholders">
|
||||
<template #label><SearchLabel>{{ i18n.ts._settings.enableHighQualityImagePlaceholders }}</SearchLabel></template>
|
||||
<template #caption><SearchKeyword>{{ i18n.ts.turnOffToImprovePerformance }}</SearchKeyword></template>
|
||||
<template #caption><SearchText>{{ i18n.ts.turnOffToImprovePerformance }}</SearchText></template>
|
||||
</MkSwitch>
|
||||
</MkPreferenceContainer>
|
||||
</SearchMarker>
|
||||
@@ -607,7 +607,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<MkPreferenceContainer k="useStickyIcons">
|
||||
<MkSwitch v-model="useStickyIcons">
|
||||
<template #label><SearchLabel>{{ i18n.ts._settings.useStickyIcons }}</SearchLabel></template>
|
||||
<template #caption><SearchKeyword>{{ i18n.ts.turnOffToImprovePerformance }}</SearchKeyword></template>
|
||||
<template #caption><SearchText>{{ i18n.ts.turnOffToImprovePerformance }}</SearchText></template>
|
||||
</MkSwitch>
|
||||
</MkPreferenceContainer>
|
||||
</SearchMarker>
|
||||
@@ -795,7 +795,6 @@ import MkInfo from '@/components/MkInfo.vue';
|
||||
import { store } from '@/store.js';
|
||||
import * as os from '@/os.js';
|
||||
import { misskeyApi } from '@/utility/misskey-api.js';
|
||||
import { reloadAsk } from '@/utility/reload-ask.js';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import { definePage } from '@/page.js';
|
||||
import { miLocalStorage } from '@/local-storage.js';
|
||||
@@ -807,6 +806,7 @@ import { claimAchievement } from '@/utility/achievements.js';
|
||||
import { instance } from '@/instance.js';
|
||||
import { ensureSignin } from '@/i.js';
|
||||
import { genId } from '@/utility/id.js';
|
||||
import { suggestReload } from '@/utility/reload-suggest.js';
|
||||
|
||||
const $i = ensureSignin();
|
||||
|
||||
@@ -928,8 +928,8 @@ watch([
|
||||
enablePullToRefresh,
|
||||
reduceAnimation,
|
||||
showAvailableReactionsFirstInNote,
|
||||
], async () => {
|
||||
await reloadAsk({ reason: i18n.ts.reloadToApplySetting, unison: true });
|
||||
], () => {
|
||||
suggestReload();
|
||||
});
|
||||
|
||||
const emojiIndexLangs = ['en-US', 'ja-JP', 'ja-JP_hira'] as const;
|
||||
|
||||
@@ -7,13 +7,13 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<SearchMarker path="/settings/privacy" :label="i18n.ts.privacy" :keywords="['privacy']" icon="ti ti-lock-open">
|
||||
<div class="_gaps_m">
|
||||
<MkFeatureBanner icon="/client-assets/unlocked_3d.png" color="#aeff00">
|
||||
<SearchKeyword>{{ i18n.ts._settings.privacyBanner }}</SearchKeyword>
|
||||
<SearchText>{{ i18n.ts._settings.privacyBanner }}</SearchText>
|
||||
</MkFeatureBanner>
|
||||
|
||||
<SearchMarker :keywords="['follow', 'lock']">
|
||||
<MkSwitch v-model="isLocked" @update:modelValue="save()">
|
||||
<template #label><SearchLabel>{{ i18n.ts.makeFollowManuallyApprove }}</SearchLabel></template>
|
||||
<template #caption><SearchKeyword>{{ i18n.ts.lockedAccountInfo }}</SearchKeyword></template>
|
||||
<template #caption><SearchText>{{ i18n.ts.lockedAccountInfo }}</SearchText></template>
|
||||
</MkSwitch>
|
||||
</SearchMarker>
|
||||
|
||||
@@ -28,7 +28,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<SearchMarker :keywords="['reaction', 'public']">
|
||||
<MkSwitch v-model="publicReactions" @update:modelValue="save()">
|
||||
<template #label><SearchLabel>{{ i18n.ts.makeReactionsPublic }}</SearchLabel></template>
|
||||
<template #caption><SearchKeyword>{{ i18n.ts.makeReactionsPublicDescription }}</SearchKeyword></template>
|
||||
<template #caption><SearchText>{{ i18n.ts.makeReactionsPublicDescription }}</SearchText></template>
|
||||
</MkSwitch>
|
||||
</SearchMarker>
|
||||
|
||||
@@ -53,28 +53,28 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<SearchMarker :keywords="['online', 'status']">
|
||||
<MkSwitch v-model="hideOnlineStatus" @update:modelValue="save()">
|
||||
<template #label><SearchLabel>{{ i18n.ts.hideOnlineStatus }}</SearchLabel></template>
|
||||
<template #caption><SearchKeyword>{{ i18n.ts.hideOnlineStatusDescription }}</SearchKeyword></template>
|
||||
<template #caption><SearchText>{{ i18n.ts.hideOnlineStatusDescription }}</SearchText></template>
|
||||
</MkSwitch>
|
||||
</SearchMarker>
|
||||
|
||||
<SearchMarker :keywords="['crawle', 'index', 'search']">
|
||||
<MkSwitch v-model="noCrawle" @update:modelValue="save()">
|
||||
<template #label><SearchLabel>{{ i18n.ts.noCrawle }}</SearchLabel></template>
|
||||
<template #caption><SearchKeyword>{{ i18n.ts.noCrawleDescription }}</SearchKeyword></template>
|
||||
<template #caption><SearchText>{{ i18n.ts.noCrawleDescription }}</SearchText></template>
|
||||
</MkSwitch>
|
||||
</SearchMarker>
|
||||
|
||||
<SearchMarker :keywords="['crawle', 'ai']">
|
||||
<MkSwitch v-model="preventAiLearning" @update:modelValue="save()">
|
||||
<template #label><SearchLabel>{{ i18n.ts.preventAiLearning }}</SearchLabel></template>
|
||||
<template #caption><SearchKeyword>{{ i18n.ts.preventAiLearningDescription }}</SearchKeyword></template>
|
||||
<template #caption><SearchText>{{ i18n.ts.preventAiLearningDescription }}</SearchText></template>
|
||||
</MkSwitch>
|
||||
</SearchMarker>
|
||||
|
||||
<SearchMarker :keywords="['explore']">
|
||||
<MkSwitch v-model="isExplorable" @update:modelValue="save()">
|
||||
<template #label><SearchLabel>{{ i18n.ts.makeExplorable }}</SearchLabel></template>
|
||||
<template #caption><SearchKeyword>{{ i18n.ts.makeExplorableDescription }}</SearchKeyword></template>
|
||||
<template #caption><SearchText>{{ i18n.ts.makeExplorableDescription }}</SearchText></template>
|
||||
</MkSwitch>
|
||||
</SearchMarker>
|
||||
|
||||
@@ -146,7 +146,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
</div>
|
||||
|
||||
<template #caption>
|
||||
<div><SearchKeyword>{{ i18n.ts._accountSettings.makeNotesFollowersOnlyBeforeDescription }}</SearchKeyword></div>
|
||||
<div><SearchText>{{ i18n.ts._accountSettings.makeNotesFollowersOnlyBeforeDescription }}</SearchText></div>
|
||||
</template>
|
||||
</FormSlot>
|
||||
</SearchMarker>
|
||||
@@ -183,7 +183,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
</div>
|
||||
|
||||
<template #caption>
|
||||
<div><SearchKeyword>{{ i18n.ts._accountSettings.makeNotesHiddenBeforeDescription }}</SearchKeyword></div>
|
||||
<div><SearchText>{{ i18n.ts._accountSettings.makeNotesHiddenBeforeDescription }}</SearchText></div>
|
||||
</template>
|
||||
</FormSlot>
|
||||
</SearchMarker>
|
||||
|
||||
@@ -110,7 +110,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<MkInput v-model="profile.followedMessage" :max="200" manualSave :mfmPreview="false">
|
||||
<template #label><SearchLabel>{{ i18n.ts._profile.followedMessage }}</SearchLabel><span class="_beta">{{ i18n.ts.beta }}</span></template>
|
||||
<template #caption>
|
||||
<div><SearchKeyword>{{ i18n.ts._profile.followedMessageDescription }}</SearchKeyword></div>
|
||||
<div><SearchText>{{ i18n.ts._profile.followedMessageDescription }}</SearchText></div>
|
||||
<div>{{ i18n.ts._profile.followedMessageDescriptionForLockedAccount }}</div>
|
||||
</template>
|
||||
</MkInput>
|
||||
|
||||
@@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<SearchMarker path="/settings/security" :label="i18n.ts.security" :keywords="['security']" icon="ti ti-lock" :inlining="['2fa']">
|
||||
<div class="_gaps_m">
|
||||
<MkFeatureBanner icon="/client-assets/locked_with_key_3d.png" color="#ffbf00">
|
||||
<SearchKeyword>{{ i18n.ts._settings.securityBanner }}</SearchKeyword>
|
||||
<SearchText>{{ i18n.ts._settings.securityBanner }}</SearchText>
|
||||
</MkFeatureBanner>
|
||||
|
||||
<SearchMarker :keywords="['password']">
|
||||
@@ -24,30 +24,34 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
<X2fa/>
|
||||
|
||||
<FormSection>
|
||||
<template #label>{{ i18n.ts.signinHistory }}</template>
|
||||
<MkPagination :paginator="paginator" withControl>
|
||||
<template #default="{items}">
|
||||
<div>
|
||||
<div v-for="item in items" :key="item.id" v-panel class="timnmucd">
|
||||
<header>
|
||||
<i v-if="item.success" class="ti ti-check icon succ"></i>
|
||||
<i v-else class="ti ti-circle-x icon fail"></i>
|
||||
<code class="ip _monospace">{{ item.ip }}</code>
|
||||
<MkTime :time="item.createdAt" class="time"/>
|
||||
</header>
|
||||
<SearchMarker :keywords="['signin', 'login', 'history', 'log']">
|
||||
<FormSection>
|
||||
<template #label><SearchLabel>{{ i18n.ts.signinHistory }}</SearchLabel></template>
|
||||
<MkPagination :paginator="paginator" withControl>
|
||||
<template #default="{items}">
|
||||
<div>
|
||||
<div v-for="item in items" :key="item.id" v-panel class="timnmucd">
|
||||
<header>
|
||||
<i v-if="item.success" class="ti ti-check icon succ"></i>
|
||||
<i v-else class="ti ti-circle-x icon fail"></i>
|
||||
<code class="ip _monospace">{{ item.ip }}</code>
|
||||
<MkTime :time="item.createdAt" class="time"/>
|
||||
</header>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</MkPagination>
|
||||
</FormSection>
|
||||
</template>
|
||||
</MkPagination>
|
||||
</FormSection>
|
||||
</SearchMarker>
|
||||
|
||||
<FormSection>
|
||||
<FormSlot>
|
||||
<MkButton danger @click="regenerateToken"><i class="ti ti-refresh"></i> {{ i18n.ts.regenerateLoginToken }}</MkButton>
|
||||
<template #caption>{{ i18n.ts.regenerateLoginTokenDescription }}</template>
|
||||
</FormSlot>
|
||||
</FormSection>
|
||||
<SearchMarker :keywords="['regenerate', 'refresh', 'reset', 'token']">
|
||||
<FormSection>
|
||||
<FormSlot>
|
||||
<MkButton danger @click="regenerateToken"><i class="ti ti-refresh"></i> <SearchLabel>{{ i18n.ts.regenerateLoginToken }}</SearchLabel></MkButton>
|
||||
<template #caption>{{ i18n.ts.regenerateLoginTokenDescription }}</template>
|
||||
</FormSlot>
|
||||
</FormSection>
|
||||
</SearchMarker>
|
||||
</div>
|
||||
</SearchMarker>
|
||||
</template>
|
||||
|
||||
@@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<SearchMarker path="/settings/sounds" :label="i18n.ts.sounds" :keywords="['sounds']" icon="ti ti-music">
|
||||
<div class="_gaps_m">
|
||||
<MkFeatureBanner icon="/client-assets/speaker_high_volume_3d.png" color="#ff006f">
|
||||
<SearchKeyword>{{ i18n.ts._settings.soundsBanner }}</SearchKeyword>
|
||||
<SearchText>{{ i18n.ts._settings.soundsBanner }}</SearchText>
|
||||
</MkFeatureBanner>
|
||||
|
||||
<SearchMarker :keywords="['mute']">
|
||||
|
||||
@@ -35,7 +35,9 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="_gaps">
|
||||
<MkInfo v-if="isSafeMode" warn>{{ i18n.ts.themeIsDefaultBecauseSafeMode }}</MkInfo>
|
||||
|
||||
<div v-else class="_gaps">
|
||||
<template v-if="!store.r.darkMode.value">
|
||||
<SearchMarker :keywords="['light', 'theme']">
|
||||
<MkFolder :defaultOpen="true" :max-height="500">
|
||||
@@ -204,12 +206,14 @@ import JSON5 from 'json5';
|
||||
import defaultLightTheme from '@@/themes/l-light.json5';
|
||||
import defaultDarkTheme from '@@/themes/d-green-lime.json5';
|
||||
import type { Theme } from '@/theme.js';
|
||||
import { isSafeMode } from '@@/js/config.js';
|
||||
import * as os from '@/os.js';
|
||||
import MkSwitch from '@/components/MkSwitch.vue';
|
||||
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 MkInfo from '@/components/MkInfo.vue';
|
||||
import { getBuiltinThemesRef, getThemesRef, removeTheme } from '@/theme.js';
|
||||
import { isDeviceDarkmode } from '@/utility/is-device-darkmode.js';
|
||||
import { store } from '@/store.js';
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
import { ref, defineAsyncComponent } from 'vue';
|
||||
import { Interpreter, Parser, utils, values } from '@syuilo/aiscript';
|
||||
import { compareVersions } from 'compare-versions';
|
||||
import { isSafeMode } from '@@/js/config.js';
|
||||
import { genId } from '@/utility/id.js';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import { aiScriptReadline, createAiScriptEnv } from '@/aiscript/api.js';
|
||||
@@ -232,6 +233,7 @@ export function launchPlugins() {
|
||||
}
|
||||
|
||||
async function launchPlugin(id: Plugin['installId']): Promise<void> {
|
||||
if (isSafeMode) return;
|
||||
const plugin = prefer.s.plugins.find(x => x.installId === id);
|
||||
if (!plugin) return;
|
||||
|
||||
|
||||
@@ -491,10 +491,6 @@ export const ROUTE_DEF = [{
|
||||
path: '/performance',
|
||||
name: 'performance',
|
||||
component: page(() => import('@/pages/admin/performance.vue')),
|
||||
}, {
|
||||
path: '/server-rules',
|
||||
name: 'server-rules',
|
||||
component: page(() => import('@/pages/admin/server-rules.vue')),
|
||||
}, {
|
||||
path: '/invites',
|
||||
name: 'invites',
|
||||
|
||||
@@ -37,11 +37,6 @@ html {
|
||||
color: var(--MI_THEME-fg);
|
||||
accent-color: var(--MI_THEME-accent);
|
||||
|
||||
&, * {
|
||||
scrollbar-color: var(--MI_THEME-scrollbarHandle) transparent;
|
||||
scrollbar-width: thin;
|
||||
}
|
||||
|
||||
&.f-1 {
|
||||
font-size: 15px;
|
||||
}
|
||||
@@ -91,7 +86,11 @@ html::selection {
|
||||
100% {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
html, body, main, div {
|
||||
scrollbar-color: var(--MI_THEME-scrollbarHandle) transparent;
|
||||
scrollbar-width: thin;
|
||||
}
|
||||
|
||||
html,
|
||||
|
||||
@@ -48,10 +48,6 @@ function skip() {
|
||||
.title {
|
||||
padding: 0 10px;
|
||||
font-weight: bold;
|
||||
|
||||
&:empty {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.body {
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
<!--
|
||||
SPDX-FileCopyrightText: syuilo and misskey-project
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div :class="$style.root">
|
||||
<span :class="$style.icon">
|
||||
<i class="ti ti-info-circle"></i>
|
||||
</span>
|
||||
<span :class="$style.title">{{ i18n.ts.reloadRequiredToApplySettings }}</span>
|
||||
<span :class="$style.body"><button class="_textButton" style="color: var(--MI_THEME-fgOnAccent);" @click="reload">{{ i18n.ts.reload }}</button> | <button class="_textButton" style="color: var(--MI_THEME-fgOnAccent);" @click="skip">{{ i18n.ts.skip }}</button></span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { i18n } from '@/i18n.js';
|
||||
import { shouldSuggestReload } from '@/utility/reload-suggest.js';
|
||||
import { unisonReload } from '@/utility/unison-reload.js';
|
||||
|
||||
function reload() {
|
||||
unisonReload();
|
||||
}
|
||||
|
||||
function skip() {
|
||||
shouldSuggestReload.value = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" module>
|
||||
.root {
|
||||
--height: 24px;
|
||||
font-size: 0.85em;
|
||||
display: flex;
|
||||
vertical-align: bottom;
|
||||
width: 100%;
|
||||
line-height: var(--height);
|
||||
height: var(--height);
|
||||
overflow: clip;
|
||||
contain: strict;
|
||||
background: var(--MI_THEME-accent);
|
||||
color: var(--MI_THEME-fgOnAccent);
|
||||
}
|
||||
|
||||
.icon {
|
||||
margin-left: 10px;
|
||||
animation: blink 2s infinite;
|
||||
}
|
||||
|
||||
.title {
|
||||
padding: 0 10px;
|
||||
font-weight: bold;
|
||||
animation: blink 2s infinite;
|
||||
}
|
||||
|
||||
.body {
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
overflow: clip;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
@keyframes blink {
|
||||
0% { opacity: 1; }
|
||||
10% { opacity: 1; }
|
||||
50% { opacity: 0; }
|
||||
90% { opacity: 1; }
|
||||
100% { opacity: 1; }
|
||||
}
|
||||
</style>
|
||||
@@ -94,6 +94,11 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<div v-if="dev" id="devTicker"><span style="animation: dev-ticker-blink 2s infinite;">DEV BUILD</span></div>
|
||||
|
||||
<div v-if="$i && $i.isBot" id="botWarn"><span style="animation: dev-ticker-blink 2s infinite;">{{ i18n.ts.loggedInAsBot }}</span></div>
|
||||
|
||||
<div v-if="isSafeMode" id="safemodeWarn">
|
||||
<span style="animation: dev-ticker-blink 2s infinite;">{{ i18n.ts.safeModeEnabled }}</span>
|
||||
<button class="_textButton" style="pointer-events: all;" @click="exitSafeMode">{{ i18n.ts.turnItOff }}</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
@@ -101,7 +106,10 @@ import { defineAsyncComponent, ref, TransitionGroup } from 'vue';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import { swInject } from './sw-inject.js';
|
||||
import XNotification from './notification.vue';
|
||||
import { isSafeMode } from '@@/js/config.js';
|
||||
import { popups } from '@/os.js';
|
||||
import { unisonReload } from '@/utility/unison-reload.js';
|
||||
import { miLocalStorage } from '@/local-storage.js';
|
||||
import { pendingApiRequestsCount } from '@/utility/misskey-api.js';
|
||||
import * as sound from '@/utility/sound.js';
|
||||
import { $i } from '@/i.js';
|
||||
@@ -144,6 +152,13 @@ function onNotification(notification: Misskey.entities.Notification, isClient =
|
||||
sound.playMisskeySfx('notification');
|
||||
}
|
||||
|
||||
function exitSafeMode() {
|
||||
miLocalStorage.removeItem('isSafeMode');
|
||||
const url = new URL(window.location.href);
|
||||
url.searchParams.delete('safemode');
|
||||
unisonReload(url.toString());
|
||||
}
|
||||
|
||||
if ($i) {
|
||||
if (store.s.realtimeMode) {
|
||||
const connection = useStream().useChannel('main');
|
||||
@@ -396,7 +411,7 @@ if ($i) {
|
||||
width: 100%;
|
||||
height: max-content;
|
||||
text-align: center;
|
||||
z-index: 2147483647;
|
||||
z-index: 2147483646;
|
||||
color: #ff0;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
padding: 4px 7px;
|
||||
@@ -405,6 +420,11 @@ if ($i) {
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
#safemodeWarn {
|
||||
@extend #botWarn;
|
||||
z-index: 2147483647;
|
||||
}
|
||||
|
||||
#devTicker {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
|
||||
@@ -13,6 +13,8 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<div :class="[$style.main, { [$style.withWallpaper]: withWallpaper, [$style.withSidebarAndTitlebar]: !isMobile && prefer.r['deck.navbarPosition'].value === 'left' && prefer.r.showTitlebar.value }]" :style="{ backgroundImage: prefer.s['deck.wallpaper'] != null ? `url(${ prefer.s['deck.wallpaper'] })` : null }">
|
||||
<XNavbarH v-if="!isMobile && prefer.r['deck.navbarPosition'].value === 'top'"/>
|
||||
|
||||
<XReloadSuggest v-if="shouldSuggestReload"/>
|
||||
<XPreferenceRestore v-if="shouldSuggestRestoreBackup"/>
|
||||
<XAnnouncements v-if="$i"/>
|
||||
<XStatusBars/>
|
||||
<div :class="$style.columnsWrapper">
|
||||
@@ -81,12 +83,14 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { defineAsyncComponent, ref, useTemplateRef } from 'vue';
|
||||
import { genId } from '@/utility/id.js';
|
||||
import XCommon from './_common_/common.vue';
|
||||
import { genId } from '@/utility/id.js';
|
||||
import XSidebar from '@/ui/_common_/navbar.vue';
|
||||
import XNavbarH from '@/ui/_common_/navbar-h.vue';
|
||||
import XMobileFooterMenu from '@/ui/_common_/mobile-footer-menu.vue';
|
||||
import XTitlebar from '@/ui/_common_/titlebar.vue';
|
||||
import XPreferenceRestore from '@/ui/_common_/PreferenceRestore.vue';
|
||||
import XReloadSuggest from '@/ui/_common_/ReloadSuggest.vue';
|
||||
import * as os from '@/os.js';
|
||||
import { $i } from '@/i.js';
|
||||
import { i18n } from '@/i18n.js';
|
||||
@@ -105,6 +109,8 @@ import XRoleTimelineColumn from '@/ui/deck/role-timeline-column.vue';
|
||||
import XChatColumn from '@/ui/deck/chat-column.vue';
|
||||
import { mainRouter } from '@/router.js';
|
||||
import { columns, layout, columnTypes, switchProfileMenu, addColumn as addColumnToStore, deleteProfile as deleteProfile_ } from '@/deck.js';
|
||||
import { shouldSuggestRestoreBackup } from '@/preferences/utility.js';
|
||||
import { shouldSuggestReload } from '@/utility/reload-suggest.js';
|
||||
|
||||
const XStatusBars = defineAsyncComponent(() => import('@/ui/_common_/statusbars.vue'));
|
||||
const XAnnouncements = defineAsyncComponent(() => import('@/ui/_common_/announcements.vue'));
|
||||
|
||||
@@ -12,6 +12,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
<div :class="[$style.contents, !isMobile && prefer.r.showTitlebar.value ? $style.withSidebarAndTitlebar : null]" @contextmenu.stop="onContextmenu">
|
||||
<div>
|
||||
<XReloadSuggest v-if="shouldSuggestReload"/>
|
||||
<XPreferenceRestore v-if="shouldSuggestRestoreBackup"/>
|
||||
<XAnnouncements v-if="$i"/>
|
||||
<XStatusBars :class="$style.statusbars"/>
|
||||
@@ -38,6 +39,7 @@ import XCommon from './_common_/common.vue';
|
||||
import type { PageMetadata } from '@/page.js';
|
||||
import XMobileFooterMenu from '@/ui/_common_/mobile-footer-menu.vue';
|
||||
import XPreferenceRestore from '@/ui/_common_/PreferenceRestore.vue';
|
||||
import XReloadSuggest from '@/ui/_common_/ReloadSuggest.vue';
|
||||
import XTitlebar from '@/ui/_common_/titlebar.vue';
|
||||
import XSidebar from '@/ui/_common_/navbar.vue';
|
||||
import * as os from '@/os.js';
|
||||
@@ -50,6 +52,7 @@ import { mainRouter } from '@/router.js';
|
||||
import { prefer } from '@/preferences.js';
|
||||
import { shouldSuggestRestoreBackup } from '@/preferences/utility.js';
|
||||
import { DI } from '@/di.js';
|
||||
import { shouldSuggestReload } from '@/utility/reload-suggest.js';
|
||||
|
||||
const XWidgets = defineAsyncComponent(() => import('./_common_/widgets.vue'));
|
||||
const XStatusBars = defineAsyncComponent(() => import('@/ui/_common_/statusbars.vue'));
|
||||
|
||||
@@ -44,6 +44,8 @@
|
||||
"😑": ["face", "indifferent", "-_-", "meh", "deadpan"],
|
||||
"😒": ["indifference", "bored", "straight face", "serious", "sarcasm", "unimpressed", "skeptical", "dubious", "side_eye"],
|
||||
"🙄": ["face", "eyeroll", "frustrated"],
|
||||
"🙂↔️": ["face", "head", "horizontally", "no", "shake", "shaking"],
|
||||
"🙂↕️": ["face", "head", "nod", "shaking", "vertically", "yes"],
|
||||
"🤔": ["face", "hmmm", "think", "consider"],
|
||||
"🤥": ["face", "lie", "pinocchio"],
|
||||
"🤭": ["face", "whoops", "shock", "surprise"],
|
||||
@@ -76,6 +78,7 @@
|
||||
"😥": ["face", "phew", "sweat", "nervous"],
|
||||
"🤤": ["face"],
|
||||
"😪": ["face", "tired", "rest", "nap"],
|
||||
"": ["face", "bags", "bored", "exhausted", "eyes", "fatigued", "late", "sleepy", "tired", "weary"],
|
||||
"😓": ["face", "hot", "sad", "tired", "exercise"],
|
||||
"🥵": ["face", "feverish", "heat", "red", "sweating"],
|
||||
"🥶": ["face", "blue", "freezing", "frozen", "frostbite", "icicles"],
|
||||
@@ -92,10 +95,11 @@
|
||||
"🥴": ["face", "dizzy", "intoxicated", "tipsy", "wavy"],
|
||||
"🥱": ["face", "tired", "yawning"],
|
||||
"😴": ["face", "tired", "sleepy", "night", "zzz"],
|
||||
"👁️🗨️": ["balloon", "bubble", "eye", "speech", "witness"],
|
||||
"💤": ["sleepy", "tired", "dream"],
|
||||
"😶🌫️": [],
|
||||
"😮💨": [],
|
||||
"😵💫": [],
|
||||
"😶🌫️": ["face", "absentminded", "clouds", "fog", "head"],
|
||||
"😮💨": ["face", "blow", "blowing", "exhale", "exhaling", "exhausted", "gasp", "groan", "relief", "sigh", "smiley", "smoke", "whisper", "whistle"],
|
||||
"😵💫": ["face", "confused", "dizzy", "eyes", "hypnotized", "omg", "smiley", "spiral", "trouble", "whoa", "woah", "woozy"],
|
||||
"🫠": ["disappear", "dissolve", "liquid", "melt", "toketa"],
|
||||
"🫢": ["amazement", "awe", "disbelief", "embarrass", "scared", "surprise", "ohoho"],
|
||||
"🫣": ["captivated", "peep", "stare", "chunibyo"],
|
||||
@@ -159,19 +163,19 @@
|
||||
"🤞": ["good", "lucky"],
|
||||
"🖖": ["hand", "fingers", "spock", "star trek"],
|
||||
"✍": ["lower_left_ballpoint_pen", "stationery", "write", "compose"],
|
||||
"🫰": [],
|
||||
"🫱": [],
|
||||
"🫲": [],
|
||||
"🫳": [],
|
||||
"🫴": [],
|
||||
"🫵": [],
|
||||
"🫰": ["<3", "crossed", "expensive", "finger", "hand", "heart", "index", "love", "money", "snap", "thumb"],
|
||||
"🫱": ["hand", "handshake", "hold", "reach", "right", "rightward", "rightwards", "shake"],
|
||||
"🫲": ["hand", "handshake", "hold", "left", "leftward", "leftwards", "reach", "shake"],
|
||||
"🫳": ["dismiss", "down", "drop", "dropped", "hand", "palm", "pick", "shoo", "up"],
|
||||
"🫴": ["beckon", "catch", "come", "hand", "hold", "know", "lift", "me", "offer", "palm", "tell"],
|
||||
"🫵": ["at", "finger", "hand", "index", "pointing", "poke", "viewer", "you"],
|
||||
"🫶": ["moemoekyun"],
|
||||
"🤏": ["hand", "fingers"],
|
||||
"🤌": ["hand", "fingers"],
|
||||
"🤳": ["camera", "phone"],
|
||||
"💅": ["beauty", "manicure", "finger", "fashion", "nail"],
|
||||
"👄": ["mouth", "kiss"],
|
||||
"🫦": [],
|
||||
"🫦": ["anxious", "bite", "biting", "fear", "flirt", "flirting", "kiss", "lip", "lipstick", "nervous", "sexy", "uncomfortable", "worried", "worry"],
|
||||
"🦷": ["teeth", "dentist"],
|
||||
"👅": ["mouth", "playful"],
|
||||
"👂": ["face", "hear", "sound", "listen"],
|
||||
@@ -180,11 +184,12 @@
|
||||
"👁": ["face", "look", "see", "watch", "stare"],
|
||||
"👀": ["look", "watch", "stalk", "peek", "see"],
|
||||
"🧠": ["smart", "intelligent"],
|
||||
"🫀": [],
|
||||
"🫁": [],
|
||||
"🫀": ["anatomical", "beat", "cardiology", "heart", "heartbeat", "organ", "pulse", "real", "red"],
|
||||
"🫁": ["breath", "breathe", "exhalation", "inhalation", "lung", "lungs", "organ", "respiration"],
|
||||
"👤": ["user", "person", "human"],
|
||||
"👥": ["user", "person", "human", "group", "team"],
|
||||
"🗣": ["user", "person", "human", "sing", "say", "talk"],
|
||||
"": ["clue", "crime", "detective", "fingerprint", "forensics", "identity", "mystery", "print", "safety", "trace"],
|
||||
"👶": ["child", "boy", "girl", "toddler"],
|
||||
"🧒": ["gender-neutral", "young"],
|
||||
"👦": ["man", "male", "guy", "teenager"],
|
||||
@@ -199,6 +204,7 @@
|
||||
"👩🦰": ["woman", "female", "girl", "ginger", "redhead"],
|
||||
"👨🦰": ["man", "male", "boy", "guy", "ginger", "redhead"],
|
||||
"👱♀️": ["woman", "female", "girl", "blonde", "person"],
|
||||
"👱♂️": ["blond", "blond-haired", "hair", "man"],
|
||||
"👱": ["man", "male", "boy", "blonde", "guy", "person"],
|
||||
"🧑🦳": ["gray", "old", "white"],
|
||||
"👩🦳": ["woman", "female", "girl", "gray", "old", "white"],
|
||||
@@ -207,20 +213,27 @@
|
||||
"👩🦲": ["woman", "female", "girl", "bald", "chemotherapy", "hairless", "shaven"],
|
||||
"👨🦲": ["man", "male", "boy", "guy", "bald", "chemotherapy", "hairless", "shaven"],
|
||||
"🧔": ["person", "bewhiskered"],
|
||||
"🧔♀️": ["beard", "bearded", "whiskers", "woman"],
|
||||
"🧔♂️": ["beard", "bearded", "man", "whiskers"],
|
||||
"🧓": ["human", "elder", "senior", "gender-neutral"],
|
||||
"👴": ["human", "male", "men", "old", "elder", "senior"],
|
||||
"👵": ["human", "female", "women", "lady", "old", "elder", "senior"],
|
||||
"👲": ["male", "boy", "chinese"],
|
||||
"🧕": ["female", "hijab", "mantilla", "tichel"],
|
||||
"👳♀️": ["female", "indian", "hinduism", "arabs", "woman"],
|
||||
"👳♂️": ["man", "turban", "wearing"],
|
||||
"👳": ["male", "indian", "hinduism", "arabs"],
|
||||
"👮♀️": ["woman", "police", "law", "legal", "enforcement", "arrest", "911", "female"],
|
||||
"👮♂️": ["apprehend", "arrest", "citation", "cop", "law", "man", "officer", "over", "police", "pulled", "undercover"],
|
||||
"👮": ["man", "police", "law", "legal", "enforcement", "arrest", "911"],
|
||||
"👷♀️": ["female", "human", "wip", "build", "construction", "worker", "labor", "woman"],
|
||||
"👷♂️": ["build", "construction", "fix", "hardhat", "hat", "man", "rebuild", "remodel", "repair", "work", "worker"],
|
||||
"👷": ["male", "human", "wip", "guy", "build", "construction", "worker", "labor"],
|
||||
"💂♀️": ["uk", "gb", "british", "female", "royal", "woman"],
|
||||
"💂♂️": ["buckingham", "guard", "helmet", "london", "man", "palace"],
|
||||
"💂": ["uk", "gb", "british", "male", "guy", "royal"],
|
||||
"🕵️♀️": ["human", "spy", "detective", "female", "woman"],
|
||||
"🕵️♂️": ["detective", "man", "sleuth", "spy"],
|
||||
"🕵": ["human", "spy", "detective"],
|
||||
"🧑⚕️": ["doctor", "nurse", "therapist", "healthcare", "human"],
|
||||
"👩⚕️": ["doctor", "nurse", "therapist", "healthcare", "woman", "human"],
|
||||
@@ -270,102 +283,152 @@
|
||||
"🧑⚖️": ["justice", "court", "human"],
|
||||
"👩⚖️": ["justice", "court", "woman", "human"],
|
||||
"👨⚖️": ["justice", "court", "man", "human"],
|
||||
"🦸": ["good", "hero", "superhero", "superpower"],
|
||||
"🦸♀️": ["woman", "female", "good", "heroine", "superpowers"],
|
||||
"🦸♂️": ["man", "male", "good", "hero", "superpowers"],
|
||||
"🦹": ["bad", "criminal", "evil", "superpower", "supervillain", "villain"],
|
||||
"🦹♀️": ["woman", "female", "evil", "bad", "criminal", "heroine", "superpowers"],
|
||||
"🦹♂️": ["man", "male", "evil", "bad", "criminal", "hero", "superpowers"],
|
||||
"🤶": ["woman", "female", "xmas", "mother christmas"],
|
||||
"🧑🎄": ["xmas", "christmas"],
|
||||
"🎅": ["festival", "man", "male", "xmas", "father christmas"],
|
||||
"🥷": [],
|
||||
"🥷": ["assassin", "fight", "fighter", "hidden", "ninja", "person", "secret", "skills", "sly", "soldier", "stealth", "war"],
|
||||
"🧙": ["fantasy", "mage", "magic", "play", "sorcerer", "sorceress", "sorcery", "spell", "summon", "witch", "wizard"],
|
||||
"🧙♀️": ["woman", "female", "mage", "witch"],
|
||||
"🧙♂️": ["man", "male", "mage", "sorcerer"],
|
||||
"🧝": ["elf", "elves", "enchantment", "fantasy", "folklore", "magic", "magical", "myth"],
|
||||
"🧝♀️": ["woman", "female"],
|
||||
"🧝♂️": ["man", "male"],
|
||||
"🧛": ["blood", "dracula", "fangs", "halloween", "scary", "supernatural", "teeth", "undead", "vampire"],
|
||||
"🧛♀️": ["woman", "female"],
|
||||
"🧛♂️": ["man", "male", "dracula"],
|
||||
"🧟": ["apocalypse", "dead", "halloween", "horror", "scary", "undead", "walking", "zombie"],
|
||||
"🧟♀️": ["woman", "female", "undead", "walking dead"],
|
||||
"🧟♂️": ["man", "male", "dracula", "undead", "walking dead"],
|
||||
"🧞": ["djinn", "fantasy", "genie", "jinn", "lamp", "myth", "rub", "wishes"],
|
||||
"🧞♀️": ["woman", "female"],
|
||||
"🧞♂️": ["man", "male"],
|
||||
"🧜": ["creature", "fairytale", "folklore", "merperson", "ocean", "sea", "siren", "trident"],
|
||||
"🧜♀️": ["woman", "female", "merwoman", "ariel"],
|
||||
"🧜♂️": ["man", "male", "triton"],
|
||||
"🧚": ["fairy", "fairytale", "fantasy", "myth", "person", "pixie", "tale", "wings"],
|
||||
"🧚♀️": ["woman", "female"],
|
||||
"🧚♂️": ["man", "male"],
|
||||
"👼": ["heaven", "wings", "halo"],
|
||||
"🧌": [],
|
||||
"🧌": ["fairy", "fantasy", "monster", "tale", "troll", "trolling"],
|
||||
"🤰": ["baby"],
|
||||
"🫃": [],
|
||||
"🫄": [],
|
||||
"🫅": [],
|
||||
"🫃": ["belly", "bloated", "full", "man", "overeat", "pregnant"],
|
||||
"🫄": ["belly", "bloated", "full", "overeat", "person", "pregnant", "stuffed"],
|
||||
"🫅": ["crown", "monarch", "noble", "person", "regal", "royal", "royalty"],
|
||||
"🤱": ["nursing", "baby"],
|
||||
"👩🍼": [],
|
||||
"👨🍼": [],
|
||||
"🧑🍼": [],
|
||||
"👩🍼": ["baby", "feed", "feeding", "mom", "mother", "nanny", "newborn", "nursing", "woman"],
|
||||
"👨🍼": ["baby", "dad", "father", "feed", "feeding", "man", "nanny", "newborn", "nursing"],
|
||||
"🧑🍼": ["baby", "feed", "feeding", "nanny", "newborn", "nursing", "parent"],
|
||||
"👸": ["girl", "woman", "female", "blond", "crown", "royal", "queen"],
|
||||
"🤴": ["boy", "man", "male", "crown", "royal", "king"],
|
||||
"👰": ["couple", "marriage", "wedding", "woman", "bride"],
|
||||
"👰": ["couple", "marriage", "wedding", "woman", "bride"],
|
||||
"🤵": ["couple", "marriage", "wedding", "groom"],
|
||||
"👰♀️": ["bride", "veil", "wedding", "woman"],
|
||||
"👰♂️": ["man", "veil", "wedding"],
|
||||
"🤵": ["couple", "marriage", "wedding", "groom"],
|
||||
"🤵♀️": ["formal", "tuxedo", "wedding", "woman"],
|
||||
"🤵♂️": ["formal", "groom", "man", "tuxedo", "wedding"],
|
||||
"🏃➡️": ["facing", "fast", "hurry", "marathon", "move", "person", "quick", "race", "racing", "right", "run", "rush", "speed"],
|
||||
"🏃♀️": ["woman", "walking", "exercise", "race", "running", "female"],
|
||||
"🏃": ["man", "walking", "exercise", "race", "running"],
|
||||
"🏃♀️➡️": ["facing", "fast", "hurry", "marathon", "move", "quick", "race", "racing", "right", "run", "rush", "speed", "woman"],
|
||||
"🏃♂️": ["fast", "hurry", "man", "marathon", "move", "quick", "race", "racing", "run", "rush", "speed"],
|
||||
"🏃♂️➡️": ["facing", "fast", "hurry", "man", "marathon", "move", "quick", "race", "racing", "right", "run", "rush", "speed"],
|
||||
"🚶➡️": ["amble", "facing", "gait", "hike", "man", "pace", "pedestrian", "person", "right", "stride", "stroll", "walk", "walking"],
|
||||
"🚶♀️": ["human", "feet", "steps", "woman", "female"],
|
||||
"🚶": ["human", "feet", "steps"],
|
||||
"🚶♀️➡️": ["amble", "facing", "gait", "hike", "man", "pace", "pedestrian", "right", "stride", "stroll", "walk", "walking", "woman"],
|
||||
"🚶♂️": ["amble", "gait", "hike", "man", "pace", "pedestrian", "stride", "stroll", "walk", "walking"],
|
||||
"🚶♂️➡️": ["amble", "facing", "gait", "hike", "man", "pace", "pedestrian", "right", "stride", "stroll", "walk", "walking"],
|
||||
"💃": ["female", "girl", "woman", "fun"],
|
||||
"🕺": ["male", "boy", "fun", "dancer"],
|
||||
"👯": ["female", "bunny", "women", "girls"],
|
||||
"👯♀️": ["bestie", "bff", "bunny", "counterpart", "dancer", "double", "ear", "identical", "pair", "party", "partying", "people", "soulmate", "twin", "twinsies", "women"],
|
||||
"👯♂️": ["male", "bunny", "men", "boys"],
|
||||
"👫": ["pair", "people", "human", "love", "date", "dating", "like", "affection", "valentines", "marriage"],
|
||||
"🧑🤝🧑": ["pair", "couple", "love", "like", "bromance", "friendship", "people", "human"],
|
||||
"👬": ["pair", "couple", "love", "like", "bromance", "friendship", "people", "man", "human"],
|
||||
"👭": ["pair", "couple", "love", "like", "bromance", "friendship", "people", "female", "human"],
|
||||
"🫂": [],
|
||||
"🫂": ["comfort", "embrace", "farewell", "friendship", "goodbye", "hello", "hug", "hugging", "love", "people", "thanks"],
|
||||
"🙇♀️": ["woman", "female", "girl"],
|
||||
"🙇": ["man", "male", "boy"],
|
||||
"🙇♂️": ["apology", "ask", "beg", "bow", "bowing", "favor", "forgive", "gesture", "man", "meditate", "meditation", "pity", "regret", "sorry"],
|
||||
"🤦": ["again", "bewilder", "disbelief", "exasperation", "facepalm", "no", "not", "oh", "omg", "person", "shock", "smh"],
|
||||
"🤦♂️": ["man", "male", "boy", "disbelief"],
|
||||
"🤦♀️": ["woman", "female", "girl", "disbelief"],
|
||||
"🤷": ["woman", "female", "girl", "confused", "indifferent", "doubt"],
|
||||
"🤷♀️": ["doubt", "dunno", "guess", "idk", "ignorance", "indifference", "knows", "maybe", "shrug", "shrugging", "whatever", "who", "woman"],
|
||||
"🤷♂️": ["man", "male", "boy", "confused", "indifferent", "doubt"],
|
||||
"💁": ["female", "girl", "woman", "human", "information"],
|
||||
"💁♀️": ["fetch", "flick", "flip", "gossip", "hand", "sarcasm", "sarcastic", "sassy", "seriously", "tipping", "whatever", "woman"],
|
||||
"💁♂️": ["male", "boy", "man", "human", "information"],
|
||||
"🙅": ["female", "girl", "woman", "nope"],
|
||||
"🙅♀️": ["forbidden", "gesture", "hand", "no", "not", "prohibit", "woman"],
|
||||
"🙅♂️": ["male", "boy", "man", "nope"],
|
||||
"🙆": ["women", "girl", "female", "pink", "human", "woman"],
|
||||
"🙆♀️": ["exercise", "gesture", "gesturing", "hand", "ok", "omg", "woman"],
|
||||
"🙆♂️": ["men", "boy", "male", "blue", "human", "man"],
|
||||
"🙋": ["female", "girl", "woman"],
|
||||
"🙋♀️": ["gesture", "hand", "here", "know", "me", "pick", "question", "raise", "raising", "woman"],
|
||||
"🙋♂️": ["male", "boy", "man"],
|
||||
"🙎": ["female", "girl", "woman"],
|
||||
"🙎♀️": ["disappointed", "downtrodden", "frown", "grimace", "pouting", "scowl", "sulk", "upset", "whine", "woman"],
|
||||
"🙎♂️": ["male", "boy", "man"],
|
||||
"🙍": ["female", "girl", "woman", "sad", "depressed", "discouraged", "unhappy"],
|
||||
"🙍♀️": ["annoyed", "disappointed", "disgruntled", "disturbed", "frown", "frowning", "frustrated", "gesture", "irritated", "upset", "woman"],
|
||||
"🙍♂️": ["male", "boy", "man", "sad", "depressed", "discouraged", "unhappy"],
|
||||
"💇": ["female", "girl", "woman"],
|
||||
"💇♀️": ["barber", "beauty", "chop", "cosmetology", "cut", "groom", "hair", "haircut", "parlor", "person", "shears", "style", "woman"],
|
||||
"💇♂️": ["male", "boy", "man"],
|
||||
"💆": ["female", "girl", "woman", "head"],
|
||||
"💆♀️": ["face", "getting", "headache", "massage", "relax", "relaxing", "salon", "soothe", "spa", "tension", "therapy", "treatment", "woman"],
|
||||
"💆♂️": ["male", "boy", "man", "head"],
|
||||
"🧖": ["day", "luxurious", "pamper", "person", "relax", "room", "sauna", "spa", "steam", "steambath", "unwind"],
|
||||
"🧖♀️": ["female", "woman", "spa", "steamroom", "sauna"],
|
||||
"🧖♂️": ["male", "man", "spa", "steamroom", "sauna"],
|
||||
"🧏": ["accessibility", "deaf", "ear", "gesture", "hear", "person"],
|
||||
"🧏♀️": ["woman", "female"],
|
||||
"🧏♂️": ["man", "male"],
|
||||
"🧍": ["person", "stand", "standing"],
|
||||
"🧍♀️": ["woman", "female"],
|
||||
"🧍♂️": ["man", "male"],
|
||||
"🧎": ["kneel", "kneeling", "knees", "person"],
|
||||
"🧎➡️": ["facing", "kneel", "kneeling", "knees", "person", "right"],
|
||||
"🧎♀️": ["woman", "female"],
|
||||
"🧎♀️➡️": ["facing", "kneel", "kneeling", "knees", "right", "woman"],
|
||||
"🧎♂️": ["man", "male"],
|
||||
"🧎♂️➡️": ["facing", "kneel", "kneeling", "knees", "man", "right"],
|
||||
"🧑🦯": ["accessibility", "blind"],
|
||||
"🧑🦯➡️": ["accessibility", "blind", "cane", "facing", "person", "probing", "right", "white"],
|
||||
"👩🦯": ["woman", "female", "accessibility", "blind"],
|
||||
"👩🦯➡️": ["accessibility", "blind", "cane", "facing", "probing", "right", "white", "woman"],
|
||||
"👨🦯": ["man", "male", "accessibility", "blind"],
|
||||
"👨🦯➡️": ["accessibility", "blind", "cane", "facing", "man", "probing", "right", "white"],
|
||||
"🧑🦼": ["accessibility"],
|
||||
"🧑🦼➡️": ["accessibility", "facing", "motorized", "person", "right", "wheelchair"],
|
||||
"👩🦼": ["woman", "female", "accessibility"],
|
||||
"👩🦼➡️": ["accessibility", "facing", "motorized", "right", "wheelchair", "woman"],
|
||||
"👨🦼": ["man", "male", "accessibility"],
|
||||
"👨🦼➡️": ["accessibility", "facing", "man", "motorized", "right", "wheelchair"],
|
||||
"🧑🦽": ["accessibility"],
|
||||
"🧑🦽➡️": ["accessibility", "facing", "manual", "person", "right", "wheelchair"],
|
||||
"👩🦽": ["woman", "female", "accessibility"],
|
||||
"👩🦽➡️": ["accessibility", "facing", "manual", "right", "wheelchair", "woman"],
|
||||
"👨🦽": ["man", "male", "accessibility"],
|
||||
"👨🦽➡️": ["accessibility", "facing", "man", "manual", "right", "wheelchair"],
|
||||
"💑": ["pair", "love", "like", "affection", "human", "dating", "valentines", "marriage"],
|
||||
"👩❤️👨": ["anniversary", "babe", "bae", "couple", "dating", "heart", "kiss", "love", "man", "person", "relationship", "romance", "together", "woman", "you"],
|
||||
"👩❤️👩": ["pair", "love", "like", "affection", "human", "dating", "valentines", "marriage"],
|
||||
"👨❤️👨": ["pair", "love", "like", "affection", "human", "dating", "valentines", "marriage"],
|
||||
"💏": ["pair", "valentines", "love", "like", "dating", "marriage"],
|
||||
"👩❤️💋👨": ["anniversary", "babe", "bae", "couple", "date", "dating", "heart", "kiss", "love", "man", "mwah", "person", "romance", "together", "woman", "xoxo"],
|
||||
"👩❤️💋👩": ["pair", "valentines", "love", "like", "dating", "marriage"],
|
||||
"👨❤️💋👨": ["pair", "valentines", "love", "like", "dating", "marriage"],
|
||||
"👨👩👦": ["boy", "child", "family", "man", "woman"],
|
||||
"👪": ["home", "parents", "child", "mom", "dad", "father", "mother", "people", "human"],
|
||||
"👨👩👧": ["home", "parents", "people", "human", "child"],
|
||||
"👨👩👧👦": ["home", "parents", "people", "human", "children"],
|
||||
@@ -391,6 +454,10 @@
|
||||
"👨👧👦": ["home", "parent", "people", "human", "children"],
|
||||
"👨👦👦": ["home", "parent", "people", "human", "children"],
|
||||
"👨👧👧": ["home", "parent", "people", "human", "children"],
|
||||
"🧑🧑🧒": ["adult", "child", "family"],
|
||||
"🧑🧑🧒🧒": ["adult", "child", "family"],
|
||||
"🧑🧒": ["adult", "child", "family"],
|
||||
"🧑🧒🧒": ["adult", "child", "family"],
|
||||
"🧶": ["ball", "crochet", "knit"],
|
||||
"🧵": ["needle", "sewing", "spool", "string"],
|
||||
"🧥": ["jacket"],
|
||||
@@ -415,7 +482,7 @@
|
||||
"👢": ["shoes", "fashion"],
|
||||
"👞": ["fashion", "male"],
|
||||
"👟": ["shoes", "sports", "sneakers"],
|
||||
"🩴": [],
|
||||
"🩴": ["beach", "flip", "flop", "sandal", "sandals", "shoe", "thong", "thongs", "zōri"],
|
||||
"🩰": ["shoes", "sports"],
|
||||
"🧦": ["stockings", "clothes"],
|
||||
"🧤": ["hands", "winter", "clothes"],
|
||||
@@ -424,7 +491,7 @@
|
||||
"🎩": ["magic", "gentleman", "classy", "circus"],
|
||||
"🧢": ["cap", "baseball"],
|
||||
"⛑": ["construction", "build"],
|
||||
"🪖": [],
|
||||
"🪖": ["army", "helmet", "military", "soldier", "war", "warrior"],
|
||||
"🎓": ["school", "college", "degree", "university", "graduation", "cap", "hat", "legal", "learn", "education"],
|
||||
"👑": ["king", "kod", "leader", "royalty", "lord"],
|
||||
"🎒": ["student", "education", "bag", "backpack"],
|
||||
@@ -475,6 +542,7 @@
|
||||
"🐦⬛": ["animal", "nature", "bird", "black", "crow", "raven", "rook"],
|
||||
"🦅": ["animal", "nature", "bird"],
|
||||
"🦉": ["animal", "nature", "bird", "hoot"],
|
||||
"🐦🔥": ["animal", "nature", "ascend", "ascension", "emerge", "fantasy", "firebird", "glory", "immortal", "phoenix", "rebirth", "reincarnation", "reinvent", "renewal", "revival", "revive", "rise", "transform"],
|
||||
"🦇": ["animal", "nature", "blind", "vampire"],
|
||||
"🐺": ["animal", "nature", "wild"],
|
||||
"🐗": ["animal", "nature"],
|
||||
@@ -575,6 +643,7 @@
|
||||
"🌿": ["vegetable", "plant", "medicine", "weed", "grass", "lawn"],
|
||||
"☘": ["vegetable", "plant", "nature", "irish", "clover"],
|
||||
"🍀": ["vegetable", "plant", "nature", "lucky", "irish"],
|
||||
"": ["plant", "nature", "bare", "barren", "branches", "dead", "drought", "leafless", "tree", "trunk", "winter", "wood"],
|
||||
"🎍": ["plant", "nature", "vegetable", "panda", "pine_decoration"],
|
||||
"🎋": ["plant", "nature", "branch", "summer"],
|
||||
"🍃": ["nature", "plant", "tree", "vegetable", "grass", "lawn", "spring"],
|
||||
@@ -644,10 +713,11 @@
|
||||
"💧": ["water", "drip", "faucet", "spring"],
|
||||
"💦": ["water", "drip", "oops"],
|
||||
"🌊": ["sea", "water", "wave", "nature", "tsunami", "disaster"],
|
||||
"🪷": [],
|
||||
"🪸": [],
|
||||
"🪹": [],
|
||||
"🪺": [],
|
||||
"🪷": ["plant", "nature", "beauty", "buddhism", "calm", "flower", "hinduism", "lotus", "peace", "purity", "serenity"],
|
||||
"🪸": ["animal", "nature", "change", "climate", "coral", "ocean", "reef", "sea"],
|
||||
"🪹": ["plant", "nature", "branch", "empty", "home", "nest", "nesting"],
|
||||
"🪺": ["plant", "nature", "bird", "branch", "egg", "eggs", "nest", "nesting"],
|
||||
"🍋🟩": ["acidity", "citrus", "cocktail", "fruit", "garnish", "key", "lime", "margarita", "mojito", "refreshing", "salsa", "sour", "tangy", "tequila", "tropical", "zest"],
|
||||
"🍏": ["fruit", "nature"],
|
||||
"🍎": ["fruit", "mac", "school"],
|
||||
"🍐": ["fruit", "nature", "food"],
|
||||
@@ -667,6 +737,8 @@
|
||||
"🥑": ["fruit", "food"],
|
||||
"🫛": ["beans", "edamame", "legume", "pea", "pod", "vegetable", "food"],
|
||||
"🥦": ["fruit", "food", "vegetable"],
|
||||
"🍄🟫": ["food", "fungi", "fungus", "mushroom", "nature", "pizza", "portobello", "shiitake", "shroom", "spore", "sprout", "toppings", "truffle", "vegetable", "vegetarian", "veggie"],
|
||||
"": ["beet", "food", "garden", "radish", "root", "salad", "turnip", "vegetable", "vegetarian"],
|
||||
"🍅": ["fruit", "vegetable", "nature", "food"],
|
||||
"🍆": ["vegetable", "nature", "food", "aubergine"],
|
||||
"🥒": ["fruit", "food", "pickle"],
|
||||
@@ -759,7 +831,7 @@
|
||||
"🍵": ["drink", "bowl", "breakfast", "green", "british"],
|
||||
"🥤": ["drink", "soda"],
|
||||
"☕": ["beverage", "caffeine", "latte", "espresso"],
|
||||
"🫖": [],
|
||||
"🫖": ["brew", "drink", "food", "pot", "tea", "teapot"],
|
||||
"🧋": ["tapioca"],
|
||||
"🍼": ["food", "container", "milk"],
|
||||
"🧃": ["food", "drink"],
|
||||
@@ -772,9 +844,9 @@
|
||||
"🥣": ["food", "breakfast", "cereal", "oatmeal", "porridge"],
|
||||
"🥡": ["food", "leftovers"],
|
||||
"🥢": ["food"],
|
||||
"🫗": [],
|
||||
"🫘": [],
|
||||
"🫙": [],
|
||||
"🫗": ["accident", "drink", "empty", "glass", "liquid", "oops", "pour", "pouring", "spill", "water"],
|
||||
"🫘": ["beans", "food", "kidney", "legume", "small"],
|
||||
"🫙": ["condiment", "container", "empty", "jar", "nothing", "sauce", "store"],
|
||||
"⚽": ["sports", "football"],
|
||||
"🏀": ["sports", "balls", "NBA"],
|
||||
"🏈": ["sports", "balls", "NFL"],
|
||||
@@ -788,6 +860,7 @@
|
||||
"⛳": ["sports", "business", "flag", "hole", "summer"],
|
||||
"🏌️♀️": ["sports", "business", "woman", "female"],
|
||||
"🏌": ["sports", "business"],
|
||||
"🏌️♂️": ["sport", "ball", "birdie", "caddy", "driving", "golf", "golfing", "green", "man", "pga", "putt", "range", "tee"],
|
||||
"🏓": ["sports", "pingpong"],
|
||||
"🏸": ["sports"],
|
||||
"🥅": ["sports"],
|
||||
@@ -799,10 +872,13 @@
|
||||
"⛷": ["sports", "winter", "snow"],
|
||||
"🏂": ["sports", "winter"],
|
||||
"🤺": ["sports", "fencing", "sword"],
|
||||
"🤼": ["sport", "combat", "duel", "grapple", "people", "ring", "tournament", "wrestle", "wrestling"],
|
||||
"🤼♀️": ["sports", "wrestlers"],
|
||||
"🤼♂️": ["sports", "wrestlers"],
|
||||
"🤸": ["sport", "active", "cartwheel", "cartwheeling", "excited", "flip", "gymnastics", "happy", "person", "somersault"],
|
||||
"🤸♀️": ["gymnastics"],
|
||||
"🤸♂️": ["gymnastics"],
|
||||
"🤾": ["sport", "athletics", "ball", "catch", "chuck", "handball", "hurl", "lob", "person", "pitch", "playing", "throw", "toss"],
|
||||
"🤾♀️": ["sports"],
|
||||
"🤾♂️": ["sports"],
|
||||
"⛸": ["sports"],
|
||||
@@ -815,32 +891,42 @@
|
||||
"🥋": ["judo", "karate", "taekwondo"],
|
||||
"🚣♀️": ["sports", "hobby", "water", "ship", "woman", "female"],
|
||||
"🚣": ["sports", "hobby", "water", "ship"],
|
||||
"🚣♂️": ["sport", "boat", "canoe", "cruise", "fishing", "lake", "man", "oar", "paddle", "raft", "river", "row", "rowboat", "rowing"],
|
||||
"🧗": ["climb", "climber", "climbing", "mountain", "person", "rock", "scale", "up"],
|
||||
"🧗♀️": ["sports", "hobby", "woman", "female", "rock"],
|
||||
"🧗♂️": ["sports", "hobby", "man", "male", "rock"],
|
||||
"🏊♀️": ["sports", "exercise", "human", "athlete", "water", "summer", "woman", "female"],
|
||||
"🏊": ["sports", "exercise", "human", "athlete", "water", "summer"],
|
||||
"🏊♂️": ["sport", "freestyle", "man", "swim", "swimmer", "swimming", "triathlon"],
|
||||
"🤽": ["sport", "person", "playing", "polo", "swimming", "water", "waterpolo"],
|
||||
"🤽♀️": ["sports", "pool"],
|
||||
"🤽♂️": ["sports", "pool"],
|
||||
"🧘": ["cross", "legged", "legs", "lotus", "meditation", "peace", "person", "position", "relax", "serenity", "yoga", "yogi", "zen"],
|
||||
"🧘♀️": ["woman", "female", "meditation", "yoga", "serenity", "zen", "mindfulness"],
|
||||
"🧘♂️": ["man", "male", "meditation", "yoga", "serenity", "zen", "mindfulness"],
|
||||
"🏄♀️": ["sports", "ocean", "sea", "summer", "beach", "woman", "female"],
|
||||
"🏄": ["sports", "ocean", "sea", "summer", "beach"],
|
||||
"🏄♂️": ["sport", "beach", "man", "ocean", "surf", "surfer", "surfing", "swell", "waves"],
|
||||
"🛀": ["clean", "shower", "bathroom"],
|
||||
"⛹️♀️": ["sports", "human", "woman", "female"],
|
||||
"⛹": ["sports", "human"],
|
||||
"⛹️♂️": ["sport", "athletic", "ball", "basketball", "bouncing", "championship", "dribble", "man", "net", "player", "throw"],
|
||||
"🏋️♀️": ["sports", "training", "exercise", "woman", "female"],
|
||||
"🏋": ["sports", "training", "exercise"],
|
||||
"🏋️♂️": ["sport", "barbell", "bodybuilder", "deadlift", "lifter", "lifting", "man", "powerlifting", "weight", "weightlifter", "weights", "workout"],
|
||||
"🚴♀️": ["sports", "bike", "exercise", "hipster", "woman", "female"],
|
||||
"🚴": ["sports", "bike", "exercise", "hipster"],
|
||||
"🚴♂️": ["sport", "bicycle", "bicyclist", "bike", "biking", "cycle", "cyclist", "man", "riding"],
|
||||
"🚵♀️": ["transportation", "sports", "human", "race", "bike", "woman", "female"],
|
||||
"🚵": ["transportation", "sports", "human", "race", "bike"],
|
||||
"🚵♂️": ["sport", "bicycle", "bicyclist", "bike", "biking", "cycle", "cyclist", "man", "mountain", "riding"],
|
||||
"🏇": ["animal", "betting", "competition", "gambling", "luck"],
|
||||
"🤿": ["sports"],
|
||||
"🪀": ["sports"],
|
||||
"🪁": ["sports"],
|
||||
"🦺": ["sports"],
|
||||
"🪡": [],
|
||||
"🪢": [],
|
||||
"🪡": ["embroidery", "needle", "sew", "sewing", "stitches", "sutures", "tailoring", "thread"],
|
||||
"🪢": ["cord", "knot", "rope", "tangled", "tie", "twine", "twist"],
|
||||
"🕴": ["suit", "business", "levitate", "hover", "jump"],
|
||||
"🏆": ["win", "award", "contest", "place", "ftw", "ceremony"],
|
||||
"🎽": ["play", "pageant"],
|
||||
@@ -856,6 +942,7 @@
|
||||
"🎭": ["acting", "theater", "drama"],
|
||||
"🎨": ["design", "paint", "draw", "colors"],
|
||||
"🎪": ["festival", "carnival", "party"],
|
||||
"🤹": ["sport", "act", "balance", "balancing", "handle", "juggle", "juggling", "manage", "multitask", "person", "skill"],
|
||||
"🤹♀️": ["juggle", "balance", "skill", "multitask"],
|
||||
"🤹♂️": ["juggle", "balance", "skill", "multitask"],
|
||||
"🎤": ["sound", "music", "PA", "sing", "talkshow"],
|
||||
@@ -872,6 +959,7 @@
|
||||
"🪕": ["music", "instrument"],
|
||||
"🪗": ["music", "instrument"],
|
||||
"🪘": ["music", "instrument"],
|
||||
"": ["cupid", "harp", "instrument", "love", "music", "orchestra"],
|
||||
"🎬": ["movie", "film", "record"],
|
||||
"🎮": ["play", "console", "PS4", "controller"],
|
||||
"👾": ["game", "arcade", "play"],
|
||||
@@ -881,11 +969,11 @@
|
||||
"🎰": ["bet", "gamble", "vegas", "fruit machine", "luck", "casino"],
|
||||
"🧩": ["interlocking", "puzzle", "piece"],
|
||||
"🎳": ["sports", "fun", "play"],
|
||||
"🪄": [],
|
||||
"🪅": [],
|
||||
"🪆": [],
|
||||
"🪬": [],
|
||||
"🪩": [],
|
||||
"🪄": ["magic", "magician", "wand", "witch", "wizard"],
|
||||
"🪅": ["candy", "celebrate", "celebration", "cinco", "de", "festive", "mayo", "party", "pinada", "pinata", "piñata"],
|
||||
"🪆": ["babooshka", "baboushka", "babushka", "doll", "dolls", "matryoshka", "nesting", "russia"],
|
||||
"🪬": ["amulet", "fatima", "fortune", "guide", "hamsa", "hand", "mary", "miriam", "palm", "protect", "protection"],
|
||||
"🪩": ["ball", "dance", "disco", "glitter", "mirror", "party"],
|
||||
"🚗": ["red", "transportation", "vehicle"],
|
||||
"🚕": ["uber", "vehicle", "cars", "transportation"],
|
||||
"🚙": ["transportation", "vehicle"],
|
||||
@@ -941,7 +1029,7 @@
|
||||
"🚀": ["launch", "ship", "staffmode", "NASA", "outer space", "outer_space", "fly"],
|
||||
"🛰": ["communication", "gps", "orbit", "spaceflight", "NASA", "ISS"],
|
||||
"🛻": ["car"],
|
||||
"🛼": [],
|
||||
"🛼": ["blades", "roller", "skate", "skates", "sport"],
|
||||
"💺": ["sit", "airplane", "transport", "bus", "flight", "fly"],
|
||||
"🛶": ["boat", "paddle", "water", "ship"],
|
||||
"⚓": ["ship", "ferry", "sea", "boat"],
|
||||
@@ -1013,12 +1101,12 @@
|
||||
"🕋": ["mecca", "mosque", "islam"],
|
||||
"⛩": ["temple", "japan", "kyoto"],
|
||||
"🛕": ["temple"],
|
||||
"🪨": [],
|
||||
"🪵": [],
|
||||
"🛖": [],
|
||||
"🛝": [],
|
||||
"🛞": [],
|
||||
"🛟": [],
|
||||
"🪨": ["boulder", "heavy", "rock", "solid", "stone", "tough"],
|
||||
"🪵": ["log", "lumber", "timber", "wood"],
|
||||
"🛖": ["home", "house", "hut", "roundhouse", "shelter", "yurt"],
|
||||
"🛝": ["amusement", "park", "play", "playground", "playing", "slide", "sliding", "theme"],
|
||||
"🛞": ["car", "circle", "tire", "turn", "vehicle", "wheel"],
|
||||
"🛟": ["buoy", "float", "life", "lifesaver", "preserver", "rescue", "ring", "safety", "save", "saver", "swim"],
|
||||
"⌚": ["time", "accessories"],
|
||||
"📱": ["technology", "apple", "gadgets", "dial"],
|
||||
"📲": ["iphone", "incoming"],
|
||||
@@ -1059,7 +1147,7 @@
|
||||
"⌛": ["time", "clock", "oldschool", "limit", "exam", "quiz", "test"],
|
||||
"📡": ["communication", "future", "radio", "space"],
|
||||
"🔋": ["power", "energy", "sustain"],
|
||||
"🪫": [],
|
||||
"🪫": ["battery", "drained", "electronic", "energy", "low", "power"],
|
||||
"🔌": ["charger", "power"],
|
||||
"💡": ["light", "electricity", "idea"],
|
||||
"🔦": ["dark", "camping", "sight", "night"],
|
||||
@@ -1075,9 +1163,11 @@
|
||||
"💰": ["dollar", "payment", "coins", "sale"],
|
||||
"🪙": ["dollar", "payment", "coins", "sale"],
|
||||
"💳": ["money", "sales", "dollar", "bill", "payment", "shopping"],
|
||||
"🪪": [],
|
||||
"🪪": ["card", "credentials", "document", "id", "identification", "license", "security"],
|
||||
"🥾": ["backpacking", "boot", "brown", "camping", "hiking", "outdoors", "shoe"],
|
||||
"💎": ["blue", "ruby", "diamond", "jewelry"],
|
||||
"⚖": ["law", "fairness", "weight"],
|
||||
"⛓️💥": ["break", "breaking", "broken", "chain", "cuffs", "freedom"],
|
||||
"🧰": ["tools", "diy", "fix", "maintainer", "mechanic"],
|
||||
"🔧": ["tools", "diy", "ikea", "fix", "maintainer"],
|
||||
"🔨": ["tools", "build", "create"],
|
||||
@@ -1093,6 +1183,7 @@
|
||||
"🪛": ["tool"],
|
||||
"🪝": ["tool"],
|
||||
"🪜": ["tool"],
|
||||
"": ["bury", "dig", "garden", "hole", "plant", "scoop", "shovel", "snow", "spade"],
|
||||
"🧱": ["bricks"],
|
||||
"⛓": ["lock", "arrest"],
|
||||
"🧲": ["attraction", "magnetic"],
|
||||
@@ -1123,8 +1214,8 @@
|
||||
"🩺": ["health", "hospital", "medicine", "needle", "doctor", "nurse"],
|
||||
"🪒": ["health"],
|
||||
"🪮": ["afro", "comb", "hair", "pick"],
|
||||
"🩻": [],
|
||||
"🩼": [],
|
||||
"🩻": ["bones", "doctor", "medical", "skeleton", "skull", "x-ray", "xray"],
|
||||
"🩼": ["aid", "cane", "crutch", "disability", "help", "hurt", "injured", "mobility", "stick"],
|
||||
"🧬": ["biologist", "genetics", "life"],
|
||||
"🧫": ["bacteria", "biology", "culture", "lab"],
|
||||
"🧪": ["chemistry", "experiment", "lab", "science"],
|
||||
@@ -1159,7 +1250,7 @@
|
||||
"🪤": ["household"],
|
||||
"🪣": ["household"],
|
||||
"🪥": ["household"],
|
||||
"🫧": [],
|
||||
"🫧": ["bubble", "bubbles", "burp", "clean", "floating", "pearl", "soap", "underwater"],
|
||||
"⛱": ["weather", "summer"],
|
||||
"🗿": ["rock", "easter island", "moai"],
|
||||
"🛍": ["mall", "buy", "purchase"],
|
||||
@@ -1249,8 +1340,8 @@
|
||||
"🖌": ["drawing", "creativity", "art"],
|
||||
"🔍": ["search", "zoom", "find", "detective"],
|
||||
"🔎": ["search", "zoom", "find", "detective"],
|
||||
"🪦": [],
|
||||
"🪧": [],
|
||||
"🪦": ["cemetery", "dead", "grave", "graveyard", "headstone", "memorial", "rip", "tomb", "tombstone"],
|
||||
"🪧": ["card", "demonstration", "notice", "picket", "placard", "plaque", "protest", "sign"],
|
||||
"💯": ["score", "perfect", "numbers", "century", "exam", "quiz", "test", "pass", "hundred"],
|
||||
"🔢": ["numbers", "blue-square"],
|
||||
"🩷": ["love", "like", "affection", "valentines"],
|
||||
@@ -1275,8 +1366,8 @@
|
||||
"💘": ["love", "like", "heart", "affection", "valentines"],
|
||||
"💝": ["love", "valentines"],
|
||||
"💟": ["purple-square", "love", "like"],
|
||||
"❤️🔥": [],
|
||||
"❤️🩹": [],
|
||||
"❤️🔥": ["burn", "fire", "heart", "love", "lust", "sacred"],
|
||||
"❤️🩹": ["healthier", "heart", "improving", "mending", "recovering", "recuperating", "well"],
|
||||
"☮": ["hippie"],
|
||||
"✝": ["christianity"],
|
||||
"☪": ["islam"],
|
||||
@@ -1304,6 +1395,8 @@
|
||||
"♓": ["purple-square", "sign", "zodiac", "astrology"],
|
||||
"🆔": ["purple-square", "words"],
|
||||
"⚛": ["science", "physics", "chemistry"],
|
||||
"♀️": ["female", "sign", "woman", "pink-square"],
|
||||
"♂️": ["male", "man", "sign", "blue-square"],
|
||||
"⚧️": ["purple-square", "woman", "female", "toilet", "loo", "restroom", "gender"],
|
||||
"🈳": ["kanji", "japanese", "chinese", "empty", "sky", "blue-square", "aki"],
|
||||
"🈹": ["cut", "divide", "chinese", "kanji", "pink-square", "waribiki"],
|
||||
@@ -1459,13 +1552,15 @@
|
||||
"➖": ["math", "calculation", "subtract", "less"],
|
||||
"➗": ["divide", "math", "calculation"],
|
||||
"✖️": ["math", "calculation"],
|
||||
"🟰": [],
|
||||
"🟰": ["answer", "equal", "equality", "equals", "heavy", "math", "sign"],
|
||||
"♾": ["forever"],
|
||||
"💲": ["money", "sales", "payment", "currency", "buck"],
|
||||
"💱": ["money", "sales", "dollar", "travel"],
|
||||
"⚕️": ["aesculapius", "medical", "medicine", "staff", "symbol"],
|
||||
"©️": ["ip", "license", "circle", "law", "legal"],
|
||||
"®️": ["alphabet", "circle"],
|
||||
"™️": ["trademark", "brand", "law", "legal"],
|
||||
"": ["drip", "holi", "ink", "liquid", "mess", "paint", "spill", "splatter", "stain"],
|
||||
"🔚": ["words", "arrow"],
|
||||
"🔙": ["arrow", "words", "return"],
|
||||
"🔛": ["arrow", "words"],
|
||||
@@ -1576,6 +1671,7 @@
|
||||
"🇧🇲": ["bm", "bermuda", "flag", "nation", "country", "banner"],
|
||||
"🇧🇹": ["bt", "bhutan", "flag", "nation", "country", "banner"],
|
||||
"🇧🇴": ["bo", "bolivia", "flag", "nation", "country", "banner"],
|
||||
"🇧🇻": ["bv", "bouvet", "island", "flag", "nation", "country", "banner"],
|
||||
"🇧🇶": ["bq", "bonaire", "flag", "nation", "country", "banner"],
|
||||
"🇧🇦": ["ba", "bosnia", "herzegovina", "flag", "nation", "country", "banner"],
|
||||
"🇧🇼": ["bw", "botswana", "flag", "nation", "country", "banner"],
|
||||
@@ -1593,10 +1689,12 @@
|
||||
"🇮🇨": ["ic", "canary", "islands", "flag", "nation", "country", "banner"],
|
||||
"🇰🇾": ["ky", "cayman", "islands", "flag", "nation", "country", "banner"],
|
||||
"🇨🇫": ["cf", "central", "african", "republic", "flag", "nation", "country", "banner"],
|
||||
"🇪🇦": ["ea", "ceuta", "melilla", "flag", "nation", "country", "banner"],
|
||||
"🇹🇩": ["td", "chad", "flag", "nation", "country", "banner"],
|
||||
"🇨🇱": ["cl", "chile", "flag", "nation", "country", "banner"],
|
||||
"🇨🇳": ["cn", "china", "chinese", "prc", "flag", "country", "nation", "banner"],
|
||||
"🇨🇽": ["cx", "christmas", "island", "flag", "nation", "country", "banner"],
|
||||
"🇨🇵": ["cp", "clipperton", "island", "flag", "nation", "country", "banner"],
|
||||
"🇨🇨": ["cc", "cocos", "keeling", "islands", "flag", "nation", "country", "banner"],
|
||||
"🇨🇴": ["co", "colombia", "flag", "nation", "country", "banner"],
|
||||
"🇰🇲": ["km", "comoros", "flag", "nation", "country", "banner"],
|
||||
@@ -1610,6 +1708,7 @@
|
||||
"🇨🇾": ["cy", "cyprus", "flag", "nation", "country", "banner"],
|
||||
"🇨🇿": ["cz", "czech", "republic", "flag", "nation", "country", "banner"],
|
||||
"🇩🇰": ["dk", "denmark", "flag", "nation", "country", "banner"],
|
||||
"🇩🇬": ["dg", "diego", "garcia", "flag", "nation", "country", "banner"],
|
||||
"🇩🇯": ["dj", "djibouti", "flag", "nation", "country", "banner"],
|
||||
"🇩🇲": ["dm", "dominica", "flag", "nation", "country", "banner"],
|
||||
"🇩🇴": ["do", "dominican", "republic", "flag", "nation", "country", "banner"],
|
||||
@@ -1646,6 +1745,7 @@
|
||||
"🇬🇼": ["gw", "guiana", "bissau", "flag", "nation", "country", "banner"],
|
||||
"🇬🇾": ["gy", "guyana", "flag", "nation", "country", "banner"],
|
||||
"🇭🇹": ["ht", "haiti", "flag", "nation", "country", "banner"],
|
||||
"🇭🇲": ["hm", "heard", "mcdonald", "islands", "flag", "nation", "country", "banner"],
|
||||
"🇭🇳": ["hn", "honduras", "flag", "nation", "country", "banner"],
|
||||
"🇭🇰": ["hk", "hong", "kong", "flag", "nation", "country", "banner"],
|
||||
"🇭🇺": ["hu", "hungary", "flag", "nation", "country", "banner"],
|
||||
@@ -1733,10 +1833,12 @@
|
||||
"🇷🇴": ["ro", "romania", "flag", "nation", "country", "banner"],
|
||||
"🇷🇺": ["ru", "russian", "federation", "flag", "nation", "country", "banner"],
|
||||
"🇷🇼": ["rw", "rwanda", "flag", "nation", "country", "banner"],
|
||||
"🇨🇶": ["cq", "sark", "flag", "nation", "country", "banner"],
|
||||
"🇧🇱": ["bl", "saint", "barthélemy", "flag", "nation", "country", "banner"],
|
||||
"🇸🇭": ["sh", "saint", "helena", "ascension", "tristan", "cunha", "flag", "nation", "country", "banner"],
|
||||
"🇰🇳": ["kn", "saint", "kitts", "nevis", "flag", "nation", "country", "banner"],
|
||||
"🇱🇨": ["lc", "saint", "lucia", "flag", "nation", "country", "banner"],
|
||||
"🇲🇫": ["mf", "st", "martin", "flag", "nation", "country", "banner"],
|
||||
"🇵🇲": ["pm", "saint", "pierre", "miquelon", "flag", "nation", "country", "banner"],
|
||||
"🇻🇨": ["vc", "saint", "vincent", "grenadines", "flag", "nation", "country", "banner"],
|
||||
"🇼🇸": ["ws", "western", "samoa", "flag", "nation", "country", "banner"],
|
||||
@@ -1762,6 +1864,7 @@
|
||||
"🇸🇩": ["sd", "sudan", "flag", "nation", "country", "banner"],
|
||||
"🇸🇷": ["sr", "suriname", "flag", "nation", "country", "banner"],
|
||||
"🇸🇿": ["sz", "eswatini", "flag", "nation", "country", "banner"],
|
||||
"🇸🇯": ["sj", "svalbard", "jan", "mayen", "flag", "nation", "country", "banner"],
|
||||
"🇸🇪": ["se", "sweden", "flag", "nation", "country", "banner"],
|
||||
"🇨🇭": ["ch", "switzerland", "confoederatio", "helvetica", "flag", "nation", "country", "banner"],
|
||||
"🇸🇾": ["sy", "syrian", "arab", "republic", "flag", "nation", "country", "banner"],
|
||||
@@ -1788,6 +1891,7 @@
|
||||
"🏴": ["flag", "scottish"],
|
||||
"🏴": ["flag", "welsh"],
|
||||
"🇺🇸": ["us", "usa", "united", "states", "america", "flag", "nation", "country", "banner"],
|
||||
"🇺🇲": ["um", "us", "outlying", "islands", "flag", "nation", "country", "banner"],
|
||||
"🇻🇮": ["vi", "virgin", "islands", "us", "flag", "nation", "country", "banner"],
|
||||
"🇺🇾": ["uy", "uruguay", "flag", "nation", "country", "banner"],
|
||||
"🇺🇿": ["uz", "uzbekistan", "flag", "nation", "country", "banner"],
|
||||
|
||||
@@ -36,6 +36,9 @@
|
||||
"🤡":["ピエロの顔","ピエロ","顔"],
|
||||
"😏":["にやにやした顔","顔","にやにや"],
|
||||
"😶":["口のない顔","顔","口","静かに","沈黙"],
|
||||
"🙂↔️":["いいえ","不賛成","顔","首を振る","首を横に振る"],
|
||||
"🙂↕️":["はい","頷く","顔","首を振る","首を縦に振る"],
|
||||
"":["クマ","徹夜","疲れた","眠い","顔"],
|
||||
"🫥":["点線の顔","落ち込んだ","消える","隠れる","内向的","目に見えない"],
|
||||
"😐":["普通の顔","無表情","顔","平静"],
|
||||
"🫤":["口が斜めになった顔","がっかり","無関心","疑い深い","不安"],
|
||||
@@ -111,6 +114,38 @@
|
||||
"💩":["うんち","マンガ","漫画","フン","顔","モンスター"],
|
||||
"👻":["お化け","妖怪","顔","おとぎ話","ファンタジー","幽霊","モンスター","ハロウィーン"],
|
||||
"💀":["ドクロ","体","死","顔","おとぎ話","モンスター","骸骨","ハロウィーン"],
|
||||
"":["指紋","鑑識","生体認証","セキュリティ"],
|
||||
"🏃➡️":["ジョギング","マラソン","ランナー","ランニング","右向き","急ぐ","走る","走る人","駆け足"],
|
||||
"🏃♀️➡️":["ジョギング","マラソン","ランナー","ランニング","右向き","女性","急ぐ","走る","走る女","駆け足"],
|
||||
"🏃♂️➡️":["ジョギング","マラソン","ランナー","ランニング","右向き","男性","走る男"],
|
||||
"🚶➡️":["ウォーキング","ハイキング","ぶらつく","人","右向き","大股","散歩","歩く","歩行","歩行者"],
|
||||
"🚶♀️➡️":["ウォーキング","ハイキング","右向き","女性","散歩","歩く","歩く女","歩行","歩行者"],
|
||||
"🚶♂️➡️":["ウォーキング","右向き","歩く男","歩行","歩行者","男性"],
|
||||
"🧎➡️":["ひざまずく","人","右向き","座る","正座する人"],
|
||||
"🧎♀️➡️":["ひざまずく","右向き","女性","座る","正座する女性"],
|
||||
"🧎♂️➡️":["ひざまずく","右向き","座る","正座する男性","男性"],
|
||||
"🧑🦯➡️":["アクセシビリティ","右向き","杖をついた人","目","視覚","障がい"],
|
||||
"👩🦯➡️":["アクセシビリティ","右向き","女性","杖をついた女性","白杖をついた女性","目","視覚","障がい"],
|
||||
"👨🦯➡️":["アクセシビリティ","右向き","杖をついた男性","男性","白杖をついた男性","目","視覚","障がい"],
|
||||
"🧑🦼➡️":["アクセシビリティ","右向き","車いす","障がい","電動車椅子の人"],
|
||||
"👩🦼➡️":["アクセシビリティ","右向き","女性","車いす","障がい","電動車椅子の女性"],
|
||||
"👨🦼➡️":["アクセシビリティ","右向き","男性","車いす","障がい","電動車椅子の男性"],
|
||||
"🧑🦽➡️":["アクセシビリティ","右向き","手動式車椅子の人","車いす","障がい"],
|
||||
"👩🦽➡️":["アクセシビリティ","右向き","女性","手動式車椅子の女性","車いす","障がい"],
|
||||
"👨🦽➡️":["アクセシビリティ","右向き","手動式車椅子の男性","男性","車いす","障がい"],
|
||||
"👨👩👦":["女性","子供","家族","男の子","男性","親子"],
|
||||
"🧑🧑🧒":["大人二人","子供一人","家族","大人二人と子供一人","親子"],
|
||||
"🧑🧑🧒🧒":["大人二人","子供二人","家族","大人二人と子供二人","親子"],
|
||||
"🧑🧒":["大人一人","子供一人","家族","大人一人と子供一人","親子"],
|
||||
"🧑🧒🧒":["大人一人","子供二人","家族","大人一人と子供二人","親子"],
|
||||
"🐦🔥":["ファンタジー","フェニックス","不死鳥","再生","復活","火の鳥","生まれ変わり","神話","転生","輪廻"],
|
||||
"":["不毛","干ばつ","葉のない木","冬"],
|
||||
"🍋🟩":["さわやか","トロピカル","フルーツ","マルガリータ","モヒート","ライム","果物","柑橘類","緑","酸っぱい"],
|
||||
"🍄🟫":["きのこ","キノコ","しいたけ","トリュフ","ブラウンマッシュルーム","ポートベロー","マッシュルーム","椎茸","茸","菌類"],
|
||||
"":["ビーツ","庭","根","カブ","野菜"],
|
||||
"":["オーケストラ","キューピッド","ハープ","弦楽器","愛","楽器","音楽"],
|
||||
"⛓️💥":["くさり","チェーン","壊れた","壊れた鎖"],
|
||||
"":["シャベル","ショベル","スコップ","掘る","穴","鋤"],
|
||||
"☠":["ドクロマーク","体","交差した骨","死","顔","モンスター","骸骨","ハロウィーン"],
|
||||
"👽":["宇宙人","怪獣","異星人","顔","おとぎ話","ファンタジー","モンスター","宇宙","UFO"],
|
||||
"🤖":["ロボットの顔","顔","モンスター","ロボット"],
|
||||
@@ -1518,6 +1553,7 @@
|
||||
"©️":["コピーライトマーク","著作権"],
|
||||
"®️":["登録商標マーク","登録済み","商標"],
|
||||
"™️":["商標マーク","マーク","tm","商標"],
|
||||
"":["しぶき","ペンキ","飛沫","飛び散り","スプラッシュ"],
|
||||
"🔚":["ENDと左矢印","矢印","端"],
|
||||
"🔙":["BACKと左矢印","矢印","戻る"],
|
||||
"🔛":["ON!と左右矢印","矢印","マーク","オン"],
|
||||
@@ -1643,6 +1679,7 @@
|
||||
"🇧🇷":["ブラジル国旗","ブラジル","国旗"],
|
||||
"🇧🇸":["バハマ国旗","バハマ","国旗"],
|
||||
"🇧🇹":["ブータン国旗","ブータン","国旗"],
|
||||
"🇧🇻":["ブーベ島の旗","ブーベ島","国旗"],
|
||||
"🇧🇼":["ボツワナ国旗","ボツワナ","国旗"],
|
||||
"🇧🇾":["ベラルーシ国旗","ベラルーシ","国旗"],
|
||||
"🇧🇿":["ベリーズ国旗","ベリーズ","国旗"],
|
||||
@@ -1658,6 +1695,8 @@
|
||||
"🇨🇲":["カメルーン国旗","カメルーン","国旗"],
|
||||
"🇨🇳":["中国国旗","中国","国旗"],
|
||||
"🇨🇴":["コロンビア国旗","コロンビア","国旗"],
|
||||
"🇨🇵":["クリッパートン島の旗","クリッパートン島","国旗"],
|
||||
"🇨🇶":["サーク島の旗", "サーク島", "国旗"],
|
||||
"🇨🇷":["コスタリカ国旗","コスタリカ","国旗"],
|
||||
"🇨🇺":["キューバ国旗","キューバ","国旗"],
|
||||
"🇨🇻":["カーボベルデ国旗","カーボ","ケープ","国旗","ベルデ"],
|
||||
@@ -1666,11 +1705,13 @@
|
||||
"🇨🇾":["キプロス国旗","キプロス","国旗"],
|
||||
"🇨🇿":["チェコ国旗","チェコ共和国","国旗"],
|
||||
"🇩🇪":["ドイツ国旗","国旗","ドイツ"],
|
||||
"🇩🇬":["ディエゴガルシア島の旗","ディエゴガルシア島","国旗"],
|
||||
"🇩🇯":["ジブチ国旗","ジブチ","国旗"],
|
||||
"🇩🇰":["デンマーク国旗","デンマーク","国旗"],
|
||||
"🇩🇲":["ドミニカ国旗","ドミニカ","国旗"],
|
||||
"🇩🇴":["ドミニカ共和国国旗","ドミニカ共和国","国旗"],
|
||||
"🇩🇿":["アルジェリア国旗","アルジェリア","国旗"],
|
||||
"🇪🇦":["セウタ・メリリャの旗","セウタ・メリリャ","国旗"],
|
||||
"🇪🇨":["エクアドル国旗","エクアドル","国旗"],
|
||||
"🏴":["イングランドの旗","イングランド","旗"],
|
||||
"🇪🇪":["エストニア国旗","エストニア","国旗"],
|
||||
@@ -1706,6 +1747,7 @@
|
||||
"🇬🇼":["ギニアビサウ国旗","ビサウ","国旗","ギニア"],
|
||||
"🇬🇾":["ガイアナ国旗","国旗","ガイアナ"],
|
||||
"🇭🇰":["香港の旗","中国","国旗","香港"],
|
||||
"🇭🇲":["ハード島・マクドナルド諸島の旗","ハード島・マクドナルド諸島","国旗"],
|
||||
"🇭🇳":["ホンジュラス国旗","国旗","ホンジュラス"],
|
||||
"🇭🇷":["クロアチア国旗","クロアチア","国旗"],
|
||||
"🇭🇹":["ハイチ国旗","国旗","ハイチ"],
|
||||
@@ -1751,6 +1793,7 @@
|
||||
"🇲🇨":["モナコ国旗","国旗","モナコ"],
|
||||
"🇲🇩":["モルドバ国旗","国旗","モルドバ"],
|
||||
"🇲🇪":["モンテネグロ国旗","国旗","モンテネグロ"],
|
||||
"🇲🇫":["サン・マルタンの旗","サン・マルタン","国旗"],
|
||||
"🇲🇬":["マダガスカル国旗","国旗","マダガスカル"],
|
||||
"🇲🇭":["マーシャル諸島国旗","国旗","諸島","マーシャル"],
|
||||
"🇲🇰":["マケドニア国旗","国旗","マケドニア"],
|
||||
@@ -1811,6 +1854,7 @@
|
||||
"🇸🇬":["シンガポール国旗","国旗","シンガポール"],
|
||||
"🇸🇭":["セントヘレナ島の旗","旗","ヘレナ","セント"],
|
||||
"🇸🇮":["スロベニア国旗","国旗","スロベニア"],
|
||||
"🇸🇯":["スバールバル諸島・ヤンマイエン島の旗","スバールバル諸島・ヤンマイエン島","国旗"],
|
||||
"🇸🇰":["スロバキア国旗","国旗","スロバキア"],
|
||||
"🇸🇱":["シエラレオネ国旗","国旗","シエラレオネ"],
|
||||
"🇸🇲":["サンマリノ国旗","国旗","サンマリノ"],
|
||||
@@ -1842,6 +1886,7 @@
|
||||
"🇹🇿":["タンザニア国旗","国旗","タンザニア"],
|
||||
"🇺🇦":["ウクライナ国旗","国旗","ウクライナ"],
|
||||
"🇺🇬":["ウガンダ国旗","国旗","ウガンダ"],
|
||||
"🇺🇲":["合衆国領有小離島の旗","合衆国領有小離島","国旗"],
|
||||
"🇺🇳":["国連の旗","旗","国連","連合","国際"],
|
||||
"🇺🇸":["アメリカ国旗","アメリカ","旗","合衆","合衆国","アメリカ合衆国","合衆国領有小離島"],
|
||||
"🇺🇾":["ウルグアイ国旗","国旗","ウルグアイ"],
|
||||
|
||||
@@ -36,6 +36,9 @@
|
||||
"🤡": ["ぴえろのかお","ぴえろ","かお"],
|
||||
"😏": ["にやにやしたかお","かお","にやにや"],
|
||||
"😶": ["くちのないかお","かお","くち","しずかに","ちんもく"],
|
||||
"🙂↔️": ["いいえ","ふさんせい","かお","くびをふる","くびをよこにふる"],
|
||||
"🙂↕️": ["はい","うなずく","かお","くびをふる","くびをたてにふる"],
|
||||
"": ["くま","てつや","つかれた","ねむい","かお"],
|
||||
"🫥": ["てんせんのかお","おちこんだ","きえる","かくれる","ないこうてき","めにみえない"],
|
||||
"😐": ["ふつうのかお","むひょうじょう","かお","へいせい"],
|
||||
"🫤": ["くちがななめになったかお","がっかり","むかんしん","うたがいぶかい","ふあん"],
|
||||
@@ -111,6 +114,38 @@
|
||||
"💩": ["うんち","まんが","ふん","かお","もんすたー"],
|
||||
"👻": ["おばけ","ようかい","かお","おとぎばなし","ふぁんたじー","ゆうれい","もんすたー","はろうぃーん"],
|
||||
"💀": ["どくろ","からだ","し","かお","おとぎばなし","もんすたー","がいこつ","はろうぃーん"],
|
||||
"": ["しもん","かんしき","せいたいにんしょう","せきゅりてぃ"],
|
||||
"🏃➡️": ["じょぎんぐ","まらそん","らんなー","らんにんぐ","みぎむき","いそぐ","はしる","はしるひと","かけあし"],
|
||||
"🏃♀️➡️": ["じょぎんぐ","まらそん","らんなー","らんにんぐ","みぎむき","じょせい","いそぐ","はしる","はしるおんな","かけあし"],
|
||||
"🏃♂️➡️": ["じょぎんぐ","まらそん","らんなー","らんにんぐ","みぎむき","だんせい","はしるおとこ"],
|
||||
"🚶➡️": ["うぉーきんぐ","はいきんぐ","ぶらつく","ひと","みぎむき","おおまた","さんぽ","あるく","ほこう","ほこうしゃ"],
|
||||
"🚶♀️➡️": ["うぉーきんぐ","はいきんぐ","みぎむき","じょせい","さんぽ","あるく","あるくおんな","ほこう","ほこうしゃ"],
|
||||
"🚶♂️➡️": ["うぉーきんぐ","みぎむき","あるくおとこ","ほこう","ほこうしゃ","だんせい"],
|
||||
"🧎➡️": ["ひざまずく","ひと","みぎむき","すわる","せいざするひと"],
|
||||
"🧎♀️➡️": ["ひざまずく","みぎむき","じょせい","すわる","せいざするじょせい"],
|
||||
"🧎♂️➡️": ["ひざまずく","みぎむき","すわる","せいざするだんせい","だんせい"],
|
||||
"🧑🦯➡️": ["あくせしびりてぃ","みぎむき","つえをついたひと","め","しかく","しょうがい"],
|
||||
"👩🦯➡️": ["あくせしびりてぃ","みぎむき","じょせい","つえをついたじょせい","はくじょうをついたじょせい","め","しかく","しょうがい"],
|
||||
"👨🦯➡️": ["あくせしびりてぃ","みぎむき","つえをついただんせい","だんせい","はくじょうをついただんせい","め","しかく","しょうがい"],
|
||||
"🧑🦼➡️": ["あくせしびりてぃ","みぎむき","くるまいす","しょうがい","でんどうくるまいすのひと"],
|
||||
"👩🦼➡️": ["あくせしびりてぃ","みぎむき","じょせい","くるまいす","しょうがい","でんどうくるまいすのじょせい"],
|
||||
"👨🦼➡️": ["あくせしびりてぃ","みぎむき","だんせい","くるまいす","しょうがい","でんどうくるまいすのだんせい"],
|
||||
"🧑🦽➡️": ["あくせしびりてぃ","みぎむき","しゅどうしきくるまいすのひと","くるまいす","しょうがい"],
|
||||
"👩🦽➡️": ["あくせしびりてぃ","みぎむき","じょせい","しゅどうしきくるまいすのじょせい","くるまいす","しょうがい"],
|
||||
"👨🦽➡️": ["あくせしびりてぃ","みぎむき","しゅどうしきくるまいすのだんせい","だんせい","くるまいす","しょうがい"],
|
||||
"👨👩👦": ["じょせい","こども","かぞく","おとこのこ","だんせい","おやこ"],
|
||||
"🧑🧑🧒": ["おとなふたり","こどもひとり","かぞく","おとなふたりとこどもひとり","おやこ"],
|
||||
"🧑🧑🧒🧒": ["おとなふたり","こどもふたり","かぞく","おとなふたりとこどもふたり","おやこ"],
|
||||
"🧑🧒": ["おとなひとり","こどもひとり","かぞく","おとなひとりとこどもひとり","おやこ"],
|
||||
"🧑🧒🧒": ["おとなひとり","こどもふたり","かぞく","おとなひとりとこどもふたり","おやこ"],
|
||||
"🐦🔥": ["ふぁんたじー","ふぇにっくす","ふしちょう","さいせい","ふっかつ","ひのとり","うまれかわり","しんわ","てんせい","りんね"],
|
||||
"": ["ふもう","かんばつ","はのないき","ふゆ"],
|
||||
"🍋🟩": ["さわやか","とろぴかる","ふるーつ","まるがりーた","もひーと","らいむ","くだもの","かんきつるい","みどり","すっぱい"],
|
||||
"🍄🟫": ["きのこ","しいたけ","とりゅふ","ぶらうんまっしゅるーむ","ぽーとべろー","まっしゅるーむ","たけ","きんるい"],
|
||||
"": ["びーつ","にわ","ね","かぶ","やさい"],
|
||||
"": ["おーけすとら","きゅーぴっど","はーぷ","げんがっき","あい","がっき","おんがく"],
|
||||
"⛓️💥": ["くさり","ちぇーん","こわれた","こわれたくさり"],
|
||||
"": ["しゃべる","しょべる","すこっぷ","ほる","あな","すき"],
|
||||
"☠": ["どくろまーく","からだ","こうさしたほね","し","かお","もんすたー","がいこつ","はろうぃーん"],
|
||||
"👽": ["うちゅうじん","かいじゅう","いせいじん","かお","おとぎばなし","ふぁんたじー","もんすたー","うちゅう","UFO"],
|
||||
"🤖": ["ろぼっとのかお","かお","もんすたー","ろぼっと"],
|
||||
@@ -382,9 +417,9 @@
|
||||
"🚶♀️": ["あるくじょせい","はいきんぐ","ほこうしゃ","あるく","うぉーきんぐ","じょせい","おんな"],
|
||||
"🚶": ["あるくひと","はいきんぐ","ほこうしゃ","あるく","うぉーきんぐ"],
|
||||
"🚶♂️": ["あるくだんせい","はいきんぐ","ほこうしゃ","あるく","うぉーきんぐ","おとこ","だんせい"],
|
||||
"👩🦯": ["しろつえをもったじょせい","あくせしびりてぃ","めがふじゆう","じょせい","おんな"],
|
||||
"🧑🦯": ["しろつえをもったひと","あくせしびりてぃ","めがふじゆう"],
|
||||
"👨🦯": ["しろつえをもっただんせい","あくせしびりてぃ","めがふじゆう","だんせい","おとこ"],
|
||||
"👩🦯": ["はくじょうをもったじょせい","あくせしびりてぃ","めがふじゆう","じょせい","おんな"],
|
||||
"🧑🦯": ["はくじょうをもったひと","あくせしびりてぃ","めがふじゆう"],
|
||||
"👨🦯": ["はくじょうをもっただんせい","あくせしびりてぃ","めがふじゆう","だんせい","おとこ"],
|
||||
"🏃♀️": ["はしるじょせい","まらそん","らんなー","らんにんぐ","じょせい","おんな"],
|
||||
"🏃": ["はしるひと","まらそん","らんなー","らんにんぐ"],
|
||||
"🏃♂️": ["はしるだんせい","まらそん","らんなー","らんにんぐ","おとこ","だんせい"],
|
||||
@@ -1129,7 +1164,7 @@
|
||||
"🧾": ["りょうしゅうしょ","かいけい","ぼき","しょうこ","しょうめい"],
|
||||
"💎": ["ほうせき","だいあもんど","じゅえる","ろまんす"],
|
||||
"⚖": ["はかり","てんびん","こうせい","てんびんざ","ものさし","どうぐ","じゅうりょう","せいざ"],
|
||||
"🦯": ["しろつえ","あくせしびりてぃ","めがふじゆう"],
|
||||
"🦯": ["はくじょう","あくせしびりてぃ","めがふじゆう"],
|
||||
"🧰": ["どうぐばこ","むね","せいびし","こうぐ"],
|
||||
"🔧": ["れんち","どうぐ"],
|
||||
"🪛": ["どらいばー","ねじ","こうぐ"],
|
||||
@@ -1518,6 +1553,7 @@
|
||||
"©️": ["こぴーらいとまーく","ちょさくけん"],
|
||||
"®️": ["とうろくしょうひょうまーく","とうろくずみ","しょうひょう"],
|
||||
"™️": ["しょうひょうまーく","まーく","tm","しょうひょう"],
|
||||
"": ["しぶき","ぺんき","ひまつ","とびちり","すぷらっしゅ"],
|
||||
"🔚": ["ENDとひだりやじるし","やじるし","はじ"],
|
||||
"🔙": ["BACKとひだりやじるし","やじるし","もどる"],
|
||||
"🔛": ["ON!とさゆうやじるし","やじるし","まーく","おん"],
|
||||
@@ -1643,6 +1679,7 @@
|
||||
"🇧🇷": ["ぶらじるこっき","ぶらじる","こっき"],
|
||||
"🇧🇸": ["ばはまこっき","ばはま","こっき"],
|
||||
"🇧🇹": ["ぶーたんこっき","ぶーたん","こっき"],
|
||||
"🇧🇻": ["ぶーべとうのはた","ぶーべとう","こっき"],
|
||||
"🇧🇼": ["ぼつわなこっき","ぼつわな","こっき"],
|
||||
"🇧🇾": ["べらるーしこっき","べらるーし","こっき"],
|
||||
"🇧🇿": ["べりーずこっき","べりーず","こっき"],
|
||||
@@ -1658,6 +1695,8 @@
|
||||
"🇨🇲": ["かめるーんこっき","かめるーん","こっき"],
|
||||
"🇨🇳": ["ちゅうごくこっき","ちゅうごく","こっき"],
|
||||
"🇨🇴": ["ころんびあこっき","ころんびあ","こっき"],
|
||||
"🇨🇵": ["くりっぱーとんとうのはた","くりっぱーとんとう","こっき"],
|
||||
"🇨🇶": ["さーくとうのはた", "さーくとう", "こっき"],
|
||||
"🇨🇷": ["こすたりかこっき","こすたりか","こっき"],
|
||||
"🇨🇺": ["きゅーばこっき","きゅーば","こっき"],
|
||||
"🇨🇻": ["かーぼべるでこっき","かーぼ","けーぷ","こっき","べるで"],
|
||||
@@ -1666,11 +1705,13 @@
|
||||
"🇨🇾": ["きぷろすこっき","きぷろす","こっき"],
|
||||
"🇨🇿": ["ちぇここっき","ちぇこきょうわこく","こっき"],
|
||||
"🇩🇪": ["どいつこっき","こっき","どいつ"],
|
||||
"🇩🇬": ["でぃえごがるしあとうのはた","でぃえごがるしあとう","こっき"],
|
||||
"🇩🇯": ["じぶちこっき","じぶち","こっき"],
|
||||
"🇩🇰": ["でんまーくこっき","でんまーく","こっき"],
|
||||
"🇩🇲": ["どみにかこっき","どみにか","こっき"],
|
||||
"🇩🇴": ["どみにかきょうわこくこっき","どみにかきょうわこく","こっき"],
|
||||
"🇩🇿": ["あるじぇりあこっき","あるじぇりあ","こっき"],
|
||||
"🇪🇦": ["せうた・めりりゃのはた","せうた・めりりゃ","こっき"],
|
||||
"🇪🇨": ["えくあどるこっき","えくあどる","こっき"],
|
||||
"🏴": ["いんぐらんどのはた","いんぐらんど","こっき"],
|
||||
"🇪🇪": ["えすとにあこっき","えすとにあ","こっき"],
|
||||
@@ -1706,6 +1747,7 @@
|
||||
"🇬🇼": ["ぎにあびさうこっき","びさう","こっき","ぎにあ"],
|
||||
"🇬🇾": ["がいあなこっき","こっき","がいあな"],
|
||||
"🇭🇰": ["ほんこんのはた","ちゅうごく","こっき","ほんこん"],
|
||||
"🇭🇲": ["はーどとう・まくどなるどしょとうのはた","はーどとう・まくどなるどしょとう","こっき"],
|
||||
"🇭🇳": ["ほんじゅらすこっき","こっき","ほんじゅらす"],
|
||||
"🇭🇷": ["くろあちあこっき","くろあちあ","こっき"],
|
||||
"🇭🇹": ["はいちこっき","こっき","はいち"],
|
||||
@@ -1751,6 +1793,7 @@
|
||||
"🇲🇨": ["もなここっき","こっき","もなこ"],
|
||||
"🇲🇩": ["もるどばこっき","こっき","もるどば"],
|
||||
"🇲🇪": ["もんてねぐろこっき","こっき","もんてねぐろ"],
|
||||
"🇲🇫": ["さん・まるたんのはた","さん・まるたん","こっき"],
|
||||
"🇲🇬": ["まだがすかるこっき","こっき","まだがすかる"],
|
||||
"🇲🇭": ["まーしゃるしょとうこっき","こっき","しょとう","まーしゃる"],
|
||||
"🇲🇰": ["まけどにあこっき","こっき","まけどにあ"],
|
||||
@@ -1811,6 +1854,7 @@
|
||||
"🇸🇬": ["しんがぽーるこっき","こっき","しんがぽーる"],
|
||||
"🇸🇭": ["せんとへれなとうのはた","はた","へれな","せんと"],
|
||||
"🇸🇮": ["すろべにあこっき","こっき","すろべにあ"],
|
||||
"🇸🇯": ["すばーるばるしょとう・やんまいえんとうのはた","すばーるばるしょとう・やんまいえんとう","こっき"],
|
||||
"🇸🇰": ["すろばきあこっき","こっき","すろばきあ"],
|
||||
"🇸🇱": ["しえられおねこっき","こっき","しえられおね"],
|
||||
"🇸🇲": ["さんまりのこっき","こっき","さんまりの"],
|
||||
@@ -1842,6 +1886,7 @@
|
||||
"🇹🇿": ["たんざにあこっき","こっき","たんざにあ"],
|
||||
"🇺🇦": ["うくらいなこっき","こっき","うくらいな"],
|
||||
"🇺🇬": ["うがんだこっき","こっき","うがんだ"],
|
||||
"🇺🇲": ["がっしゅうこくりょうゆうしょうりとうのはた","がっしゅうこくりょうゆうしょうりとう","こっき"],
|
||||
"🇺🇳": ["こくれんのはた","はた","こくれん","れんごう","こくさい"],
|
||||
"🇺🇸": ["あめりかこっき","あめりか","はた","ごうしゅう","がっしゅうこく","あめりかがっしゅうこく","がっしゅうこくりょうゆうしょうりとう"],
|
||||
"🇺🇾": ["うるぐあいこっき","こっき","うるぐあい"],
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import type { GeneratedSearchIndexItem } from 'search-index';
|
||||
|
||||
export type SearchIndexItem = {
|
||||
id: string;
|
||||
parentId?: string;
|
||||
path?: string;
|
||||
label: string;
|
||||
keywords: string[];
|
||||
texts: string[];
|
||||
icon?: string;
|
||||
};
|
||||
|
||||
export function genSearchIndexes(generated: GeneratedSearchIndexItem[]): SearchIndexItem[] {
|
||||
const rootMods = new Map(generated.map(item => [item.id, item]));
|
||||
|
||||
// link inlining here
|
||||
for (const item of generated) {
|
||||
if (item.inlining) {
|
||||
for (const id of item.inlining) {
|
||||
const inline = rootMods.get(id);
|
||||
if (inline) {
|
||||
inline.parentId = item.id;
|
||||
inline.path = item.path;
|
||||
} else {
|
||||
console.log('[Settings Search Index] Failed to inline', id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return generated;
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import { i18n } from '@/i18n.js';
|
||||
import * as os from '@/os.js';
|
||||
import { unisonReload } from '@/utility/unison-reload.js';
|
||||
|
||||
let isReloadConfirming = false;
|
||||
|
||||
export async function reloadAsk(opts: {
|
||||
unison?: boolean;
|
||||
reason?: string;
|
||||
}) {
|
||||
if (isReloadConfirming) {
|
||||
return;
|
||||
}
|
||||
|
||||
isReloadConfirming = true;
|
||||
|
||||
const { canceled } = await os.confirm(opts.reason == null ? {
|
||||
type: 'info',
|
||||
text: i18n.ts.reloadConfirm,
|
||||
} : {
|
||||
type: 'info',
|
||||
title: i18n.ts.reloadConfirm,
|
||||
text: opts.reason,
|
||||
}).finally(() => {
|
||||
isReloadConfirming = false;
|
||||
});
|
||||
|
||||
if (canceled) return;
|
||||
|
||||
if (opts.unison) {
|
||||
unisonReload();
|
||||
} else {
|
||||
window.location.reload();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import { ref } from 'vue';
|
||||
|
||||
export const shouldSuggestReload = ref(false);
|
||||
|
||||
export function suggestReload() {
|
||||
shouldSuggestReload.value = true;
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import { searchIndexes as generated } from 'search-index:settings';
|
||||
import type { GeneratedSearchIndexItem } from 'search-index:settings';
|
||||
|
||||
export type SearchIndexItem = {
|
||||
id: string;
|
||||
parentId?: string;
|
||||
path?: string;
|
||||
label: string;
|
||||
keywords: string[];
|
||||
icon?: string;
|
||||
};
|
||||
|
||||
const rootMods = new Map(generated.map(item => [item.id, item]));
|
||||
|
||||
// link inlining here
|
||||
for (const item of generated) {
|
||||
if (item.inlining) {
|
||||
for (const id of item.inlining) {
|
||||
const inline = rootMods.get(id);
|
||||
if (inline) {
|
||||
inline.parentId = item.id;
|
||||
inline.path = item.path;
|
||||
} else {
|
||||
console.log('[Settings Search Index] Failed to inline', id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const searchIndexes: SearchIndexItem[] = generated;
|
||||
|
||||
+20
-11
@@ -3,16 +3,25 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
declare module 'search-index:settings' {
|
||||
export type GeneratedSearchIndexItem = {
|
||||
id: string;
|
||||
parentId?: string;
|
||||
path?: string;
|
||||
label: string;
|
||||
keywords: string[];
|
||||
icon?: string;
|
||||
inlining?: string[];
|
||||
};
|
||||
type XGeneratedSearchIndexItem = {
|
||||
id: string;
|
||||
parentId?: string;
|
||||
path?: string;
|
||||
label: string;
|
||||
keywords: string[];
|
||||
texts: string[];
|
||||
icon?: string;
|
||||
inlining?: string[];
|
||||
};
|
||||
|
||||
export const searchIndexes: GeneratedSearchIndexItem[];
|
||||
declare module 'search-index' {
|
||||
export type GeneratedSearchIndexItem = XGeneratedSearchIndexItem;
|
||||
}
|
||||
|
||||
declare module 'search-index:settings' {
|
||||
export const searchIndexes: XGeneratedSearchIndexItem[];
|
||||
}
|
||||
|
||||
declare module 'search-index:admin' {
|
||||
export const searchIndexes: XGeneratedSearchIndexItem[];
|
||||
}
|
||||
|
||||
@@ -28,6 +28,11 @@ export const searchIndexes = [{
|
||||
mainVirtualModule: 'search-index:settings',
|
||||
modulesToHmrOnUpdate: ['src/pages/settings/index.vue'],
|
||||
verbose: process.env.FRONTEND_SEARCH_INDEX_VERBOSE === 'true',
|
||||
}, {
|
||||
targetFilePaths: ['src/pages/admin/*.vue'],
|
||||
mainVirtualModule: 'search-index:admin',
|
||||
modulesToHmrOnUpdate: ['src/pages/admin/index.vue'],
|
||||
verbose: process.env.FRONTEND_SEARCH_INDEX_VERBOSE === 'true',
|
||||
}] satisfies SearchIndexOptions[];
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
{
|
||||
"$schema": "https://swc.rs/schema.json",
|
||||
"jsc": {
|
||||
"parser": {
|
||||
"syntax": "typescript",
|
||||
"dynamicImport": true,
|
||||
"decorators": true
|
||||
},
|
||||
"transform": {
|
||||
"legacyDecorator": true,
|
||||
"decoratorMetadata": true
|
||||
},
|
||||
"experimental": {
|
||||
"keepImportAssertions": true
|
||||
},
|
||||
"baseUrl": "src",
|
||||
"paths": {
|
||||
"@/*": ["*"]
|
||||
},
|
||||
"target": "es2022"
|
||||
},
|
||||
"minify": false
|
||||
}
|
||||
@@ -9,7 +9,7 @@ export default [
|
||||
'**/node_modules',
|
||||
'built',
|
||||
'coverage',
|
||||
'jest.config.ts',
|
||||
'vitest.config.ts',
|
||||
'test',
|
||||
'test-d',
|
||||
'generator',
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
"generate": "tsx src/generator.ts && eslint ./built/**/*.ts --fix"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@readme/openapi-parser": "2.7.0",
|
||||
"@readme/openapi-parser": "5.0.0",
|
||||
"@types/node": "22.16.4",
|
||||
"@typescript-eslint/eslint-plugin": "8.37.0",
|
||||
"@typescript-eslint/parser": "8.37.0",
|
||||
|
||||
@@ -2,7 +2,7 @@ import assert from 'assert';
|
||||
import { mkdir, readFile, writeFile } from 'fs/promises';
|
||||
import type { OpenAPIV3_1 } from 'openapi-types';
|
||||
import { toPascal } from 'ts-case-convert';
|
||||
import OpenAPIParser from '@readme/openapi-parser';
|
||||
import { parse } from '@readme/openapi-parser';
|
||||
import openapiTS, { astToString } from 'openapi-typescript';
|
||||
import type { OpenAPI3, OperationObject, PathItemObject } from 'openapi-typescript';
|
||||
import ts from 'typescript';
|
||||
@@ -401,7 +401,7 @@ async function main() {
|
||||
await mkdir(generatePath, { recursive: true });
|
||||
|
||||
const openApiJsonPath = './api.json';
|
||||
const openApiDocs = await OpenAPIParser.parse(openApiJsonPath) as OpenAPIV3_1.Document;
|
||||
const openApiDocs = await parse(openApiJsonPath) as OpenAPIV3_1.Document;
|
||||
|
||||
const typeFileName = './built/autogen/types.ts';
|
||||
await generateBaseTypes(openApiDocs, openApiJsonPath, typeFileName);
|
||||
|
||||
@@ -1,207 +0,0 @@
|
||||
/*
|
||||
* For a detailed explanation regarding each configuration property and type check, visit:
|
||||
* https://jestjs.io/docs/en/configuration.html
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
// All imported modules in your tests should be mocked automatically
|
||||
// automock: false,
|
||||
|
||||
// Stop running tests after `n` failures
|
||||
// bail: 0,
|
||||
|
||||
// The directory where Jest should store its cached dependency information
|
||||
// cacheDirectory: "C:\\Users\\ai\\AppData\\Local\\Temp\\jest",
|
||||
|
||||
// Automatically clear mock calls and instances between every test
|
||||
// clearMocks: false,
|
||||
|
||||
// Indicates whether the coverage information should be collected while executing the test
|
||||
// collectCoverage: false,
|
||||
|
||||
// An array of glob patterns indicating a set of files for which coverage information should be collected
|
||||
// collectCoverageFrom: undefined,
|
||||
|
||||
// The directory where Jest should output its coverage files
|
||||
coverageDirectory: "coverage",
|
||||
|
||||
// An array of regexp pattern strings used to skip coverage collection
|
||||
// coveragePathIgnorePatterns: [
|
||||
// "\\\\node_modules\\\\"
|
||||
// ],
|
||||
|
||||
// Indicates which provider should be used to instrument code for coverage
|
||||
coverageProvider: "v8",
|
||||
|
||||
// A list of reporter names that Jest uses when writing coverage reports
|
||||
// coverageReporters: [
|
||||
// "json",
|
||||
// "text",
|
||||
// "lcov",
|
||||
// "clover"
|
||||
// ],
|
||||
|
||||
// An object that configures minimum threshold enforcement for coverage results
|
||||
// coverageThreshold: undefined,
|
||||
|
||||
// A path to a custom dependency extractor
|
||||
// dependencyExtractor: undefined,
|
||||
|
||||
// Make calling deprecated APIs throw helpful error messages
|
||||
// errorOnDeprecated: false,
|
||||
|
||||
// Force coverage collection from ignored files using an array of glob patterns
|
||||
// forceCoverageMatch: [],
|
||||
|
||||
// A path to a module which exports an async function that is triggered once before all test suites
|
||||
// globalSetup: undefined,
|
||||
|
||||
// A path to a module which exports an async function that is triggered once after all test suites
|
||||
// globalTeardown: undefined,
|
||||
|
||||
// A set of global variables that need to be available in all test environments
|
||||
// globals: {},
|
||||
|
||||
// The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers.
|
||||
// maxWorkers: "50%",
|
||||
|
||||
// An array of directory names to be searched recursively up from the requiring module's location
|
||||
// moduleDirectories: [
|
||||
// "node_modules"
|
||||
// ],
|
||||
|
||||
// An array of file extensions your modules use
|
||||
// moduleFileExtensions: [
|
||||
// "js",
|
||||
// "json",
|
||||
// "jsx",
|
||||
// "ts",
|
||||
// "tsx",
|
||||
// "node"
|
||||
// ],
|
||||
|
||||
// A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module
|
||||
moduleNameMapper: {
|
||||
// Do not resolve .wasm.js to .wasm by the rule below
|
||||
'^(.+)\\.wasm\\.js$': '$1.wasm.js',
|
||||
// SWC converts @/foo/bar.js to `../../src/foo/bar.js`, and then this rule
|
||||
// converts it again to `../../src/foo/bar` which then can be resolved to
|
||||
// `.ts` files.
|
||||
// See https://github.com/swc-project/jest/issues/64#issuecomment-1029753225
|
||||
// TODO: Use `--allowImportingTsExtensions` on TypeScript 5.0 so that we can
|
||||
// directly import `.ts` files without this hack.
|
||||
'^((?:\\.{1,2}|[A-Z:])*/.*)\\.js$': '$1',
|
||||
},
|
||||
|
||||
// An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader
|
||||
// modulePathIgnorePatterns: [],
|
||||
|
||||
// Activates notifications for test results
|
||||
// notify: false,
|
||||
|
||||
// An enum that specifies notification mode. Requires { notify: true }
|
||||
// notifyMode: "failure-change",
|
||||
|
||||
// A preset that is used as a base for Jest's configuration
|
||||
// preset: undefined,
|
||||
|
||||
// Run tests from one or more projects
|
||||
// projects: undefined,
|
||||
|
||||
// Use this configuration option to add custom reporters to Jest
|
||||
// reporters: undefined,
|
||||
|
||||
// Automatically reset mock state between every test
|
||||
// resetMocks: false,
|
||||
|
||||
// Reset the module registry before running each individual test
|
||||
// resetModules: false,
|
||||
|
||||
// A path to a custom resolver
|
||||
// resolver: undefined,
|
||||
|
||||
// Automatically restore mock state between every test
|
||||
// restoreMocks: false,
|
||||
|
||||
// The root directory that Jest should scan for tests and modules within
|
||||
// rootDir: undefined,
|
||||
|
||||
// A list of paths to directories that Jest should use to search for files in
|
||||
roots: [
|
||||
"<rootDir>"
|
||||
],
|
||||
|
||||
// Allows you to use a custom runner instead of Jest's default test runner
|
||||
// runner: "jest-runner",
|
||||
|
||||
// The paths to modules that run some code to configure or set up the testing environment before each test
|
||||
// setupFiles: [],
|
||||
|
||||
// A list of paths to modules that run some code to configure or set up the testing framework before each test
|
||||
// setupFilesAfterEnv: [],
|
||||
|
||||
// The number of seconds after which a test is considered as slow and reported as such in the results.
|
||||
// slowTestThreshold: 5,
|
||||
|
||||
// A list of paths to snapshot serializer modules Jest should use for snapshot testing
|
||||
// snapshotSerializers: [],
|
||||
|
||||
// The test environment that will be used for testing
|
||||
testEnvironment: "node",
|
||||
|
||||
// Options that will be passed to the testEnvironment
|
||||
// testEnvironmentOptions: {},
|
||||
|
||||
// Adds a location field to test results
|
||||
// testLocationInResults: false,
|
||||
|
||||
// The glob patterns Jest uses to detect test files
|
||||
testMatch: [
|
||||
"**/__tests__/**/*.[jt]s?(x)",
|
||||
"**/?(*.)+(spec|test).[tj]s?(x)",
|
||||
"<rootDir>/test/**/*"
|
||||
],
|
||||
|
||||
// An array of regexp pattern strings that are matched against all test paths, matched tests are skipped
|
||||
// testPathIgnorePatterns: [
|
||||
// "\\\\node_modules\\\\"
|
||||
// ],
|
||||
|
||||
// The regexp pattern or array of patterns that Jest uses to detect test files
|
||||
// testRegex: [],
|
||||
|
||||
// This option allows the use of a custom results processor
|
||||
// testResultsProcessor: undefined,
|
||||
|
||||
// This option allows use of a custom test runner
|
||||
// testRunner: "jasmine2",
|
||||
|
||||
// This option sets the URL for the jsdom environment. It is reflected in properties such as location.href
|
||||
// testURL: "http://localhost",
|
||||
|
||||
// Setting this value to "fake" allows the use of fake timers for functions such as "setTimeout"
|
||||
// timers: "real",
|
||||
|
||||
// A map from regular expressions to paths to transformers
|
||||
transform: {
|
||||
"^.+\\.(t|j)sx?$": ["@swc/jest"],
|
||||
},
|
||||
|
||||
// An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation
|
||||
// transformIgnorePatterns: [
|
||||
// "\\\\node_modules\\\\",
|
||||
// "\\.pnp\\.[^\\\\]+$"
|
||||
// ],
|
||||
|
||||
// An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them
|
||||
// unmockedModulePathPatterns: undefined,
|
||||
|
||||
// Indicates whether each individual test should be reported during the run
|
||||
// verbose: undefined,
|
||||
|
||||
// An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode
|
||||
// watchPathIgnorePatterns: [],
|
||||
|
||||
// Whether to use watchman for file crawling
|
||||
// watchman: true,
|
||||
};
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"type": "module",
|
||||
"name": "misskey-js",
|
||||
"version": "2025.8.0-alpha.1",
|
||||
"version": "2025.8.0-alpha.4",
|
||||
"description": "Misskey SDK for JavaScript",
|
||||
"license": "MIT",
|
||||
"main": "./built/index.js",
|
||||
@@ -25,8 +25,8 @@
|
||||
"eslint": "eslint './**/*.{js,jsx,ts,tsx}'",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"lint": "pnpm typecheck && pnpm eslint",
|
||||
"jest": "jest --coverage --detectOpenHandles",
|
||||
"test": "pnpm jest && pnpm tsd",
|
||||
"vitest": "vitest run --coverage",
|
||||
"test": "pnpm vitest && pnpm tsd",
|
||||
"update-autogen-code": "pnpm --filter misskey-js-type-generator generate && ncp generator/built/autogen src/autogen"
|
||||
},
|
||||
"repository": {
|
||||
@@ -36,22 +36,19 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@microsoft/api-extractor": "7.52.8",
|
||||
"@swc/jest": "0.2.39",
|
||||
"@types/jest": "29.5.14",
|
||||
"@types/node": "22.16.4",
|
||||
"@typescript-eslint/eslint-plugin": "8.37.0",
|
||||
"@typescript-eslint/parser": "8.37.0",
|
||||
"jest": "29.7.0",
|
||||
"jest-fetch-mock": "3.0.3",
|
||||
"jest-websocket-mock": "2.5.0",
|
||||
"mock-socket": "9.3.1",
|
||||
"@vitest/coverage-v8": "3.2.4",
|
||||
"esbuild": "0.25.6",
|
||||
"execa": "9.6.0",
|
||||
"glob": "11.0.3",
|
||||
"ncp": "2.0.0",
|
||||
"nodemon": "3.1.10",
|
||||
"execa": "8.0.1",
|
||||
"tsd": "0.32.0",
|
||||
"typescript": "5.8.3",
|
||||
"esbuild": "0.25.6",
|
||||
"glob": "11.0.3"
|
||||
"vitest": "3.2.4",
|
||||
"vitest-websocket-mock": "0.5.0"
|
||||
},
|
||||
"files": [
|
||||
"built"
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { describe, test } from 'vitest';
|
||||
import { expectType } from 'tsd';
|
||||
import * as Misskey from '../src/index.js';
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { describe, test } from 'vitest';
|
||||
import { expectType } from 'tsd';
|
||||
import * as Misskey from '../src/index.js';
|
||||
|
||||
|
||||
+141
-125
@@ -1,41 +1,24 @@
|
||||
import { enableFetchMocks } from 'jest-fetch-mock';
|
||||
import { vi, describe, test, expect } from 'vitest';
|
||||
import { APIClient, isAPIError } from '../src/api.js';
|
||||
|
||||
enableFetchMocks();
|
||||
|
||||
function getFetchCall(call: any[]) {
|
||||
const { body, method } = call[1];
|
||||
const contentType = call[1].headers['Content-Type'];
|
||||
if (
|
||||
body == null ||
|
||||
(contentType === 'application/json' && typeof body !== 'string') ||
|
||||
(contentType === 'multipart/form-data' && !(body instanceof FormData))
|
||||
) {
|
||||
throw new Error('invalid body');
|
||||
}
|
||||
return {
|
||||
url: call[0],
|
||||
method: method,
|
||||
contentType: contentType,
|
||||
body: body instanceof FormData ? Object.fromEntries(body.entries()) : JSON.parse(body),
|
||||
};
|
||||
}
|
||||
|
||||
describe('API', () => {
|
||||
test('success', async () => {
|
||||
fetchMock.resetMocks();
|
||||
fetchMock.mockResponse(async (req) => {
|
||||
const body = await req.json();
|
||||
if (req.method == 'POST' && req.url == 'https://misskey.test/api/i') {
|
||||
if (body.i === 'TOKEN') {
|
||||
return JSON.stringify({ id: 'foo' });
|
||||
} else {
|
||||
return { status: 400 };
|
||||
const fetchMock = vi
|
||||
.spyOn(globalThis, 'fetch')
|
||||
.mockImplementation(async (url, options) => {
|
||||
if (url === 'https://misskey.test/api/i' && options?.method === 'POST') {
|
||||
if (options.body) {
|
||||
const body = JSON.parse(options.body as string);
|
||||
if (body.i === 'TOKEN') {
|
||||
return new Response(JSON.stringify({ id: 'foo' }), { status: 200 });
|
||||
}
|
||||
}
|
||||
|
||||
return new Response(null, { status: 400 });
|
||||
}
|
||||
} else {
|
||||
return { status: 404 };
|
||||
}
|
||||
});
|
||||
|
||||
return new Response(null, { status: 404 });
|
||||
});
|
||||
|
||||
const cli = new APIClient({
|
||||
origin: 'https://misskey.test',
|
||||
@@ -48,28 +31,38 @@ describe('API', () => {
|
||||
id: 'foo'
|
||||
});
|
||||
|
||||
expect(getFetchCall(fetchMock.mock.calls[0])).toEqual({
|
||||
url: 'https://misskey.test/api/i',
|
||||
fetch('https://misskey.test/api/i', {
|
||||
method: 'POST',
|
||||
contentType: 'application/json',
|
||||
body: { i: 'TOKEN' }
|
||||
})
|
||||
|
||||
expect(fetchMock).toHaveBeenCalledWith('https://misskey.test/api/i', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
credentials: 'omit',
|
||||
cache: 'no-cache',
|
||||
body: JSON.stringify({ i: 'TOKEN' }),
|
||||
});
|
||||
|
||||
fetchMock.mockRestore();
|
||||
});
|
||||
|
||||
test('with params', async () => {
|
||||
fetchMock.resetMocks();
|
||||
fetchMock.mockResponse(async (req) => {
|
||||
const body = await req.json();
|
||||
if (req.method == 'POST' && req.url == 'https://misskey.test/api/notes/show') {
|
||||
if (body.i === 'TOKEN' && body.noteId === 'aaaaa') {
|
||||
return JSON.stringify({ id: 'foo' });
|
||||
} else {
|
||||
return { status: 400 };
|
||||
const fetchMock = vi
|
||||
.spyOn(globalThis, 'fetch')
|
||||
.mockImplementation(async (url, options) => {
|
||||
if (url === 'https://misskey.test/api/notes/show' && options?.method === 'POST') {
|
||||
if (options.body) {
|
||||
const body = JSON.parse(options.body as string);
|
||||
if (body.i === 'TOKEN' && body.noteId === 'aaaaa') {
|
||||
return new Response(JSON.stringify({ id: 'foo' }), { status: 200 });
|
||||
}
|
||||
}
|
||||
return new Response(null, { status: 400 });
|
||||
}
|
||||
} else {
|
||||
return { status: 404 };
|
||||
}
|
||||
});
|
||||
return new Response(null, { status: 404 });
|
||||
});
|
||||
|
||||
const cli = new APIClient({
|
||||
origin: 'https://misskey.test',
|
||||
@@ -82,23 +75,34 @@ describe('API', () => {
|
||||
id: 'foo'
|
||||
});
|
||||
|
||||
expect(getFetchCall(fetchMock.mock.calls[0])).toEqual({
|
||||
url: 'https://misskey.test/api/notes/show',
|
||||
expect(fetchMock).toHaveBeenCalledWith('https://misskey.test/api/notes/show', {
|
||||
method: 'POST',
|
||||
contentType: 'application/json',
|
||||
body: { i: 'TOKEN', noteId: 'aaaaa' }
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
credentials: 'omit',
|
||||
cache: 'no-cache',
|
||||
body: JSON.stringify({ noteId: 'aaaaa', i: 'TOKEN' }),
|
||||
});
|
||||
|
||||
fetchMock.mockRestore();
|
||||
});
|
||||
|
||||
test('multipart/form-data', async () => {
|
||||
fetchMock.resetMocks();
|
||||
fetchMock.mockResponse(async (req) => {
|
||||
if (req.method == 'POST' && req.url == 'https://misskey.test/api/drive/files/create') {
|
||||
return JSON.stringify({ id: 'foo' });
|
||||
} else {
|
||||
return { status: 404 };
|
||||
}
|
||||
});
|
||||
const fetchMock = vi
|
||||
.spyOn(globalThis, 'fetch')
|
||||
.mockImplementation(async (url, options) => {
|
||||
if (url === 'https://misskey.test/api/drive/files/create' && options?.method === 'POST') {
|
||||
if (options.body instanceof FormData) {
|
||||
const file = options.body.get('file');
|
||||
if (file instanceof File && file.name === 'foo.txt') {
|
||||
return new Response(JSON.stringify({ id: 'foo' }), { status: 200 });
|
||||
}
|
||||
}
|
||||
return new Response(null, { status: 400 });
|
||||
}
|
||||
return new Response(null, { status: 404 });
|
||||
});
|
||||
|
||||
const cli = new APIClient({
|
||||
origin: 'https://misskey.test',
|
||||
@@ -116,26 +120,26 @@ describe('API', () => {
|
||||
id: 'foo'
|
||||
});
|
||||
|
||||
expect(getFetchCall(fetchMock.mock.calls[0])).toEqual({
|
||||
url: 'https://misskey.test/api/drive/files/create',
|
||||
expect(fetchMock).toHaveBeenCalledWith('https://misskey.test/api/drive/files/create', {
|
||||
method: 'POST',
|
||||
contentType: undefined,
|
||||
body: {
|
||||
i: 'TOKEN',
|
||||
file: testFile,
|
||||
}
|
||||
body: expect.any(FormData),
|
||||
headers: {},
|
||||
credentials: 'omit',
|
||||
cache: 'no-cache',
|
||||
});
|
||||
|
||||
fetchMock.mockRestore();
|
||||
});
|
||||
|
||||
test('204 No Content で null が返る', async () => {
|
||||
fetchMock.resetMocks();
|
||||
fetchMock.mockResponse(async (req) => {
|
||||
if (req.method == 'POST' && req.url == 'https://misskey.test/api/reset-password') {
|
||||
return { status: 204 };
|
||||
} else {
|
||||
return { status: 404 };
|
||||
}
|
||||
});
|
||||
const fetchMock = vi
|
||||
.spyOn(globalThis, 'fetch')
|
||||
.mockImplementation(async (url, options) => {
|
||||
if (url === 'https://misskey.test/api/reset-password' && options?.method === 'POST') {
|
||||
return new Response(null, { status: 204 });
|
||||
}
|
||||
return new Response(null, { status: 404 });
|
||||
});
|
||||
|
||||
const cli = new APIClient({
|
||||
origin: 'https://misskey.test',
|
||||
@@ -146,37 +150,42 @@ describe('API', () => {
|
||||
|
||||
expect(res).toEqual(null);
|
||||
|
||||
expect(getFetchCall(fetchMock.mock.calls[0])).toEqual({
|
||||
url: 'https://misskey.test/api/reset-password',
|
||||
expect(fetchMock).toHaveBeenCalledWith('https://misskey.test/api/reset-password', {
|
||||
method: 'POST',
|
||||
contentType: 'application/json',
|
||||
body: { i: 'TOKEN', token: 'aaa', password: 'aaa' }
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
credentials: 'omit',
|
||||
cache: 'no-cache',
|
||||
body: JSON.stringify({ token: 'aaa', password: 'aaa', i: 'TOKEN' }),
|
||||
});
|
||||
|
||||
fetchMock.mockRestore();
|
||||
});
|
||||
|
||||
test('インスタンスの credential が指定されていても引数で credential が null ならば null としてリクエストされる', async () => {
|
||||
fetchMock.resetMocks();
|
||||
fetchMock.mockResponse(async (req) => {
|
||||
const body = await req.json();
|
||||
if (req.method == 'POST' && req.url == 'https://misskey.test/api/i') {
|
||||
if (typeof body.i === 'string') {
|
||||
return JSON.stringify({ id: 'foo' });
|
||||
} else {
|
||||
return {
|
||||
status: 401,
|
||||
body: JSON.stringify({
|
||||
error: {
|
||||
message: 'Credential required.',
|
||||
code: 'CREDENTIAL_REQUIRED',
|
||||
id: '1384574d-a912-4b81-8601-c7b1c4085df1',
|
||||
}
|
||||
})
|
||||
};
|
||||
const fetchMock = vi
|
||||
.spyOn(globalThis, 'fetch')
|
||||
.mockImplementation(async (url, options) => {
|
||||
if (url === 'https://misskey.test/api/i' && options?.method === 'POST') {
|
||||
if (options.body) {
|
||||
const body = JSON.parse(options.body as string);
|
||||
if (typeof body.i === 'string') {
|
||||
return new Response(JSON.stringify({ id: 'foo' }), { status: 200 });
|
||||
} else {
|
||||
return new Response(JSON.stringify({
|
||||
error: {
|
||||
message: 'Credential required.',
|
||||
code: 'CREDENTIAL_REQUIRED',
|
||||
id: '1384574d-a912-4b81-8601-c7b1c4085df1',
|
||||
}
|
||||
}), { status: 401 });
|
||||
}
|
||||
}
|
||||
return new Response(null, { status: 400 });
|
||||
}
|
||||
} else {
|
||||
return { status: 404 };
|
||||
}
|
||||
});
|
||||
return new Response(null, { status: 404 });
|
||||
});
|
||||
|
||||
try {
|
||||
const cli = new APIClient({
|
||||
@@ -187,24 +196,24 @@ describe('API', () => {
|
||||
await cli.request('i', {}, null);
|
||||
} catch (e) {
|
||||
expect(isAPIError(e)).toEqual(true);
|
||||
} finally {
|
||||
fetchMock.mockRestore();
|
||||
}
|
||||
});
|
||||
|
||||
test('api error', async () => {
|
||||
fetchMock.resetMocks();
|
||||
fetchMock.mockResponse(async (req) => {
|
||||
return {
|
||||
status: 500,
|
||||
body: JSON.stringify({
|
||||
const fetchMock = vi
|
||||
.spyOn(globalThis, 'fetch')
|
||||
.mockImplementation(async () => {
|
||||
return new Response(JSON.stringify({
|
||||
error: {
|
||||
message: 'Internal error occurred. Please contact us if the error persists.',
|
||||
code: 'INTERNAL_ERROR',
|
||||
id: '5d37dbcb-891e-41ca-a3d6-e690c97775ac',
|
||||
kind: 'server',
|
||||
},
|
||||
})
|
||||
};
|
||||
});
|
||||
}), { status: 500 });
|
||||
});
|
||||
|
||||
try {
|
||||
const cli = new APIClient({
|
||||
@@ -216,12 +225,17 @@ describe('API', () => {
|
||||
} catch (e: any) {
|
||||
expect(isAPIError(e)).toEqual(true);
|
||||
expect(e.id).toEqual('5d37dbcb-891e-41ca-a3d6-e690c97775ac');
|
||||
} finally {
|
||||
fetchMock.mockRestore();
|
||||
}
|
||||
});
|
||||
|
||||
test('network error', async () => {
|
||||
fetchMock.resetMocks();
|
||||
fetchMock.mockAbort();
|
||||
const fetchMock = vi
|
||||
.spyOn(globalThis, 'fetch')
|
||||
.mockImplementation(async () => {
|
||||
throw new Error('Network error');
|
||||
});
|
||||
|
||||
try {
|
||||
const cli = new APIClient({
|
||||
@@ -232,17 +246,17 @@ describe('API', () => {
|
||||
await cli.request('i');
|
||||
} catch (e) {
|
||||
expect(isAPIError(e)).toEqual(false);
|
||||
} finally {
|
||||
fetchMock.mockRestore();
|
||||
}
|
||||
});
|
||||
|
||||
test('json parse error', async () => {
|
||||
fetchMock.resetMocks();
|
||||
fetchMock.mockResponse(async (req) => {
|
||||
return {
|
||||
status: 500,
|
||||
body: '<html>I AM NOT JSON</html>'
|
||||
};
|
||||
});
|
||||
const fetchMock = vi
|
||||
.spyOn(globalThis, 'fetch')
|
||||
.mockImplementation(async () => {
|
||||
return new Response('<html>I AM NOT JSON</html>', { status: 500 });
|
||||
});
|
||||
|
||||
try {
|
||||
const cli = new APIClient({
|
||||
@@ -253,18 +267,18 @@ describe('API', () => {
|
||||
await cli.request('i');
|
||||
} catch (e) {
|
||||
expect(isAPIError(e)).toEqual(false);
|
||||
} finally {
|
||||
fetchMock.mockRestore();
|
||||
}
|
||||
});
|
||||
|
||||
test('admin/roles/create の型が合う', async() => {
|
||||
fetchMock.resetMocks();
|
||||
fetchMock.mockResponse(async () => {
|
||||
return {
|
||||
const fetchMock = vi
|
||||
.spyOn(globalThis, 'fetch')
|
||||
.mockImplementation(async () => {
|
||||
// 本来返すべき値は`Role`型だが、テストなのでお茶を濁す
|
||||
status: 200,
|
||||
body: '{}'
|
||||
};
|
||||
});
|
||||
return new Response('{}', { status: 200 });
|
||||
});
|
||||
|
||||
const cli = new APIClient({
|
||||
origin: 'https://misskey.test',
|
||||
@@ -292,5 +306,7 @@ describe('API', () => {
|
||||
},
|
||||
target: 'manual',
|
||||
});
|
||||
|
||||
fetchMock.mockRestore();
|
||||
})
|
||||
});
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import WS from 'jest-websocket-mock';
|
||||
import { describe, test, expect } from 'vitest';
|
||||
import WS from 'vitest-websocket-mock';
|
||||
import Stream from '../src/streaming.js';
|
||||
|
||||
describe('Streaming', () => {
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import { defineConfig, configDefaults } from 'vitest/config';
|
||||
|
||||
export default defineConfig({
|
||||
test: {
|
||||
include: ['test/**/*.ts'],
|
||||
coverage: {
|
||||
exclude: [
|
||||
...configDefaults.coverage.exclude!,
|
||||
'src/autogen/**/*',
|
||||
'generator/**/*',
|
||||
'built/**/*',
|
||||
'test-d/**/*',
|
||||
'build.js',
|
||||
],
|
||||
}
|
||||
},
|
||||
});
|
||||
Generated
+94
-269
@@ -99,8 +99,8 @@ importers:
|
||||
specifier: 3.826.0
|
||||
version: 3.826.0(@aws-sdk/client-s3@3.826.0)
|
||||
'@discordapp/twemoji':
|
||||
specifier: 15.1.0
|
||||
version: 15.1.0
|
||||
specifier: 16.0.1
|
||||
version: 16.0.1
|
||||
'@fastify/accepts':
|
||||
specifier: 5.0.2
|
||||
version: 5.0.2
|
||||
@@ -168,8 +168,8 @@ importers:
|
||||
specifier: 1.12.0
|
||||
version: 1.12.0
|
||||
'@twemoji/parser':
|
||||
specifier: 15.1.1
|
||||
version: 15.1.1
|
||||
specifier: 16.0.0
|
||||
version: 16.0.0
|
||||
'@types/redis-info':
|
||||
specifier: 3.0.3
|
||||
version: 3.0.3
|
||||
@@ -294,8 +294,8 @@ importers:
|
||||
specifier: 0.51.0
|
||||
version: 0.51.0
|
||||
mfm-js:
|
||||
specifier: 0.24.0
|
||||
version: 0.24.0
|
||||
specifier: 0.25.0
|
||||
version: 0.25.0
|
||||
microformats-parser:
|
||||
specifier: 2.0.3
|
||||
version: 2.0.3
|
||||
@@ -707,8 +707,8 @@ importers:
|
||||
specifier: 1.1.0
|
||||
version: 1.1.0
|
||||
'@discordapp/twemoji':
|
||||
specifier: 15.1.0
|
||||
version: 15.1.0
|
||||
specifier: 16.0.1
|
||||
version: 16.0.1
|
||||
'@github/webauthn-json':
|
||||
specifier: 2.1.1
|
||||
version: 2.1.1
|
||||
@@ -734,8 +734,8 @@ importers:
|
||||
specifier: 0.19.0
|
||||
version: 0.19.0
|
||||
'@twemoji/parser':
|
||||
specifier: 15.1.1
|
||||
version: 15.1.1
|
||||
specifier: 16.0.0
|
||||
version: 16.0.0
|
||||
'@vitejs/plugin-vue':
|
||||
specifier: 5.2.4
|
||||
version: 5.2.4(vite@6.3.5(@types/node@22.16.4)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3))(vue@3.5.17(typescript@5.8.3))
|
||||
@@ -818,8 +818,8 @@ importers:
|
||||
specifier: 0.20.0
|
||||
version: 0.20.0
|
||||
mfm-js:
|
||||
specifier: 0.24.0
|
||||
version: 0.24.0
|
||||
specifier: 0.25.0
|
||||
version: 0.25.0
|
||||
misskey-bubble-game:
|
||||
specifier: workspace:*
|
||||
version: link:../misskey-bubble-game
|
||||
@@ -1080,8 +1080,8 @@ importers:
|
||||
packages/frontend-embed:
|
||||
dependencies:
|
||||
'@discordapp/twemoji':
|
||||
specifier: 15.1.0
|
||||
version: 15.1.0
|
||||
specifier: 16.0.1
|
||||
version: 16.0.1
|
||||
'@rollup/plugin-json':
|
||||
specifier: 6.1.0
|
||||
version: 6.1.0(rollup@4.45.1)
|
||||
@@ -1092,8 +1092,8 @@ importers:
|
||||
specifier: 5.2.0
|
||||
version: 5.2.0(rollup@4.45.1)
|
||||
'@twemoji/parser':
|
||||
specifier: 15.1.1
|
||||
version: 15.1.1
|
||||
specifier: 16.0.0
|
||||
version: 16.0.0
|
||||
'@vitejs/plugin-vue':
|
||||
specifier: 5.2.4
|
||||
version: 5.2.4(vite@6.3.5(@types/node@22.16.4)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3))(vue@3.5.17(typescript@5.8.3))
|
||||
@@ -1119,8 +1119,8 @@ importers:
|
||||
specifier: 2.2.3
|
||||
version: 2.2.3
|
||||
mfm-js:
|
||||
specifier: 0.24.0
|
||||
version: 0.24.0
|
||||
specifier: 0.25.0
|
||||
version: 0.25.0
|
||||
misskey-js:
|
||||
specifier: workspace:*
|
||||
version: link:../misskey-js
|
||||
@@ -1372,12 +1372,6 @@ importers:
|
||||
'@microsoft/api-extractor':
|
||||
specifier: 7.52.8
|
||||
version: 7.52.8(@types/node@22.16.4)
|
||||
'@swc/jest':
|
||||
specifier: 0.2.39
|
||||
version: 0.2.39(@swc/core@1.12.0)
|
||||
'@types/jest':
|
||||
specifier: 29.5.14
|
||||
version: 29.5.14
|
||||
'@types/node':
|
||||
specifier: 22.16.4
|
||||
version: 22.16.4
|
||||
@@ -1387,27 +1381,18 @@ importers:
|
||||
'@typescript-eslint/parser':
|
||||
specifier: 8.37.0
|
||||
version: 8.37.0(eslint@9.31.0)(typescript@5.8.3)
|
||||
'@vitest/coverage-v8':
|
||||
specifier: 3.2.4
|
||||
version: 3.2.4(vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.16.4)(happy-dom@17.6.3)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.4(@types/node@22.16.4)(typescript@5.8.3))(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3))
|
||||
esbuild:
|
||||
specifier: 0.25.6
|
||||
version: 0.25.6
|
||||
execa:
|
||||
specifier: 8.0.1
|
||||
version: 8.0.1
|
||||
specifier: 9.6.0
|
||||
version: 9.6.0
|
||||
glob:
|
||||
specifier: 11.0.3
|
||||
version: 11.0.3
|
||||
jest:
|
||||
specifier: 29.7.0
|
||||
version: 29.7.0(@types/node@22.16.4)
|
||||
jest-fetch-mock:
|
||||
specifier: 3.0.3
|
||||
version: 3.0.3(encoding@0.1.13)
|
||||
jest-websocket-mock:
|
||||
specifier: 2.5.0
|
||||
version: 2.5.0
|
||||
mock-socket:
|
||||
specifier: 9.3.1
|
||||
version: 9.3.1
|
||||
ncp:
|
||||
specifier: 2.0.0
|
||||
version: 2.0.0
|
||||
@@ -1420,12 +1405,18 @@ importers:
|
||||
typescript:
|
||||
specifier: 5.8.3
|
||||
version: 5.8.3
|
||||
vitest:
|
||||
specifier: 3.2.4
|
||||
version: 3.2.4(@types/debug@4.1.12)(@types/node@22.16.4)(happy-dom@17.6.3)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.4(@types/node@22.16.4)(typescript@5.8.3))(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3)
|
||||
vitest-websocket-mock:
|
||||
specifier: 0.5.0
|
||||
version: 0.5.0(vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.16.4)(happy-dom@17.6.3)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.4(@types/node@22.16.4)(typescript@5.8.3))(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3))
|
||||
|
||||
packages/misskey-js/generator:
|
||||
devDependencies:
|
||||
'@readme/openapi-parser':
|
||||
specifier: 2.7.0
|
||||
version: 2.7.0(openapi-types@12.1.3)
|
||||
specifier: 5.0.0
|
||||
version: 5.0.0(openapi-types@12.1.3)
|
||||
'@types/node':
|
||||
specifier: 22.16.4
|
||||
version: 22.16.4
|
||||
@@ -1543,8 +1534,9 @@ packages:
|
||||
'@analytics/type-utils@0.6.2':
|
||||
resolution: {integrity: sha512-TD+xbmsBLyYy/IxFimW/YL/9L2IEnM7/EoV9Aeh56U64Ify8o27HJcKjo38XY9Tcn0uOq1AX3thkKgvtWvwFQg==}
|
||||
|
||||
'@apidevtools/swagger-methods@3.0.2':
|
||||
resolution: {integrity: sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg==}
|
||||
'@apidevtools/json-schema-ref-parser@14.1.1':
|
||||
resolution: {integrity: sha512-uGF1YGOzzD50L7HLNWclXmsEhQflw8/zZHIz0/AzkJrKL5r9PceUipZxR/cp/8veTk4TVfdDJLyIwXLjaP5ePg==}
|
||||
engines: {node: '>= 20'}
|
||||
|
||||
'@asamuzakjp/css-color@2.8.3':
|
||||
resolution: {integrity: sha512-GIc76d9UI1hCvOATjZPyHFmE5qhRccp3/zGfMPapK3jBi+yocEzp6BBB0UnfRYP9NP4FANqUZYb0hnfs3TM3hw==}
|
||||
@@ -1964,8 +1956,8 @@ packages:
|
||||
resolution: {integrity: sha512-Ahk1N+s7urkgj7WvvUND5f8GiWEPfUw0D41hdElaqLgu8wZScI8gdI0q+qWw5N1d35x7GCRH2uk9mi+Uzo9M3g==}
|
||||
engines: {node: '>=14.0'}
|
||||
|
||||
'@discordapp/twemoji@15.1.0':
|
||||
resolution: {integrity: sha512-QdpV4ifTONAXvDjRrMohausZeGrQ1ac/Ox6togUh6Xl3XKJ/KAaMMuAEi0qsb0wDwoVTSZBll5Y6+N3hB2ktBw==}
|
||||
'@discordapp/twemoji@16.0.1':
|
||||
resolution: {integrity: sha512-figLiBWzjS5cyrAjLaGjM8AAaowO3qvK8rg5bA2dElB4qsaPMvBVlFDMO2d3x+nC1igt7kgWH4dvNmvvUHUF8w==}
|
||||
|
||||
'@emnapi/runtime@1.4.3':
|
||||
resolution: {integrity: sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ==}
|
||||
@@ -2567,10 +2559,6 @@ packages:
|
||||
resolution: {integrity: sha512-4QqS3LY5PBmTRHj9sAg1HLoPzqAI0uOX6wI/TRqHIcOxlFidy6YEmCQJk6FSZjNLGCeubDMfmkWL+qaLKhSGQA==}
|
||||
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||
|
||||
'@jest/create-cache-key-function@30.0.2':
|
||||
resolution: {integrity: sha512-AwlDAHwEHDi+etw9vKWx9HeIApVos8GD/sSTpHtDkqhm9OWuEUPKKPP6EaS17yv0GSzBB3TeeJFLyJ5LPjRqWg==}
|
||||
engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
|
||||
|
||||
'@jest/environment@29.7.0':
|
||||
resolution: {integrity: sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==}
|
||||
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||
@@ -2591,10 +2579,6 @@ packages:
|
||||
resolution: {integrity: sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==}
|
||||
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||
|
||||
'@jest/pattern@30.0.1':
|
||||
resolution: {integrity: sha512-gWp7NfQW27LaBQz3TITS8L7ZCQ0TLvtmI//4OwlQRx4rnWxcPNIYjxZpDcN4+UlGxgm3jS5QPz8IPTCkb59wZA==}
|
||||
engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
|
||||
|
||||
'@jest/reporters@29.7.0':
|
||||
resolution: {integrity: sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==}
|
||||
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||
@@ -2608,10 +2592,6 @@ packages:
|
||||
resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==}
|
||||
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||
|
||||
'@jest/schemas@30.0.1':
|
||||
resolution: {integrity: sha512-+g/1TKjFuGrf1Hh0QPCv0gISwBxJ+MQSNXmG9zjHy7BmFhtoJ9fdNhWJp3qUKRi93AOZHXtdxZgJ1vAtz6z65w==}
|
||||
engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
|
||||
|
||||
'@jest/source-map@29.6.3':
|
||||
resolution: {integrity: sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==}
|
||||
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||
@@ -2632,10 +2612,6 @@ packages:
|
||||
resolution: {integrity: sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==}
|
||||
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||
|
||||
'@jest/types@30.0.1':
|
||||
resolution: {integrity: sha512-HGwoYRVF0QSKJu1ZQX0o5ZrUrrhj0aOOFA8hXrumD7SIzjouevhawbTjmXdwOmURdGluU9DM/XvGm3NyFoiQjw==}
|
||||
engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
|
||||
|
||||
'@joshwooding/vite-plugin-react-docgen-typescript@0.5.0':
|
||||
resolution: {integrity: sha512-qYDdL7fPwLRI+bJNurVcis+tNgJmvWjH4YTBGXTA8xMuxFrnAz6E5o35iyzyKbq5J5Lr8mJGfrR5GXl+WGwhgQ==}
|
||||
peerDependencies:
|
||||
@@ -2645,29 +2621,21 @@ packages:
|
||||
typescript:
|
||||
optional: true
|
||||
|
||||
'@jridgewell/gen-mapping@0.3.5':
|
||||
resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==}
|
||||
engines: {node: '>=6.0.0'}
|
||||
'@jridgewell/gen-mapping@0.3.12':
|
||||
resolution: {integrity: sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==}
|
||||
|
||||
'@jridgewell/resolve-uri@3.1.0':
|
||||
resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==}
|
||||
engines: {node: '>=6.0.0'}
|
||||
|
||||
'@jridgewell/set-array@1.2.1':
|
||||
resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==}
|
||||
engines: {node: '>=6.0.0'}
|
||||
|
||||
'@jridgewell/source-map@0.3.6':
|
||||
resolution: {integrity: sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==}
|
||||
|
||||
'@jridgewell/sourcemap-codec@1.5.0':
|
||||
resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==}
|
||||
|
||||
'@jridgewell/trace-mapping@0.3.25':
|
||||
resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==}
|
||||
|
||||
'@jsdevtools/ono@7.1.3':
|
||||
resolution: {integrity: sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==}
|
||||
'@jridgewell/trace-mapping@0.3.29':
|
||||
resolution: {integrity: sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==}
|
||||
|
||||
'@kurkle/color@0.3.2':
|
||||
resolution: {integrity: sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw==}
|
||||
@@ -3255,19 +3223,15 @@ packages:
|
||||
'@prisma/instrumentation@5.22.0':
|
||||
resolution: {integrity: sha512-LxccF392NN37ISGxIurUljZSh1YWnphO34V5a0+T7FVQG2u9bhAXRTJpgmQ3483woVhkraQZFF7cbRrpbw/F4Q==}
|
||||
|
||||
'@readme/better-ajv-errors@2.2.2':
|
||||
resolution: {integrity: sha512-YBWor3QZhavGHqTEHJfYfk2e0UczQaImc0MIIJ5JNJrns4HKm94pwUdjkPVZO2eZJBt/Rzs/D8ZrdvFFwRh0zQ==}
|
||||
'@readme/better-ajv-errors@2.3.2':
|
||||
resolution: {integrity: sha512-T4GGnRAlY3C339NhoUpgJJFsMYko9vIgFAlhgV+/vEGFw66qEY4a4TRJIAZBcX/qT1pq5DvXSme+SQODHOoBrw==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
ajv: 4.11.8 - 8
|
||||
|
||||
'@readme/json-schema-ref-parser@1.2.0':
|
||||
resolution: {integrity: sha512-Bt3QVovFSua4QmHa65EHUmh2xS0XJ3rgTEUPH998f4OW4VVJke3BuS16f+kM0ZLOGdvIrzrPRqwihuv5BAjtrA==}
|
||||
deprecated: This package is no longer maintained. Please use `@apidevtools/json-schema-ref-parser` instead.
|
||||
|
||||
'@readme/openapi-parser@2.7.0':
|
||||
resolution: {integrity: sha512-P8WSr8WTOxilnT89tcCRKWYsG/II4sAwt1a/DIWub8xTtkrG9cCBBy/IUcvc5X8oGWN82MwcTA3uEkDrXZd/7A==}
|
||||
engines: {node: '>=18'}
|
||||
'@readme/openapi-parser@5.0.0':
|
||||
resolution: {integrity: sha512-dO3yReohn5lqsJQ4tyXpc6grjab4a4fybUY2yxkqOmWP++2QP3Q7wIj2BwABsdU72KMCfNCRyV6VTuxxnKKuMA==}
|
||||
engines: {node: '>=20'}
|
||||
peerDependencies:
|
||||
openapi-types: '>=7'
|
||||
|
||||
@@ -3572,9 +3536,6 @@ packages:
|
||||
'@sinclair/typebox@0.27.8':
|
||||
resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==}
|
||||
|
||||
'@sinclair/typebox@0.34.37':
|
||||
resolution: {integrity: sha512-2TRuQVgQYfy+EzHRTIvkhv2ADEouJ2xNS/Vq+W5EuuewBdOrvATvljZTxHWZSTYr2sTjTHpGvucaGAt67S2akw==}
|
||||
|
||||
'@sindresorhus/is@5.6.0':
|
||||
resolution: {integrity: sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==}
|
||||
engines: {node: '>=14.16'}
|
||||
@@ -4166,12 +4127,6 @@ packages:
|
||||
peerDependencies:
|
||||
'@swc/core': '*'
|
||||
|
||||
'@swc/jest@0.2.39':
|
||||
resolution: {integrity: sha512-eyokjOwYd0Q8RnMHri+8/FS1HIrIUKK/sRrFp8c1dThUOfNeCWbLmBP1P5VsKdvmkd25JaH+OKYwEYiAYg9YAA==}
|
||||
engines: {npm: '>= 7.0.0'}
|
||||
peerDependencies:
|
||||
'@swc/core': '*'
|
||||
|
||||
'@swc/types@0.1.22':
|
||||
resolution: {integrity: sha512-D13mY/ZA4PPEFSy6acki9eBT/3WgjMoRqNcdpIvjaYLQ44Xk5BdaL7UkDxAh6Z9UOe7tCCp67BVmZCojYp9owg==}
|
||||
|
||||
@@ -4270,14 +4225,8 @@ packages:
|
||||
resolution: {integrity: sha512-oKarNCN1QUhG148M88mtZdOlBZWWGcInquef+U8QL7gwJkRuNo5WS45Fjsd+3hM9cDJWGpqSZ4Oo097KDx4IWA==}
|
||||
engines: {node: '>=14.17'}
|
||||
|
||||
'@twemoji/parser@15.0.0':
|
||||
resolution: {integrity: sha512-lh9515BNsvKSNvyUqbj5yFu83iIDQ77SwVcsN/SnEGawczhsKU6qWuogewN1GweTi5Imo5ToQ9s+nNTf97IXvg==}
|
||||
|
||||
'@twemoji/parser@15.1.0':
|
||||
resolution: {integrity: sha512-3HTiSxPvkWUJ4kZeCvwyKlIwkpTUfBOk6igpBBRQni58ceQMv5YK4smkc8vX/eqOlMMNER/9qobv+Q6Q8LVrqA==}
|
||||
|
||||
'@twemoji/parser@15.1.1':
|
||||
resolution: {integrity: sha512-CChRzIu6ngkCJOmURBlYEdX5DZSu+bBTtqR60XjBkFrmvplKW7OQsea+i8XwF4bLVlUXBO7ZmHhRPDzfQyLwwg==}
|
||||
'@twemoji/parser@16.0.0':
|
||||
resolution: {integrity: sha512-jmuIjkp3OIaEemwMy3sArBwZSuZkRqmueGwRe2Zk4cFzbUJISFBJSZLDUUBNIgq3c+nY49ideYN2OiII6JUqwA==}
|
||||
|
||||
'@types/accepts@1.3.7':
|
||||
resolution: {integrity: sha512-Pay9fq2lM2wXPWbteBsRAGiWH2hig4ZE2asK+mm7kUzlxRTfL961rj89I6zV/E3PcIkDqyuBEcMxFT7rccugeQ==}
|
||||
@@ -5413,9 +5362,6 @@ packages:
|
||||
resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
call-me-maybe@1.0.2:
|
||||
resolution: {integrity: sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==}
|
||||
|
||||
callsites@3.1.0:
|
||||
resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
|
||||
engines: {node: '>=6'}
|
||||
@@ -5803,9 +5749,6 @@ packages:
|
||||
engines: {node: '>=10.14', npm: '>=6', yarn: '>=1'}
|
||||
hasBin: true
|
||||
|
||||
cross-fetch@3.1.6:
|
||||
resolution: {integrity: sha512-riRvo06crlE8HiqOwIpQhxwdOk4fOeR7FVM/wXoxchFEqMNUjvbs3bfo4OTgMEMHzppd4DxFBDbyySj8Cv781g==}
|
||||
|
||||
cross-fetch@4.1.0:
|
||||
resolution: {integrity: sha512-uKm5PU+MHTootlWEY+mZ4vvXoCn4fLQxT9dSc1sXVMSFkINTJVN8cAQROpwcKm8bJ/c7rgZVIBWzH5T78sNZZw==}
|
||||
|
||||
@@ -7483,9 +7426,6 @@ packages:
|
||||
resolution: {integrity: sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==}
|
||||
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||
|
||||
jest-fetch-mock@3.0.3:
|
||||
resolution: {integrity: sha512-Ux1nWprtLrdrH4XwE7O7InRY6psIi3GOsqNESJgMJ+M5cv4A8Lh7SN9d2V2kKRZ8ebAfcd1LNyZguAOb6JiDqw==}
|
||||
|
||||
jest-get-type@29.6.3:
|
||||
resolution: {integrity: sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==}
|
||||
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||
@@ -7523,10 +7463,6 @@ packages:
|
||||
resolution: {integrity: sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==}
|
||||
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||
|
||||
jest-regex-util@30.0.1:
|
||||
resolution: {integrity: sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA==}
|
||||
engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
|
||||
|
||||
jest-resolve-dependencies@29.7.0:
|
||||
resolution: {integrity: sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==}
|
||||
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||
@@ -7559,9 +7495,6 @@ packages:
|
||||
resolution: {integrity: sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==}
|
||||
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||
|
||||
jest-websocket-mock@2.5.0:
|
||||
resolution: {integrity: sha512-a+UJGfowNIWvtIKIQBHoEWIUqRxxQHFx4CXT+R5KxxKBtEQ5rS3pPOV/5299sHzqbmeCzxxY5qE4+yfXePePig==}
|
||||
|
||||
jest-worker@29.7.0:
|
||||
resolution: {integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==}
|
||||
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||
@@ -7986,8 +7919,8 @@ packages:
|
||||
resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==}
|
||||
engines: {node: '>= 0.6'}
|
||||
|
||||
mfm-js@0.24.0:
|
||||
resolution: {integrity: sha512-6m8N0ElH9/4CA1izhVqmxTfLj5Z9RspdqM/lMew4xU/UTgm4Pf//VpDunpasxbRFjeJSVW+zoVwL4ZPfPtfiQg==}
|
||||
mfm-js@0.25.0:
|
||||
resolution: {integrity: sha512-JoK5TOtswXIvZSZ9hUEL+ZkcNV4onu/DtkaKeXK846+sJBBF8DvxYmPutt7nPaRDsUMmJGr64PNVMFpMGdk3hw==}
|
||||
|
||||
microformats-parser@2.0.3:
|
||||
resolution: {integrity: sha512-MhZoQXkvWrvI3iu1IJ7Zk2VglphPt5P482E5rXvHalvtxhuS/QioMB8gEr8omt7Fd0K/qQEAbTmabvNpCbi3EQ==}
|
||||
@@ -9094,9 +9027,6 @@ packages:
|
||||
promise-limit@2.7.0:
|
||||
resolution: {integrity: sha512-7nJ6v5lnJsXwGprnGXga4wx6d1POjvi5Qmf1ivTRxTjH4Z/9Czja/UCMLVmB9N93GeWOU93XaFaEt6jbuoagNw==}
|
||||
|
||||
promise-polyfill@8.3.0:
|
||||
resolution: {integrity: sha512-H5oELycFml5yto/atYqmjyigJoAo3+OXwolYiH7OfQuYlAqhxNvTfiNMbV9hsC6Yp83yE5r2KTVmtrG6R9i6Pg==}
|
||||
|
||||
promise-retry@2.0.1:
|
||||
resolution: {integrity: sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==}
|
||||
engines: {node: '>=10'}
|
||||
@@ -10047,6 +9977,7 @@ packages:
|
||||
supertest@7.1.1:
|
||||
resolution: {integrity: sha512-aI59HBTlG9e2wTjxGJV+DygfNLgnWbGdZxiA/sgrnNNikIW8lbDvCtF6RnhZoJ82nU7qv7ZLjrvWqCEm52fAmw==}
|
||||
engines: {node: '>=14.18.0'}
|
||||
deprecated: Please upgrade to supertest v7.1.3+, see release notes at https://github.com/forwardemail/supertest/releases/tag/v7.1.3 - maintenance is supported by Forward Email @ https://forwardemail.net
|
||||
|
||||
supports-color@10.0.0:
|
||||
resolution: {integrity: sha512-HRVVSbCCMbj7/kdWF9Q+bbckjBHLtHMEoJWlkmYzzdwhYMkjkOwubLM6t7NbWKjgKamGDrWL1++KrjUO1t9oAQ==}
|
||||
@@ -10651,6 +10582,11 @@ packages:
|
||||
peerDependencies:
|
||||
vitest: '>=2.0.0'
|
||||
|
||||
vitest-websocket-mock@0.5.0:
|
||||
resolution: {integrity: sha512-vzBWeuF/kD/OCOFzB7WAclb7PxfI105qPkZtdOkPMwZdilBskQjJL4l319JtPtmeovDU7ZVhO3hTfGPjM4txQQ==}
|
||||
peerDependencies:
|
||||
vitest: '>=3'
|
||||
|
||||
vitest@3.2.4:
|
||||
resolution: {integrity: sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==}
|
||||
engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
|
||||
@@ -10717,8 +10653,8 @@ packages:
|
||||
vue-component-type-helpers@2.2.12:
|
||||
resolution: {integrity: sha512-YbGqHZ5/eW4SnkPNR44mKVc6ZKQoRs/Rux1sxC6rdwXb4qpbOSYfDr9DsTHolOTGmIKgM9j141mZbBeg05R1pw==}
|
||||
|
||||
vue-component-type-helpers@3.0.3:
|
||||
resolution: {integrity: sha512-koiBu7lO8e6w/UlbZAAIW11qcFQocYIl7Nh/SVwGZ804ej5KrncU32bRxi2zfU2Kyf6HWuk1CeeVP2rhIL+vyQ==}
|
||||
vue-component-type-helpers@3.0.4:
|
||||
resolution: {integrity: sha512-WtR3kPk8vqKYfCK/HGyT47lK/T3FaVyWxaCNuosaHYE8h9/k0lYRZ/PI/+T/z2wP+uuNKmL6z30rOcBboOu/YA==}
|
||||
|
||||
vue-demi@0.14.7:
|
||||
resolution: {integrity: sha512-EOG8KXDQNwkJILkx/gPcoL/7vH+hORoBaKgGe+6W7VFMvCYJfmF2dGbvgDroVnI8LU7/kTu8mbjRZGBU1z9NTA==}
|
||||
@@ -11025,8 +10961,8 @@ snapshots:
|
||||
|
||||
'@ampproject/remapping@2.3.0':
|
||||
dependencies:
|
||||
'@jridgewell/gen-mapping': 0.3.5
|
||||
'@jridgewell/trace-mapping': 0.3.25
|
||||
'@jridgewell/gen-mapping': 0.3.12
|
||||
'@jridgewell/trace-mapping': 0.3.29
|
||||
|
||||
'@analytics/cookie-utils@0.2.12':
|
||||
dependencies:
|
||||
@@ -11064,7 +11000,10 @@ snapshots:
|
||||
|
||||
'@analytics/type-utils@0.6.2': {}
|
||||
|
||||
'@apidevtools/swagger-methods@3.0.2': {}
|
||||
'@apidevtools/json-schema-ref-parser@14.1.1':
|
||||
dependencies:
|
||||
'@types/json-schema': 7.0.15
|
||||
js-yaml: 4.1.0
|
||||
|
||||
'@asamuzakjp/css-color@2.8.3':
|
||||
dependencies:
|
||||
@@ -11579,8 +11518,8 @@ snapshots:
|
||||
'@babel/generator@7.24.7':
|
||||
dependencies:
|
||||
'@babel/types': 7.28.1
|
||||
'@jridgewell/gen-mapping': 0.3.5
|
||||
'@jridgewell/trace-mapping': 0.3.25
|
||||
'@jridgewell/gen-mapping': 0.3.12
|
||||
'@jridgewell/trace-mapping': 0.3.29
|
||||
jsesc: 2.5.2
|
||||
|
||||
'@babel/helper-compilation-targets@7.24.7':
|
||||
@@ -11893,9 +11832,9 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- web-streams-polyfill
|
||||
|
||||
'@discordapp/twemoji@15.1.0':
|
||||
'@discordapp/twemoji@16.0.1':
|
||||
dependencies:
|
||||
'@twemoji/parser': 15.1.0
|
||||
'@twemoji/parser': 16.0.0
|
||||
fs-extra: 8.1.0
|
||||
jsonfile: 5.0.0
|
||||
universalify: 0.1.2
|
||||
@@ -12436,10 +12375,6 @@ snapshots:
|
||||
dependencies:
|
||||
'@jest/types': 29.6.3
|
||||
|
||||
'@jest/create-cache-key-function@30.0.2':
|
||||
dependencies:
|
||||
'@jest/types': 30.0.1
|
||||
|
||||
'@jest/environment@29.7.0':
|
||||
dependencies:
|
||||
'@jest/fake-timers': 29.7.0
|
||||
@@ -12476,11 +12411,6 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@jest/pattern@30.0.1':
|
||||
dependencies:
|
||||
'@types/node': 22.16.4
|
||||
jest-regex-util: 30.0.1
|
||||
|
||||
'@jest/reporters@29.7.0':
|
||||
dependencies:
|
||||
'@bcoe/v8-coverage': 0.2.3
|
||||
@@ -12488,7 +12418,7 @@ snapshots:
|
||||
'@jest/test-result': 29.7.0
|
||||
'@jest/transform': 29.7.0
|
||||
'@jest/types': 29.6.3
|
||||
'@jridgewell/trace-mapping': 0.3.25
|
||||
'@jridgewell/trace-mapping': 0.3.29
|
||||
'@types/node': 22.16.4
|
||||
chalk: 4.1.2
|
||||
collect-v8-coverage: 1.0.1
|
||||
@@ -12514,13 +12444,9 @@ snapshots:
|
||||
dependencies:
|
||||
'@sinclair/typebox': 0.27.8
|
||||
|
||||
'@jest/schemas@30.0.1':
|
||||
dependencies:
|
||||
'@sinclair/typebox': 0.34.37
|
||||
|
||||
'@jest/source-map@29.6.3':
|
||||
dependencies:
|
||||
'@jridgewell/trace-mapping': 0.3.25
|
||||
'@jridgewell/trace-mapping': 0.3.29
|
||||
callsites: 3.1.0
|
||||
graceful-fs: 4.2.11
|
||||
|
||||
@@ -12542,7 +12468,7 @@ snapshots:
|
||||
dependencies:
|
||||
'@babel/core': 7.24.7
|
||||
'@jest/types': 29.6.3
|
||||
'@jridgewell/trace-mapping': 0.3.25
|
||||
'@jridgewell/trace-mapping': 0.3.29
|
||||
babel-plugin-istanbul: 6.1.1
|
||||
chalk: 4.1.2
|
||||
convert-source-map: 2.0.0
|
||||
@@ -12567,16 +12493,6 @@ snapshots:
|
||||
'@types/yargs': 17.0.33
|
||||
chalk: 4.1.2
|
||||
|
||||
'@jest/types@30.0.1':
|
||||
dependencies:
|
||||
'@jest/pattern': 30.0.1
|
||||
'@jest/schemas': 30.0.1
|
||||
'@types/istanbul-lib-coverage': 2.0.6
|
||||
'@types/istanbul-reports': 3.0.4
|
||||
'@types/node': 22.16.4
|
||||
'@types/yargs': 17.0.33
|
||||
chalk: 4.1.2
|
||||
|
||||
'@joshwooding/vite-plugin-react-docgen-typescript@0.5.0(typescript@5.8.3)(vite@6.3.5(@types/node@22.16.4)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3))':
|
||||
dependencies:
|
||||
glob: 10.4.5
|
||||
@@ -12586,30 +12502,25 @@ snapshots:
|
||||
optionalDependencies:
|
||||
typescript: 5.8.3
|
||||
|
||||
'@jridgewell/gen-mapping@0.3.5':
|
||||
'@jridgewell/gen-mapping@0.3.12':
|
||||
dependencies:
|
||||
'@jridgewell/set-array': 1.2.1
|
||||
'@jridgewell/sourcemap-codec': 1.5.0
|
||||
'@jridgewell/trace-mapping': 0.3.25
|
||||
'@jridgewell/trace-mapping': 0.3.29
|
||||
|
||||
'@jridgewell/resolve-uri@3.1.0': {}
|
||||
|
||||
'@jridgewell/set-array@1.2.1': {}
|
||||
|
||||
'@jridgewell/source-map@0.3.6':
|
||||
dependencies:
|
||||
'@jridgewell/gen-mapping': 0.3.5
|
||||
'@jridgewell/trace-mapping': 0.3.25
|
||||
'@jridgewell/gen-mapping': 0.3.12
|
||||
'@jridgewell/trace-mapping': 0.3.29
|
||||
|
||||
'@jridgewell/sourcemap-codec@1.5.0': {}
|
||||
|
||||
'@jridgewell/trace-mapping@0.3.25':
|
||||
'@jridgewell/trace-mapping@0.3.29':
|
||||
dependencies:
|
||||
'@jridgewell/resolve-uri': 3.1.0
|
||||
'@jridgewell/sourcemap-codec': 1.5.0
|
||||
|
||||
'@jsdevtools/ono@7.1.3': {}
|
||||
|
||||
'@kurkle/color@0.3.2': {}
|
||||
|
||||
'@levischuck/tiny-cbor@0.2.2': {}
|
||||
@@ -13285,7 +13196,7 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@readme/better-ajv-errors@2.2.2(ajv@8.17.1)':
|
||||
'@readme/better-ajv-errors@2.3.2(ajv@8.17.1)':
|
||||
dependencies:
|
||||
'@babel/code-frame': 7.27.1
|
||||
'@babel/runtime': 7.27.0
|
||||
@@ -13295,23 +13206,14 @@ snapshots:
|
||||
leven: 3.1.0
|
||||
picocolors: 1.1.1
|
||||
|
||||
'@readme/json-schema-ref-parser@1.2.0':
|
||||
'@readme/openapi-parser@5.0.0(openapi-types@12.1.3)':
|
||||
dependencies:
|
||||
'@jsdevtools/ono': 7.1.3
|
||||
'@types/json-schema': 7.0.15
|
||||
call-me-maybe: 1.0.2
|
||||
js-yaml: 4.1.0
|
||||
|
||||
'@readme/openapi-parser@2.7.0(openapi-types@12.1.3)':
|
||||
dependencies:
|
||||
'@apidevtools/swagger-methods': 3.0.2
|
||||
'@jsdevtools/ono': 7.1.3
|
||||
'@readme/better-ajv-errors': 2.2.2(ajv@8.17.1)
|
||||
'@readme/json-schema-ref-parser': 1.2.0
|
||||
'@apidevtools/json-schema-ref-parser': 14.1.1
|
||||
'@readme/better-ajv-errors': 2.3.2(ajv@8.17.1)
|
||||
'@readme/openapi-schemas': 3.1.0
|
||||
'@types/json-schema': 7.0.15
|
||||
ajv: 8.17.1
|
||||
ajv-draft-04: 1.0.0(ajv@8.17.1)
|
||||
call-me-maybe: 1.0.2
|
||||
openapi-types: 12.1.3
|
||||
|
||||
'@readme/openapi-schemas@3.1.0': {}
|
||||
@@ -13646,8 +13548,6 @@ snapshots:
|
||||
|
||||
'@sinclair/typebox@0.27.8': {}
|
||||
|
||||
'@sinclair/typebox@0.34.37': {}
|
||||
|
||||
'@sindresorhus/is@5.6.0': {}
|
||||
|
||||
'@sindresorhus/is@7.0.1': {}
|
||||
@@ -14330,7 +14230,7 @@ snapshots:
|
||||
ts-dedent: 2.2.0
|
||||
type-fest: 2.19.0
|
||||
vue: 3.5.17(typescript@5.8.3)
|
||||
vue-component-type-helpers: 3.0.3
|
||||
vue-component-type-helpers: 3.0.4
|
||||
|
||||
'@stylistic/eslint-plugin@2.13.0(eslint@9.31.0)(typescript@5.8.3)':
|
||||
dependencies:
|
||||
@@ -14424,13 +14324,6 @@ snapshots:
|
||||
'@swc/counter': 0.1.3
|
||||
jsonc-parser: 3.2.0
|
||||
|
||||
'@swc/jest@0.2.39(@swc/core@1.12.0)':
|
||||
dependencies:
|
||||
'@jest/create-cache-key-function': 30.0.2
|
||||
'@swc/core': 1.12.0
|
||||
'@swc/counter': 0.1.3
|
||||
jsonc-parser: 3.2.0
|
||||
|
||||
'@swc/types@0.1.22':
|
||||
dependencies:
|
||||
'@swc/counter': 0.1.3
|
||||
@@ -14591,11 +14484,7 @@ snapshots:
|
||||
|
||||
'@tsd/typescript@5.8.3': {}
|
||||
|
||||
'@twemoji/parser@15.0.0': {}
|
||||
|
||||
'@twemoji/parser@15.1.0': {}
|
||||
|
||||
'@twemoji/parser@15.1.1': {}
|
||||
'@twemoji/parser@16.0.0': {}
|
||||
|
||||
'@types/accepts@1.3.7':
|
||||
dependencies:
|
||||
@@ -15709,7 +15598,7 @@ snapshots:
|
||||
|
||||
ast-v8-to-istanbul@0.3.3:
|
||||
dependencies:
|
||||
'@jridgewell/trace-mapping': 0.3.25
|
||||
'@jridgewell/trace-mapping': 0.3.29
|
||||
estree-walker: 3.0.3
|
||||
js-tokens: 9.0.1
|
||||
|
||||
@@ -16026,8 +15915,6 @@ snapshots:
|
||||
call-bind-apply-helpers: 1.0.2
|
||||
get-intrinsic: 1.3.0
|
||||
|
||||
call-me-maybe@1.0.2: {}
|
||||
|
||||
callsites@3.1.0: {}
|
||||
|
||||
camelcase-keys@6.2.2:
|
||||
@@ -16386,21 +16273,6 @@ snapshots:
|
||||
- supports-color
|
||||
- ts-node
|
||||
|
||||
create-jest@29.7.0(@types/node@22.16.4):
|
||||
dependencies:
|
||||
'@jest/types': 29.6.3
|
||||
chalk: 4.1.2
|
||||
exit: 0.1.2
|
||||
graceful-fs: 4.2.11
|
||||
jest-config: 29.7.0(@types/node@22.16.4)
|
||||
jest-util: 29.7.0
|
||||
prompts: 2.4.2
|
||||
transitivePeerDependencies:
|
||||
- '@types/node'
|
||||
- babel-plugin-macros
|
||||
- supports-color
|
||||
- ts-node
|
||||
|
||||
cron-parser@4.9.0:
|
||||
dependencies:
|
||||
luxon: 3.3.0
|
||||
@@ -16414,12 +16286,6 @@ snapshots:
|
||||
dependencies:
|
||||
cross-spawn: 7.0.6
|
||||
|
||||
cross-fetch@3.1.6(encoding@0.1.13):
|
||||
dependencies:
|
||||
node-fetch: 2.7.0(encoding@0.1.13)
|
||||
transitivePeerDependencies:
|
||||
- encoding
|
||||
|
||||
cross-fetch@4.1.0(encoding@0.1.13):
|
||||
dependencies:
|
||||
node-fetch: 2.7.0(encoding@0.1.13)
|
||||
@@ -18382,7 +18248,7 @@ snapshots:
|
||||
|
||||
istanbul-lib-source-maps@5.0.6:
|
||||
dependencies:
|
||||
'@jridgewell/trace-mapping': 0.3.25
|
||||
'@jridgewell/trace-mapping': 0.3.29
|
||||
debug: 4.4.1(supports-color@10.0.0)
|
||||
istanbul-lib-coverage: 3.2.2
|
||||
transitivePeerDependencies:
|
||||
@@ -18456,25 +18322,6 @@ snapshots:
|
||||
- supports-color
|
||||
- ts-node
|
||||
|
||||
jest-cli@29.7.0(@types/node@22.16.4):
|
||||
dependencies:
|
||||
'@jest/core': 29.7.0
|
||||
'@jest/test-result': 29.7.0
|
||||
'@jest/types': 29.6.3
|
||||
chalk: 4.1.2
|
||||
create-jest: 29.7.0(@types/node@22.16.4)
|
||||
exit: 0.1.2
|
||||
import-local: 3.1.0
|
||||
jest-config: 29.7.0(@types/node@22.16.4)
|
||||
jest-util: 29.7.0
|
||||
jest-validate: 29.7.0
|
||||
yargs: 17.7.2
|
||||
transitivePeerDependencies:
|
||||
- '@types/node'
|
||||
- babel-plugin-macros
|
||||
- supports-color
|
||||
- ts-node
|
||||
|
||||
jest-config@29.7.0(@types/node@22.15.31):
|
||||
dependencies:
|
||||
'@babel/core': 7.24.7
|
||||
@@ -18563,13 +18410,6 @@ snapshots:
|
||||
jest-mock: 29.7.0
|
||||
jest-util: 29.7.0
|
||||
|
||||
jest-fetch-mock@3.0.3(encoding@0.1.13):
|
||||
dependencies:
|
||||
cross-fetch: 3.1.6(encoding@0.1.13)
|
||||
promise-polyfill: 8.3.0
|
||||
transitivePeerDependencies:
|
||||
- encoding
|
||||
|
||||
jest-get-type@29.6.3: {}
|
||||
|
||||
jest-haste-map@29.7.0:
|
||||
@@ -18624,8 +18464,6 @@ snapshots:
|
||||
|
||||
jest-regex-util@29.6.3: {}
|
||||
|
||||
jest-regex-util@30.0.1: {}
|
||||
|
||||
jest-resolve-dependencies@29.7.0:
|
||||
dependencies:
|
||||
jest-regex-util: 29.6.3
|
||||
@@ -18752,11 +18590,6 @@ snapshots:
|
||||
jest-util: 29.7.0
|
||||
string-length: 4.0.2
|
||||
|
||||
jest-websocket-mock@2.5.0:
|
||||
dependencies:
|
||||
jest-diff: 29.7.0
|
||||
mock-socket: 9.3.1
|
||||
|
||||
jest-worker@29.7.0:
|
||||
dependencies:
|
||||
'@types/node': 22.16.4
|
||||
@@ -18776,18 +18609,6 @@ snapshots:
|
||||
- supports-color
|
||||
- ts-node
|
||||
|
||||
jest@29.7.0(@types/node@22.16.4):
|
||||
dependencies:
|
||||
'@jest/core': 29.7.0
|
||||
'@jest/types': 29.6.3
|
||||
import-local: 3.1.0
|
||||
jest-cli: 29.7.0(@types/node@22.16.4)
|
||||
transitivePeerDependencies:
|
||||
- '@types/node'
|
||||
- babel-plugin-macros
|
||||
- supports-color
|
||||
- ts-node
|
||||
|
||||
jju@1.4.0: {}
|
||||
|
||||
joi@17.13.3:
|
||||
@@ -19284,9 +19105,9 @@ snapshots:
|
||||
|
||||
methods@1.1.2: {}
|
||||
|
||||
mfm-js@0.24.0:
|
||||
mfm-js@0.25.0:
|
||||
dependencies:
|
||||
'@twemoji/parser': 15.0.0
|
||||
'@twemoji/parser': 16.0.0
|
||||
|
||||
microformats-parser@2.0.3:
|
||||
dependencies:
|
||||
@@ -20475,8 +20296,6 @@ snapshots:
|
||||
|
||||
promise-limit@2.7.0: {}
|
||||
|
||||
promise-polyfill@8.3.0: {}
|
||||
|
||||
promise-retry@2.0.1:
|
||||
dependencies:
|
||||
err-code: 2.0.3
|
||||
@@ -22115,7 +21934,7 @@ snapshots:
|
||||
|
||||
v8-to-istanbul@9.2.0:
|
||||
dependencies:
|
||||
'@jridgewell/trace-mapping': 0.3.25
|
||||
'@jridgewell/trace-mapping': 0.3.29
|
||||
'@types/istanbul-lib-coverage': 2.0.6
|
||||
convert-source-map: 2.0.0
|
||||
|
||||
@@ -22187,6 +22006,12 @@ snapshots:
|
||||
dependencies:
|
||||
vitest: 3.2.4(@types/debug@4.1.12)(@types/node@22.16.4)(happy-dom@17.6.3)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.4(@types/node@22.16.4)(typescript@5.8.3))(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3)
|
||||
|
||||
vitest-websocket-mock@0.5.0(vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.16.4)(happy-dom@17.6.3)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.4(@types/node@22.16.4)(typescript@5.8.3))(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3)):
|
||||
dependencies:
|
||||
'@vitest/utils': 3.2.4
|
||||
mock-socket: 9.3.1
|
||||
vitest: 3.2.4(@types/debug@4.1.12)(@types/node@22.16.4)(happy-dom@17.6.3)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.4(@types/node@22.16.4)(typescript@5.8.3))(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3)
|
||||
|
||||
vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.16.4)(happy-dom@17.6.3)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.4(@types/node@22.16.4)(typescript@5.8.3))(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3):
|
||||
dependencies:
|
||||
'@types/chai': 5.2.2
|
||||
@@ -22265,7 +22090,7 @@ snapshots:
|
||||
|
||||
vue-component-type-helpers@2.2.12: {}
|
||||
|
||||
vue-component-type-helpers@3.0.3: {}
|
||||
vue-component-type-helpers@3.0.4: {}
|
||||
|
||||
vue-demi@0.14.7(vue@3.5.17(typescript@5.8.3)):
|
||||
dependencies:
|
||||
|
||||
Reference in New Issue
Block a user