From 63d8b7986b077124ea2c20efd248aa141a65b1ea Mon Sep 17 00:00:00 2001 From: tamaina <tamaina@hotmail.co.jp> Date: Tue, 3 Jan 2023 14:58:10 +0000 Subject: [PATCH 01/60] do `yarn cache clean --all` in clean-all.js --- scripts/clean-all.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/scripts/clean-all.js b/scripts/clean-all.js index c65a1c3a32..563b6bc922 100644 --- a/scripts/clean-all.js +++ b/scripts/clean-all.js @@ -1,3 +1,4 @@ +const { execSync } = require('child_process'); const fs = require('fs'); (async () => { @@ -12,5 +13,9 @@ const fs = require('fs'); fs.rmSync(__dirname + '/../built', { recursive: true, force: true }); fs.rmSync(__dirname + '/../node_modules', { recursive: true, force: true }); - fs.rmSync(__dirname + '/../.yarn/cache', { recursive: true, force: true }); + + execSync('yarn cache clean --all', { + cwd: __dirname + '/../', + stdio: 'inherit', + }); })(); From c89410cab0ffb6a089a4ea442f2e33407864d6fd Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Wed, 4 Jan 2023 08:32:44 +0900 Subject: [PATCH 02/60] :art: --- packages/frontend/src/components/MkMenu.vue | 1 + packages/frontend/src/components/form/input.vue | 4 ++-- packages/frontend/src/components/form/select.vue | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/frontend/src/components/MkMenu.vue b/packages/frontend/src/components/MkMenu.vue index 263030e015..e9076138c6 100644 --- a/packages/frontend/src/components/MkMenu.vue +++ b/packages/frontend/src/components/MkMenu.vue @@ -217,6 +217,7 @@ onBeforeUnmount(() => { content: ""; display: block; position: absolute; + z-index: -1; top: 0; left: 0; right: 0; diff --git a/packages/frontend/src/components/form/input.vue b/packages/frontend/src/components/form/input.vue index 4f3e50c31a..f72e1429f5 100644 --- a/packages/frontend/src/components/form/input.vue +++ b/packages/frontend/src/components/form/input.vue @@ -78,8 +78,8 @@ const inputEl = shallowRef<HTMLElement>(); const prefixEl = shallowRef<HTMLElement>(); const suffixEl = shallowRef<HTMLElement>(); const height = - props.small ? 35 : - props.large ? 39 : + props.small ? 34 : + props.large ? 40 : 37; const focus = () => inputEl.value.focus(); diff --git a/packages/frontend/src/components/form/select.vue b/packages/frontend/src/components/form/select.vue index 2cd5ae6f4a..c8cdd9e508 100644 --- a/packages/frontend/src/components/form/select.vue +++ b/packages/frontend/src/components/form/select.vue @@ -64,8 +64,8 @@ const prefixEl = ref(null); const suffixEl = ref(null); const container = ref(null); const height = - props.small ? 35 : - props.large ? 39 : + props.small ? 34 : + props.large ? 40 : 37; const focus = () => inputEl.value.focus(); From 697836c17cc41ee8dff7d4daca51a89d9b0bd319 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Wed, 4 Jan 2023 08:54:54 +0900 Subject: [PATCH 03/60] perf(client): improve MkTime performance --- packages/frontend/src/components/global/MkTime.vue | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/frontend/src/components/global/MkTime.vue b/packages/frontend/src/components/global/MkTime.vue index 0bbb0f5399..704e6d0de0 100644 --- a/packages/frontend/src/components/global/MkTime.vue +++ b/packages/frontend/src/components/global/MkTime.vue @@ -37,12 +37,13 @@ const relative = $computed(() => { }); function tick() { - // TODO: パフォーマンス向上のため、このコンポーネントが画面内に表示されている場合のみ更新する now = new Date(); + const ago = (now.getTime() - _time.getTime()) / 1000/*ms*/; + const next = ago < 60 ? 10000 : ago < 3600 ? 60000 : 180000; tickId = window.setTimeout(() => { window.requestAnimationFrame(tick); - }, 10000); + }, next); } let tickId: number; From 5906de5ca0e4ac7a96e4b1e0b722b6763cfeab4d Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Wed, 4 Jan 2023 09:52:49 +0900 Subject: [PATCH 04/60] :art: --- packages/frontend/src/components/MkSuperMenu.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frontend/src/components/MkSuperMenu.vue b/packages/frontend/src/components/MkSuperMenu.vue index e79794aea4..36960bfc31 100644 --- a/packages/frontend/src/components/MkSuperMenu.vue +++ b/packages/frontend/src/components/MkSuperMenu.vue @@ -62,7 +62,7 @@ export default defineComponent({ align-items: center; width: 100%; box-sizing: border-box; - padding: 10px 16px 10px 8px; + padding: 9px 16px 9px 8px; border-radius: 9px; font-size: 0.9em; From 1ed078d7f9ac04956ed99cad832c0b78f49cdd33 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Wed, 4 Jan 2023 17:41:09 +0900 Subject: [PATCH 05/60] fix(server): fix pages/update --- packages/backend/src/server/api/endpoints/pages/update.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/src/server/api/endpoints/pages/update.ts b/packages/backend/src/server/api/endpoints/pages/update.ts index 4db0f80b26..35b402ec56 100644 --- a/packages/backend/src/server/api/endpoints/pages/update.ts +++ b/packages/backend/src/server/api/endpoints/pages/update.ts @@ -111,7 +111,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { updatedAt: new Date(), title: ps.title, name: ps.name === undefined ? page.name : ps.name, - summary: ps.name === undefined ? page.summary : ps.summary, + summary: ps.summary === undefined ? page.summary : ps.summary, content: ps.content, variables: ps.variables, script: ps.script, From d306db4ff8a79c674c44f6ad1013d657e4f71136 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Wed, 4 Jan 2023 17:41:39 +0900 Subject: [PATCH 06/60] =?UTF-8?q?fix(server):=20pages/like=E3=81=AE?= =?UTF-8?q?=E3=82=A8=E3=83=A9=E3=83=BCID=E3=81=8C=E9=87=8D=E8=A4=87?= =?UTF-8?q?=E3=81=97=E3=81=A6=E3=81=84=E3=82=8B=E3=81=AE=E3=82=92=E4=BF=AE?= =?UTF-8?q?=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/backend/src/server/api/endpoints/pages/like.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/src/server/api/endpoints/pages/like.ts b/packages/backend/src/server/api/endpoints/pages/like.ts index 41a11d1a31..d27990f7e1 100644 --- a/packages/backend/src/server/api/endpoints/pages/like.ts +++ b/packages/backend/src/server/api/endpoints/pages/like.ts @@ -28,7 +28,7 @@ export const meta = { alreadyLiked: { message: 'The page has already been liked.', code: 'ALREADY_LIKED', - id: 'cc98a8a2-0dc3-4123-b198-62c71df18ed3', + id: 'd4c1edbe-7da2-4eae-8714-1acfd2d63941', }, }, } as const; From 9bec87d9a569b3235c78a111c864e901ad8dd8ee Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Wed, 4 Jan 2023 17:54:47 +0900 Subject: [PATCH 07/60] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0a5e566263..ce996f5acd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -73,6 +73,8 @@ You should also include the user name that made the change. - Server: trim long text of note from ap @syuilo - Server: Ap inboxの最大ペイロードサイズを64kbに制限 @syuilo - Server: アンテナの作成数上限を追加 @syuilo +- Server: pages/likeのエラーIDが重複しているのを修正 @syuilo +- Server: pages/updateのパラメータによってはsummaryの値が更新されないのを修正 @syuilo - Client: case insensitive emoji search @saschanaz - Client: InAppウィンドウが操作できなくなることがあるのを修正 @tamaina - Client: use proxied image for instance icon @syuilo From e02183bb1b14522c8ed7c4bb1cbd48f76026c208 Mon Sep 17 00:00:00 2001 From: tamaina <tamaina@hotmail.co.jp> Date: Wed, 4 Jan 2023 11:09:05 +0000 Subject: [PATCH 08/60] chore: remove unused function (truncateUnreadAntennaNote) --- .../src/core/PushNotificationService.ts | 20 ------------------- 1 file changed, 20 deletions(-) diff --git a/packages/backend/src/core/PushNotificationService.ts b/packages/backend/src/core/PushNotificationService.ts index 667dc9c1fa..b18b7bb2cd 100644 --- a/packages/backend/src/core/PushNotificationService.ts +++ b/packages/backend/src/core/PushNotificationService.ts @@ -47,26 +47,6 @@ function truncateBody<T extends keyof pushNotificationsTypes>(type: T, body: pus return body; } -function truncateUnreadAntennaNote(notification: pushNotificationsTypes['unreadAntennaNote']): pushNotificationsTypes['unreadAntennaNote'] { - if (notification.note) { - return { - ...notification, - note: { - ...notification.note, - // textをgetNoteSummaryしたものに置き換える - text: getNoteSummary(('type' in notification && notification.type === 'renote') ? notification.note.renote as Packed<'Note'> : notification.note), - - cw: undefined, - reply: undefined, - renote: undefined, - user: undefined as any, // 通知を受け取ったユーザーである場合が多いのでこれも捨てる アンテナの場合も不要なのでいらない - }, - }; - } - - return notification; -} - @Injectable() export class PushNotificationService { constructor( From e1fe06e5974df6a56345258c7b8bc2bf62c49178 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Wed, 4 Jan 2023 21:22:47 +0900 Subject: [PATCH 09/60] New Crowdin updates (#9458) * New translations ja-JP.yml (German) * New translations ja-JP.yml (English) * New translations ja-JP.yml (Thai) * New translations ja-JP.yml (Chinese Simplified) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Romanian) * New translations ja-JP.yml (French) * New translations ja-JP.yml (Spanish) * New translations ja-JP.yml (Arabic) * New translations ja-JP.yml (Catalan) * New translations ja-JP.yml (Czech) * New translations ja-JP.yml (German) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Korean) * New translations ja-JP.yml (Dutch) * New translations ja-JP.yml (Polish) * New translations ja-JP.yml (Portuguese) * New translations ja-JP.yml (Russian) * New translations ja-JP.yml (Slovak) * New translations ja-JP.yml (Swedish) * New translations ja-JP.yml (Ukrainian) * New translations ja-JP.yml (Chinese Simplified) * New translations ja-JP.yml (Chinese Traditional) * New translations ja-JP.yml (English) * New translations ja-JP.yml (Vietnamese) * New translations ja-JP.yml (Indonesian) * New translations ja-JP.yml (Bengali) * New translations ja-JP.yml (Thai) * New translations ja-JP.yml (Japanese, Kansai) * New translations ja-JP.yml (French) * New translations ja-JP.yml (French) * New translations ja-JP.yml (Korean) * New translations ja-JP.yml (Spanish) --- locales/ar-SA.yml | 1 - locales/bn-BD.yml | 1 - locales/ca-ES.yml | 1 - locales/cs-CZ.yml | 1 - locales/de-DE.yml | 2 +- locales/en-US.yml | 2 +- locales/es-ES.yml | 26 +++++++++++++++++++++++++- locales/fr-FR.yml | 26 +++++++++++++++++++++++++- locales/id-ID.yml | 1 - locales/it-IT.yml | 10 +++++++++- locales/ja-KS.yml | 1 - locales/ko-KR.yml | 3 ++- locales/nl-NL.yml | 1 - locales/pl-PL.yml | 1 - locales/pt-PT.yml | 1 - locales/ro-RO.yml | 1 - locales/ru-RU.yml | 1 - locales/sk-SK.yml | 1 - locales/sv-SE.yml | 1 - locales/th-TH.yml | 2 +- locales/uk-UA.yml | 1 - locales/vi-VN.yml | 1 - locales/zh-CN.yml | 5 +++-- locales/zh-TW.yml | 1 - 24 files changed, 67 insertions(+), 25 deletions(-) diff --git a/locales/ar-SA.yml b/locales/ar-SA.yml index eefb41007b..7a9c235235 100644 --- a/locales/ar-SA.yml +++ b/locales/ar-SA.yml @@ -164,7 +164,6 @@ annotation: "التعليقات" federation: "الفديرالية" instances: "مثيل الخادم" registeredAt: "مسجل منذ" -latestRequestSentAt: "آخر طلب أرسِل في" latestRequestReceivedAt: "آخر طلب تُلقي في" latestStatus: "الحالات الأخيرة" storageUsage: "مساحة التخزين المستخدمة" diff --git a/locales/bn-BD.yml b/locales/bn-BD.yml index 85ec1d9935..a56c06c9da 100644 --- a/locales/bn-BD.yml +++ b/locales/bn-BD.yml @@ -164,7 +164,6 @@ annotation: "মন্তব্য" federation: "ফেডিভার্স" instances: "ইন্সট্যান্স" registeredAt: "যোগ দিয়েছেন" -latestRequestSentAt: "শেষ রিকুয়েস্ট পাঠানো হয়েছে" latestRequestReceivedAt: "শেষ রিকুয়েস্ট গৃহীত হয়েছে" latestStatus: "সর্বশেষ অবস্থা" storageUsage: "স্টোরেজের ব্যাবহার" diff --git a/locales/ca-ES.yml b/locales/ca-ES.yml index 406fdff0b4..5127803ebc 100644 --- a/locales/ca-ES.yml +++ b/locales/ca-ES.yml @@ -165,7 +165,6 @@ annotation: "Comentaris" federation: "Federació" instances: "Servidors" registeredAt: "Registrat a" -latestRequestSentAt: "Darrera petició enviada" latestRequestReceivedAt: "Última petició rebuda" latestStatus: "Últim estat" storageUsage: "Emmagatzematge utilitzat" diff --git a/locales/cs-CZ.yml b/locales/cs-CZ.yml index 552b56a430..ebe866651c 100644 --- a/locales/cs-CZ.yml +++ b/locales/cs-CZ.yml @@ -161,7 +161,6 @@ annotation: "Komentáře" federation: "Federace" instances: "Instance" registeredAt: "Registrován" -latestRequestSentAt: "Poslední požadavek poslán" latestRequestReceivedAt: "Poslední požadavek přijat" latestStatus: "Poslední status" storageUsage: "Využití úložiště" diff --git a/locales/de-DE.yml b/locales/de-DE.yml index dfac4fb791..275e4677d6 100644 --- a/locales/de-DE.yml +++ b/locales/de-DE.yml @@ -167,7 +167,6 @@ annotation: "Anmerkung" federation: "Föderation" instances: "Instanzen" registeredAt: "Registriert am" -latestRequestSentAt: "Letzte Anfrage gesendet" latestRequestReceivedAt: "Letzte Anfrage erhalten" latestStatus: "Neuster Status" storageUsage: "Verbrauchter Speicherplatz" @@ -916,6 +915,7 @@ caption: "Beschreibung" loggedInAsBot: "Momentan als Bot angemeldet" tools: "Werkzeuge" cannotLoad: "Kann nicht geladen werden" +numberOfProfileView: "Profilaufrufe" _sensitiveMediaDetection: description: "Ermöglicht eine Erleichterung der Servermoderation durch die automatische Erkennungen von NSFW-Medien unter Verwendung von Machine Learning. Hierdurch wird die Serverlast etwas erhöht." sensitivity: "Erkennungssensitivität" diff --git a/locales/en-US.yml b/locales/en-US.yml index 414bc1df51..9ff4461522 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -167,7 +167,6 @@ annotation: "Comments" federation: "Federation" instances: "Instances" registeredAt: "Registered at" -latestRequestSentAt: "Last request sent" latestRequestReceivedAt: "Last request received" latestStatus: "Latest status" storageUsage: "Storage usage" @@ -916,6 +915,7 @@ caption: "Caption" loggedInAsBot: "Currently logged in as bot" tools: "Tools" cannotLoad: "Unable to load" +numberOfProfileView: "Profile views" _sensitiveMediaDetection: description: "Reduces the effort of server moderation through automatically recognizing NSFW media via Machine Learning. This will slightly increase the load on the server." sensitivity: "Detection sensitivity" diff --git a/locales/es-ES.yml b/locales/es-ES.yml index 50db3fe306..0a1157230a 100644 --- a/locales/es-ES.yml +++ b/locales/es-ES.yml @@ -13,6 +13,7 @@ fetchingAsApObject: "Buscando en el fediverso" ok: "OK" gotIt: "¡Lo tengo!" cancel: "Cancelar" +noThankYou: "No gracias" enterUsername: "Introduce el nombre de usuario" renotedBy: "Renotado por {user}" noNotes: "No hay notas" @@ -48,6 +49,7 @@ deleteAndEdit: "Borrar y editar" deleteAndEditConfirm: "¿Estás seguro de que quieres borrar esta nota y editarla? Perderás todas las reacciones, renotas y respuestas." addToList: "Agregar a lista" sendMessage: "Enviar un mensaje" +copyRSS: "Copiar RSS" copyUsername: "Copiar nombre de usuario" searchUser: "Buscar un usuario" reply: "Responder" @@ -165,7 +167,6 @@ annotation: "Anotación" federation: "Federación" instances: "Instancia" registeredAt: "Registrado en" -latestRequestSentAt: "Ultimo pedido enviado" latestRequestReceivedAt: "Ultimo pedido recibido" latestStatus: "Último status" storageUsage: "Almacenamiento usado" @@ -455,6 +456,8 @@ language: "Idioma" uiLanguage: "Idioma de visualización de la interfaz" groupInvited: "Invitado al grupo" aboutX: "Acerca de {x}" +emojiStyle: "Estilo de emoji" +native: "Nativo" disableDrawer: "No mostrar los menús en cajones" youHaveNoGroups: "Sin grupos" joinOrCreateGroup: "Obtenga una invitación para unirse al grupos o puede crear su propio grupo." @@ -713,6 +716,7 @@ accentColor: "Acento" textColor: "Texto" saveAs: "Guardar como…" advanced: "Avanzado" +advancedSettings: "Configuración avanzada" value: "Valores" createdAt: "Fecha de creación" updatedAt: "Actualizado" @@ -898,6 +902,20 @@ navbar: "Barra de navegación" shuffle: "Aleatorio" account: "Cuentas" move: "Mover" +pushNotification: "Alerta emergente" +subscribePushNotification: "Activar las notificaciones emergentes" +unsubscribePushNotification: "Desactivar las notificaciones emergentes" +pushNotificationAlreadySubscribed: "Notificaciones emergentes ya activadas" +pushNotificationNotSupported: "El navegador o la instancia no admiten notificaciones push" +sendPushNotificationReadMessage: "Eliminar las notificaciones push después de leer las notificaciones y los mensajes" +sendPushNotificationReadMessageCaption: "La notificación \"{emptyPushNotificationMessage}\" aparecerá momentáneamente. Esto puede aumentar el consumo de batería del dispositivo." +windowMaximize: "Maximizar" +windowRestore: "Regresar" +caption: "Pie de foto" +loggedInAsBot: "Inicio sesión como cuenta bot." +tools: "Utilidades" +cannotLoad: "No se puede cargar." +numberOfProfileView: "Número de vistas de perfil" _sensitiveMediaDetection: description: "Reduce el esfuerzo de la moderación el el servidor a través del reconocimiento automático de contenido NSFW usando 'Machine Learning'. Esto puede incrementar ligeramente la carga en el servidor." sensitivity: "Sensibilidad de detección" @@ -1208,6 +1226,9 @@ _tutorial: step7_1: "Así terminó la explicación del funcionamiento básico de Misskey. Eso fue todo." step7_2: "Si quieres conocer más sobre Misskey, prueba con la sección {help}." step7_3: "Así, disfruta de Misskey 🚀" + step8_1: "Por último, ¿por qué no activar las notificaciones emergentes?" + step8_2: "Al recibir notificaciones emergentes, estarás al tanto de reacciones, seguimientos y menciones incluso cuando Misskey no esté abierto." + step8_3: "La configuración de las notificaciones puede modificarse posteriormente." _2fa: alreadyRegistered: "Ya has completado la configuración." registerDevice: "Registrar dispositivo" @@ -1295,6 +1316,7 @@ _widgets: serverMetric: "Estadísticas del servidor" aiscript: "Consola de AiScript" aichan: "indigo" + userList: "Lista de usuarios" _userList: chooseList: "Seleccione una lista" _cw: @@ -1360,6 +1382,7 @@ _profile: changeBanner: "Cambiar banner" _exportOrImport: allNotes: "Todas las notas" + favoritedNotes: "Notas favoritas" followingList: "Siguiendo" muteList: "Silenciados" blockingList: "Bloqueados" @@ -1464,6 +1487,7 @@ _notification: yourFollowRequestAccepted: "Tu solicitud de seguimiento fue aceptada" youWereInvitedToGroup: "Invitado al grupo" pollEnded: "Estan disponibles los resultados de la encuesta" + unreadAntennaNote: "Antena {name}" emptyPushNotificationMessage: "Se han actualizado las notificaciones push" _types: all: "Todo" diff --git a/locales/fr-FR.yml b/locales/fr-FR.yml index 0df7ee2e12..35c490bcff 100644 --- a/locales/fr-FR.yml +++ b/locales/fr-FR.yml @@ -2,6 +2,7 @@ _lang_: "Français" headlineMisskey: "Réseau relié par des notes" introMisskey: "Bienvenue ! Misskey est un service de microblogage décentralisé, libre et ouvert.\nÉcrivez des « notes » et partagez ce qui se passe à l’instant présent, autour de vous avec les autres 📡\nLa fonction « réactions », vous permet également d’ajouter une réaction rapide aux notes des autres utilisateur·rice·s 👍\nExplorons un nouveau monde 🚀" +poweredByMisskeyDescription: "{nom} est l'un des services propulsés par la plateforme ouverte <b>Misskey</b> (appelée \"instance Misskey\")." monthAndDay: "{day}/{month}" search: "Rechercher" notifications: "Notifications" @@ -12,6 +13,7 @@ fetchingAsApObject: "Récupération depuis le fédiverse …" ok: "OK" gotIt: "J’ai compris !" cancel: "Annuler" +noThankYou: "Pas maintenant" enterUsername: "Entrer un nom d’utilisateur·rice" renotedBy: "Renoté par {user}" noNotes: "Aucune note" @@ -47,6 +49,7 @@ deleteAndEdit: "Supprimer et réécrire" deleteAndEditConfirm: "Êtes-vous sûr·e de vouloir supprimer cette note et la reformuler ? Vous perdrez toutes les réactions, renotes et réponses y afférentes." addToList: "Ajouter à une liste" sendMessage: "Envoyer un message" +copyRSS: "Copier le RSS" copyUsername: "Copier le nom d’utilisateur·rice" searchUser: "Chercher un·e utilisateur·rice" reply: "Répondre" @@ -143,6 +146,7 @@ flagAsBotDescription: "Si ce compte est géré de manière automatisée, choisis flagAsCat: "Ce compte est un chat" flagAsCatDescription: "Activer l'option \" Je suis un chat \" pour ce compte." flagShowTimelineReplies: "Afficher les réponses dans le fil" +flagShowTimelineRepliesDescription: "Affiche les réponses des utilisateurs aux notes des autres utilisateurs dans la timeline si cette option est activée." autoAcceptFollowed: "Accepter automatiquement les demandes d’abonnement venant d’utilisateur·rice·s que vous suivez" addAccount: "Ajouter un compte" loginFailed: "Échec de la connexion" @@ -163,7 +167,6 @@ annotation: "Commentaires" federation: "Fédération" instances: "Instance" registeredAt: "Premier contact le" -latestRequestSentAt: "Dernière requête envoyée" latestRequestReceivedAt: "Dernière requête reçue" latestStatus: "Dernier statut" storageUsage: "Stockage utilisé" @@ -453,6 +456,8 @@ language: "Langue" uiLanguage: "Langue d’affichage de l’interface" groupInvited: "Invité au groupe" aboutX: "À propos de {x}" +emojiStyle: "Style des émojis" +native: "Natif" disableDrawer: "Les menus ne s'affichent pas dans le tiroir" youHaveNoGroups: "Vous n’avez aucun groupe" joinOrCreateGroup: "Vous pouvez être invité·e à rejoindre des groupes existants ou créer votre propre nouveau groupe." @@ -600,6 +605,7 @@ smtpSecureInfo: "Désactiver cette option lorsque STARTTLS est utilisé" testEmail: "Tester la distribution de courriel" wordMute: "Filtre de mots" regexpError: "Erreur d’expression régulière" +regexpErrorDescription: "Une erreur s'est produite dans l'expression régulière sur la ligne {ligne} de votre mot muet {tab} :" instanceMute: "Instance en sourdine" userSaysSomething: "{name} a dit quelque chose" makeActive: "Activer" @@ -708,6 +714,7 @@ accentColor: "Accentuation" textColor: "Texte" saveAs: "Enregistrer sous ..." advanced: "Avancé" +advancedSettings: "Paramètres avancés" value: "Valeur" createdAt: "Date de création" updatedAt: "Mis à jour le" @@ -852,6 +859,7 @@ rateLimitExceeded: "Limite de taux dépassée" cropImage: "Recadrer l'image" cropImageAsk: "Voulez-vous recadrer cette image ?" file: "Fichiers" +recentNHours: "Dernières {n} heures" noEmailServerWarning: "Serveur de courrier non configuré." thereIsUnresolvedAbuseReportWarning: "Il n’y a aucun rapport non résolu." recommended: "Recommandé" @@ -891,6 +899,17 @@ navbar: "Barre de navigation" shuffle: "Lecture aléatoire" account: "Comptes" move: "Déplacer" +pushNotification: "Notifications push" +subscribePushNotification: "Autoriser les notifications push" +unsubscribePushNotification: "Désactiver les notifications push" +pushNotificationAlreadySubscribed: "Les notifications push sont déjà activées" +pushNotificationNotSupported: "Votre navigateur ou votre instance ne prend pas en charge les notifications push" +sendPushNotificationReadMessage: "Supprimer les notifications push une fois que les notifications ou messages pertinents ont été lus." +windowRestore: "Restaurer" +caption: "Libellé" +loggedInAsBot: "Connecté actuellement en tant que bot" +tools: "Outils" +cannotLoad: "Chargement impossible" _sensitiveMediaDetection: description: "L'apprentissage automatique peut être utilisé pour détecter automatiquement les médias sensibles à modérer. La sollicitation des serveurs augmente légèrement." sensitivity: "Sensibilité de la détection" @@ -1201,6 +1220,8 @@ _tutorial: step7_1: "Félicitations ! Vous avez atteint la fin du tutoriel de base pour l’utilisation de Misskey." step7_2: "Si vous désirez en savoir plus sur Misskey, jetez un œil sur la section {help}." step7_3: "Bon courage et amusez-vous bien sur Misskey ! 🚀" + step8_1: "Enfin, souhaitez-vous activer les notifications push ?" + step8_2: "En les activant, vous recevrez des notifications pour les mentions, les réactions, les suivis, etc., même lorsque Misskey n'est pas ouvert." _2fa: alreadyRegistered: "Configuration déjà achevée." registerDevice: "Ajouter un nouvel appareil" @@ -1287,6 +1308,7 @@ _widgets: serverMetric: "Statistiques du serveur" aiscript: "Console AiScript" aichan: "Ai" + userList: "Liste utilisateur" _userList: chooseList: "Sélectionner une liste" _cw: @@ -1456,6 +1478,7 @@ _notification: yourFollowRequestAccepted: "Votre demande d’abonnement a été accepté" youWereInvitedToGroup: "Invité·e au groupe" pollEnded: "Les résultats du sondage sont disponibles" + unreadAntennaNote: "Antenne {name}" emptyPushNotificationMessage: "Les notifications push ont été mises à jour" _types: all: "Toutes" @@ -1490,6 +1513,7 @@ _deck: newProfile: "Nouveau profil" deleteProfile: "Supprimer le profil" introduction: "Créez l’interface parfaite qui vous sied en arrangeant librement les colonnes !" + introduction2: "Cliquez sur le + à droite de l'écran pour ajouter de nouvelles colonnes quand vous le souhaitez." _columns: main: "Principale" widgets: "Widgets" diff --git a/locales/id-ID.yml b/locales/id-ID.yml index a73e108d7f..1fdba0dd40 100644 --- a/locales/id-ID.yml +++ b/locales/id-ID.yml @@ -164,7 +164,6 @@ annotation: "Keterangan konten" federation: "Federasi" instances: "Instansi" registeredAt: "Terdaftar" -latestRequestSentAt: "Permintaan terakhir dikirim pada" latestRequestReceivedAt: "Permintaan terakhir diterima pada" latestStatus: "Status terakhir" storageUsage: "Penggunaan penyimpanan" diff --git a/locales/it-IT.yml b/locales/it-IT.yml index 6b1b47f4e2..0298be19ea 100644 --- a/locales/it-IT.yml +++ b/locales/it-IT.yml @@ -49,6 +49,7 @@ deleteAndEdit: "Elimina e modifica" deleteAndEditConfirm: "Vuoi davvero cancellare questa nota e scriverla di nuovo? Verrano eliminate anche tutte le reazioni, Rinote e risposte collegate." addToList: "Aggiungi alla lista" sendMessage: "Invia messaggio" +copyRSS: "Copia RSS" copyUsername: "Copia nome utente" searchUser: "Cerca utente" reply: "Rispondi" @@ -166,7 +167,6 @@ annotation: "Descrizione" federation: "Federazione" instances: "Istanza" registeredAt: "Registrato presso" -latestRequestSentAt: "Ultima richiesta inviata" latestRequestReceivedAt: "Ultima richiesta ricevuta" latestStatus: "Ultimo stato" storageUsage: "Capienza dei dischi" @@ -456,6 +456,8 @@ language: "Lingua" uiLanguage: "Lingua di visualizzazione dell'interfaccia" groupInvited: "Invitat@ al gruppo" aboutX: "Informazioni su {x}" +emojiStyle: "Stile emoji" +native: "Nativo" disableDrawer: "Non mostrare il menù sul drawer" youHaveNoGroups: "Nessun gruppo" joinOrCreateGroup: "Puoi creare il tuo gruppo o essere invitat@ a gruppi che già esistono." @@ -714,6 +716,7 @@ accentColor: "Colore principale" textColor: "Testo" saveAs: "Salva con nome" advanced: "Avanzato" +advancedSettings: "Impostazioni avanzate" value: "Valore" createdAt: "Data di creazione" updatedAt: "Aggiornato il" @@ -910,6 +913,9 @@ windowMaximize: "Ingrandisci" windowRestore: "Ripristina" caption: "Didascalia" loggedInAsBot: "Connessione come Bot" +tools: "Strumenti" +cannotLoad: "Caricamento impossibile" +numberOfProfileView: "Visualizzazioni profilo" _sensitiveMediaDetection: description: "L'apprendimento automatico può essere utilizzato per individuare automaticamente i media sensibili da moderare. Il carico del server aumenta leggermente." sensitivity: "Sensibilità di rilevamento" @@ -1310,6 +1316,7 @@ _widgets: serverMetric: "Statistiche server" aiscript: "Console AiScript" aichan: "indaco (tintura)" + userList: "Elenco utenti" _userList: chooseList: "Seleziona una lista" _cw: @@ -1375,6 +1382,7 @@ _profile: changeBanner: "Cambia intestazione" _exportOrImport: allNotes: "Tutte le note" + favoritedNotes: "Note preferite" followingList: "Follows" muteList: "Elenco profili silenziati" blockingList: "Elenco profili bloccati" diff --git a/locales/ja-KS.yml b/locales/ja-KS.yml index 994fe9a195..6e1bed8269 100644 --- a/locales/ja-KS.yml +++ b/locales/ja-KS.yml @@ -167,7 +167,6 @@ annotation: "注釈" federation: "連合" instances: "インスタンス" registeredAt: "初観測" -latestRequestSentAt: "ちょっと前のリクエスト送信" latestRequestReceivedAt: "ちょっと前のリクエスト受信" latestStatus: "ちょっと前のステータス" storageUsage: "ストレージ使うた量" diff --git a/locales/ko-KR.yml b/locales/ko-KR.yml index fa73d49a34..746b59fc2a 100644 --- a/locales/ko-KR.yml +++ b/locales/ko-KR.yml @@ -167,7 +167,6 @@ annotation: "내용에 대한 주석" federation: "연합" instances: "인스턴스" registeredAt: "등록 날짜" -latestRequestSentAt: "마지막으로 요청을 보낸 시간" latestRequestReceivedAt: "마지막으로 요청을 받은 시간" latestStatus: "마지막 상태" storageUsage: "스토리지 사용량" @@ -916,6 +915,7 @@ caption: "캡션" loggedInAsBot: "봇 계정으로 로그인중" tools: "도구" cannotLoad: "불러오지 못했습니다" +numberOfProfileView: "프로필 뷰 수" _sensitiveMediaDetection: description: "기계학습을 통해 자동으로 민감한 미디어를 탐지하여, 모더레이션에 참고할 수 있도록 합니다. 서버의 부하를 약간 증가시킵니다." sensitivity: "탐지 민감도" @@ -1382,6 +1382,7 @@ _profile: changeBanner: "배너 이미지 변경" _exportOrImport: allNotes: "모든 노트" + favoritedNotes: "즐겨찾기한 노트" followingList: "팔로잉" muteList: "뮤트" blockingList: "차단" diff --git a/locales/nl-NL.yml b/locales/nl-NL.yml index ad7ab9723e..93ed3fa7e2 100644 --- a/locales/nl-NL.yml +++ b/locales/nl-NL.yml @@ -165,7 +165,6 @@ annotation: "Reacties" federation: "Federatie" instances: "Server" registeredAt: "Geregistreerd op" -latestRequestSentAt: "Laatste aanvraag verstuurd" latestRequestReceivedAt: "Laatste aanvraag ontvangen" latestStatus: "Laatste status" storageUsage: "Gebruikte opslagruimte" diff --git a/locales/pl-PL.yml b/locales/pl-PL.yml index 6c3b100885..f898f52929 100644 --- a/locales/pl-PL.yml +++ b/locales/pl-PL.yml @@ -165,7 +165,6 @@ annotation: "Komentarze" federation: "Federacja" instances: "Instancja" registeredAt: "Zarejestrowano" -latestRequestSentAt: "Ostatnie żądanie wysłano o" latestRequestReceivedAt: "Ostatnie żądanie otrzymano o" latestStatus: "Najnowszy status" storageUsage: "Użycie pamięci" diff --git a/locales/pt-PT.yml b/locales/pt-PT.yml index d333405316..dd1c2954b7 100644 --- a/locales/pt-PT.yml +++ b/locales/pt-PT.yml @@ -164,7 +164,6 @@ annotation: "Anotação" federation: "União" instances: "Instância" registeredAt: "Registrado em" -latestRequestSentAt: "Enviar a solicitação mais recente" latestRequestReceivedAt: "Recebeu a última solicitação" latestStatus: "Status mais recente" storageUsage: "Uso de armazenamento" diff --git a/locales/ro-RO.yml b/locales/ro-RO.yml index 4bb1d4f09a..54d382474d 100644 --- a/locales/ro-RO.yml +++ b/locales/ro-RO.yml @@ -164,7 +164,6 @@ annotation: "Adnotări" federation: "Federație" instances: "Instanțe" registeredAt: "Înregistrat în" -latestRequestSentAt: "Ultima cerere trimisă" latestRequestReceivedAt: "Ultima cerere primită" latestStatus: "Ultimul status" storageUsage: "Utilizare stocare" diff --git a/locales/ru-RU.yml b/locales/ru-RU.yml index 7c256b3bef..19dfcc3af9 100644 --- a/locales/ru-RU.yml +++ b/locales/ru-RU.yml @@ -164,7 +164,6 @@ annotation: "Описание" federation: "Федерация" instances: "Инстанс" registeredAt: "Первое наблюдение" -latestRequestSentAt: "Последний отправленный запрос" latestRequestReceivedAt: "Последний полученный запрос" latestStatus: "Последний статус" storageUsage: "Использовано" diff --git a/locales/sk-SK.yml b/locales/sk-SK.yml index 03a531e63b..1c57b6c2bb 100644 --- a/locales/sk-SK.yml +++ b/locales/sk-SK.yml @@ -167,7 +167,6 @@ annotation: "Komentáre" federation: "Federácia" instances: "Inštancia" registeredAt: "Registrácia" -latestRequestSentAt: "Posledná odoslaná požiadavka" latestRequestReceivedAt: "Posledná prijatá požiadavka" latestStatus: "Posledný status" storageUsage: "Využité úložisko" diff --git a/locales/sv-SE.yml b/locales/sv-SE.yml index b00808d3d0..e4e00e341d 100644 --- a/locales/sv-SE.yml +++ b/locales/sv-SE.yml @@ -163,7 +163,6 @@ annotation: "Kommentarer" federation: "Federation" instances: "Instanser" registeredAt: "Registrerad på" -latestRequestSentAt: "Senaste förfrågan skickad" latestRequestReceivedAt: "Senaste begäran mottagen" latestStatus: "Senaste status" storageUsage: "Använt lagringsutrymme" diff --git a/locales/th-TH.yml b/locales/th-TH.yml index 9dfcf0d2c4..7fc54f5f19 100644 --- a/locales/th-TH.yml +++ b/locales/th-TH.yml @@ -167,7 +167,6 @@ annotation: "ความคิดเห็น" federation: "เฟดิเวิร์ส" instances: "ตัวอย่าง" registeredAt: "จดทะเบียนที่" -latestRequestSentAt: "ส่งคำขอล่าสุดไปแล้ว" latestRequestReceivedAt: "ได้รับคำขอล่าสุดไปแล้ว" latestStatus: "สถานะล่าสุด" storageUsage: "พื้นที่จัดเก็บข้อมูลที่ใช้ไป" @@ -916,6 +915,7 @@ caption: "รายละเอียด" loggedInAsBot: "ล็อกอินเป็นบอตอยู่ในขณะนี้" tools: "เครื่องมือ" cannotLoad: "ไม่สามารถโหลดได้" +numberOfProfileView: "มุมมองโปรไฟล์" _sensitiveMediaDetection: description: "ลดความพยายามในการดูแลเซิร์ฟเวอร์ผ่านการจดจำสื่อ NSFW โดยอัตโนมัติผ่านการเรียนรู้ของเครื่อง การทำสิ่งนี้อาจจะเพิ่มภาระบนเซิร์ฟเวอร์เล็กน้อย" sensitivity: "การตรวจจับความไว" diff --git a/locales/uk-UA.yml b/locales/uk-UA.yml index cb52a86d98..49a59500ff 100644 --- a/locales/uk-UA.yml +++ b/locales/uk-UA.yml @@ -166,7 +166,6 @@ annotation: "Коментарі" federation: "Федіверс" instances: "Інстанс" registeredAt: "Приєднався(лась)" -latestRequestSentAt: "Останній запит надіслано" latestRequestReceivedAt: "Останній запит прийнято" latestStatus: "Останній статус" storageUsage: "Використання простору" diff --git a/locales/vi-VN.yml b/locales/vi-VN.yml index fdb6ec2647..b496cfaf22 100644 --- a/locales/vi-VN.yml +++ b/locales/vi-VN.yml @@ -164,7 +164,6 @@ annotation: "Bình luận" federation: "Liên hợp" instances: "Máy chủ" registeredAt: "Đăng ký vào" -latestRequestSentAt: "Yêu cầu cuối gửi lúc" latestRequestReceivedAt: "Yêu cầu cuối nhận lúc" latestStatus: "Trạng thái cuối cùng" storageUsage: "Dung lượng lưu trữ" diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml index 80d367a284..1e147752db 100644 --- a/locales/zh-CN.yml +++ b/locales/zh-CN.yml @@ -167,7 +167,6 @@ annotation: "注解" federation: "联合" instances: "实例" registeredAt: "初次观测" -latestRequestSentAt: "上次发送的请求" latestRequestReceivedAt: "上次收到的请求" latestStatus: "最后状态" storageUsage: "已用存储" @@ -913,9 +912,10 @@ sendPushNotificationReadMessageCaption: "“{emptyPushNotificationMessage}”的 windowMaximize: "最大化" windowRestore: "还原" caption: "标题" -loggedInAsBot: "已登录的Bot" +loggedInAsBot: "以Bot账户登录" tools: "工具" cannotLoad: "无法加载" +numberOfProfileView: "个人资料展示次数" _sensitiveMediaDetection: description: "可以使用机器学习技术自动检测敏感媒体,以便进行审核。服务器负载将略微增加。" sensitivity: "检测敏感度" @@ -1382,6 +1382,7 @@ _profile: changeBanner: "修改横幅" _exportOrImport: allNotes: "所有帖子" + favoritedNotes: "收藏的帖子" followingList: "关注中" muteList: "屏蔽" blockingList: "拉黑" diff --git a/locales/zh-TW.yml b/locales/zh-TW.yml index a97593e59c..8f8a8865ac 100644 --- a/locales/zh-TW.yml +++ b/locales/zh-TW.yml @@ -167,7 +167,6 @@ annotation: "註解" federation: "站台聯邦" instances: "實例" registeredAt: "初次觀測" -latestRequestSentAt: "上次發送的請求" latestRequestReceivedAt: "上次收到的請求" latestStatus: "最後狀態" storageUsage: "已使用容量" From f51220a5bfeacb7ff14ac3a7ee68a1158b94b4aa Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Wed, 4 Jan 2023 21:23:48 +0900 Subject: [PATCH 10/60] 13.0.0-beta.21 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1e3bf82a7b..5ab38a56ec 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "13.0.0-beta.20", + "version": "13.0.0-beta.21", "codename": "indigo", "repository": { "type": "git", From b1a75177a0165f726741219206bb52c02c5f6221 Mon Sep 17 00:00:00 2001 From: tamaina <tamaina@hotmail.co.jp> Date: Thu, 5 Jan 2023 03:28:25 +0900 Subject: [PATCH 11/60] =?UTF-8?q?enhance:=20RSS=E3=82=A6=E3=82=A3=E3=82=B8?= =?UTF-8?q?=E3=82=A7=E3=83=83=E3=83=88=20/=20RSS=E3=83=86=E3=82=A3?= =?UTF-8?q?=E3=83=83=E3=82=AB=E3=83=BC=E3=82=A6=E3=82=A3=E3=82=B8=E3=82=A7?= =?UTF-8?q?=E3=83=83=E3=83=88=E3=82=92=E3=81=84=E3=81=84=E6=84=9F=E3=81=98?= =?UTF-8?q?=E3=81=AB=E3=81=99=E3=82=8B=20(#9469)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * :v: * use useInterval * :v: * rawItems.value.length !== 0 * fix * https://github.com/misskey-dev/misskey/pull/9469#discussion_r1061763613 --- packages/frontend/src/scripts/use-interval.ts | 11 +- packages/frontend/src/widgets/rss-ticker.vue | 157 +++++++++++------- packages/frontend/src/widgets/rss.vue | 88 ++++++---- 3 files changed, 159 insertions(+), 97 deletions(-) diff --git a/packages/frontend/src/scripts/use-interval.ts b/packages/frontend/src/scripts/use-interval.ts index 201ba417ef..601dea6724 100644 --- a/packages/frontend/src/scripts/use-interval.ts +++ b/packages/frontend/src/scripts/use-interval.ts @@ -3,7 +3,7 @@ import { onMounted, onUnmounted } from 'vue'; export function useInterval(fn: () => void, interval: number, options: { immediate: boolean; afterMounted: boolean; -}): void { +}): (() => void) | undefined { if (Number.isNaN(interval)) return; let intervalId: number | null = null; @@ -18,7 +18,14 @@ export function useInterval(fn: () => void, interval: number, options: { intervalId = window.setInterval(fn, interval); } - onUnmounted(() => { + const clear = () => { if (intervalId) window.clearInterval(intervalId); + intervalId = null; + }; + + onUnmounted(() => { + clear(); }); + + return clear; } diff --git a/packages/frontend/src/widgets/rss-ticker.vue b/packages/frontend/src/widgets/rss-ticker.vue index c2d6dd2873..37672e13cf 100644 --- a/packages/frontend/src/widgets/rss-ticker.vue +++ b/packages/frontend/src/widgets/rss-ticker.vue @@ -3,13 +3,15 @@ <template #header><i class="ti ti-rss"></i>RSS</template> <template #func><button class="_button" @click="configure"><i class="ti ti-settings"></i></button></template> - <div class="ekmkgxbk"> - <MkLoading v-if="fetching"/> - <div v-else class="feed"> - <Transition name="change" mode="default"> + <div :class="$style.feed"> + <div v-if="fetching" :class="$style.loading"> + <MkEllipsis/> + </div> + <div v-else> + <Transition :name="$style.change" mode="default" appear> <MarqueeText :key="key" :duration="widgetProps.duration" :reverse="widgetProps.reverse"> - <span v-for="item in items" class="item"> - <a class="link" :href="item.link" rel="nofollow noopener" target="_blank" :title="item.title">{{ item.title }}</a><span class="divider"></span> + <span v-for="item in items" :class="$style.item" :key="item.link"> + <a :class="$style.link" :href="item.link" rel="nofollow noopener" target="_blank" :title="item.title">{{ item.title }}</a><span :class="$style.divider"></span> </span> </MarqueeText> </Transition> @@ -19,14 +21,14 @@ </template> <script lang="ts" setup> -import { onMounted, onUnmounted, ref, watch } from 'vue'; +import { ref, watch, computed } from 'vue'; import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget'; import MarqueeText from '@/components/MkMarquee.vue'; import { GetFormResultType } from '@/scripts/form'; -import * as os from '@/os'; import MkContainer from '@/components/MkContainer.vue'; -import { useInterval } from '@/scripts/use-interval'; import { shuffle } from '@/scripts/shuffle'; +import { url as base } from '@/config'; +import { useInterval } from '@/scripts/use-interval'; const name = 'rssTicker'; @@ -43,6 +45,10 @@ const widgetPropsDef = { type: 'number' as const, default: 60, }, + maxEntries: { + type: 'number' as const, + default: 15, + }, duration: { type: 'range' as const, default: 70, @@ -78,29 +84,49 @@ const { widgetProps, configure } = useWidgetPropsManager(name, emit, ); -const items = ref([]); +const rawItems = ref([]); +const items = computed(() => { + const newItems = rawItems.value.slice(0, widgetProps.maxEntries) + if (widgetProps.shuffle) { + shuffle(newItems); + } + return newItems; +}); const fetching = ref(true); +const fetchEndpoint = computed(() => { + const url = new URL('/api/fetch-rss', base); + url.searchParams.set('url', widgetProps.url); + return url; +}); +let intervalClear = $ref<(() => void) | undefined>(); + let key = $ref(0); const tick = () => { - window.fetch(`/api/fetch-rss?url=${widgetProps.url}`, {}).then(res => { - res.json().then(feed => { - if (widgetProps.shuffle) { - shuffle(feed.items); - } - items.value = feed.items; - fetching.value = false; - key++; - }); + if (document.visibilityState === 'hidden' && rawItems.value.length !== 0) return; + + window.fetch(fetchEndpoint.value, {}) + .then(res => res.json()) + .then(feed => { + if (widgetProps.shuffle) { + shuffle(feed.items); + } + rawItems.value = feed.items; + fetching.value = false; + key++; }); }; -watch(() => widgetProps.url, tick); - -useInterval(tick, Math.max(10000, widgetProps.refreshIntervalSec * 1000), { - immediate: true, - afterMounted: true, -}); +watch(() => fetchEndpoint, tick); +watch(() => widgetProps.refreshIntervalSec, () => { + if (intervalClear) { + intervalClear(); + } + intervalClear = useInterval(tick, Math.max(10000, widgetProps.refreshIntervalSec * 1000), { + immediate: true, + afterMounted: true, + }); +}, { immediate: true }); defineExpose<WidgetComponentExpose>({ name, @@ -109,44 +135,49 @@ defineExpose<WidgetComponentExpose>({ }); </script> -<style lang="scss" scoped> -.change-enter-active, .change-leave-active { - position: absolute; - top: 0; - transition: all 1s ease; -} -.change-enter-from { - opacity: 0; - transform: translateY(-100%); -} -.change-leave-to { - opacity: 0; - transform: translateY(100%); -} - -.ekmkgxbk { - > .feed { - --height: 42px; - padding: 0; - font-size: 0.9em; - line-height: var(--height); - height: var(--height); - contain: strict; - - ::v-deep(.item) { - display: inline-flex; - align-items: center; - vertical-align: bottom; - color: var(--fg); - - > .divider { - display: inline-block; - width: 0.5px; - height: 16px; - margin: 0 1em; - background: var(--divider); - } - } +<style lang="scss" module> +.change { + &:global(-enter-active), + &:global(-leave-active) { + position: absolute; + top: 0; + transition: all 1s ease; + } + &:global(-enter-from) { + opacity: 0; + transform: translateY(-100%); + } + &:global(-leave-to) { + opacity: 0; + transform: translateY(100%); } } + +.feed { + --height: 42px; + padding: 0; + font-size: 0.9em; + line-height: var(--height); + height: var(--height); + contain: strict; +} + +.loading { + text-align: center; +} + +.item { + display: inline-flex; + align-items: center; + vertical-align: bottom; + color: var(--fg); +} + +.divider { + display: inline-block; + width: 0.5px; + height: 16px; + margin: 0 1em; + background: var(--divider); +} </style> diff --git a/packages/frontend/src/widgets/rss.vue b/packages/frontend/src/widgets/rss.vue index c0338c8e47..554413cd1e 100644 --- a/packages/frontend/src/widgets/rss.vue +++ b/packages/frontend/src/widgets/rss.vue @@ -5,19 +5,24 @@ <div class="ekmkgxbj"> <MkLoading v-if="fetching"/> - <div v-else class="feed"> - <a v-for="item in items" class="item" :href="item.link" rel="nofollow noopener" target="_blank" :title="item.title">{{ item.title }}</a> + <div class="_fullinfo" v-else-if="(!items || items.length === 0) && widgetProps.showHeader"> + <img src="https://xn--931a.moe/assets/info.jpg" class="_ghost"/> + <div>{{ i18n.ts.nothing }}</div> + </div> + <div v-else :class="$style.feed"> + <a v-for="item in items" :class="$style.item" :href="item.link" :key="item.link" rel="nofollow noopener" target="_blank" :title="item.title">{{ item.title }}</a> </div> </div> </MkContainer> </template> <script lang="ts" setup> -import { onMounted, onUnmounted, ref, watch } from 'vue'; +import { ref, watch, computed } from 'vue'; import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget'; import { GetFormResultType } from '@/scripts/form'; -import * as os from '@/os'; import MkContainer from '@/components/MkContainer.vue'; +import { url as base } from '@/config'; +import { i18n } from '@/i18n'; import { useInterval } from '@/scripts/use-interval'; const name = 'rss'; @@ -27,6 +32,14 @@ const widgetPropsDef = { type: 'string' as const, default: 'http://feeds.afpbb.com/rss/afpbb/afpbbnews', }, + refreshIntervalSec: { + type: 'number' as const, + default: 60, + }, + maxEntries: { + type: 'number' as const, + default: 15, + }, showHeader: { type: 'boolean' as const, default: true, @@ -47,24 +60,37 @@ const { widgetProps, configure } = useWidgetPropsManager(name, emit, ); -const items = ref([]); +const rawItems = ref([]); +const items = computed(() => rawItems.value.slice(0, widgetProps.maxEntries)); const fetching = ref(true); +const fetchEndpoint = computed(() => { + const url = new URL('/api/fetch-rss', base); + url.searchParams.set('url', widgetProps.url); + return url; +}); +let intervalClear = $ref<(() => void) | undefined>(); const tick = () => { - window.fetch(`/api/fetch-rss?url=${widgetProps.url}`, {}).then(res => { - res.json().then(feed => { - items.value = feed.items; - fetching.value = false; - }); + if (document.visibilityState === 'hidden' && rawItems.value.length !== 0) return; + + window.fetch(fetchEndpoint.value, {}) + .then(res => res.json()) + .then(feed => { + rawItems.value = feed.items ?? []; + fetching.value = false; }); }; -watch(() => widgetProps.url, tick); - -useInterval(tick, 60000, { - immediate: true, - afterMounted: true, -}); +watch(() => fetchEndpoint, tick); +watch(() => widgetProps.refreshIntervalSec, () => { + if (intervalClear) { + intervalClear(); + } + intervalClear = useInterval(tick, Math.max(10000, widgetProps.refreshIntervalSec * 1000), { + immediate: true, + afterMounted: true, + }); +}, { immediate: true }); defineExpose<WidgetComponentExpose>({ name, @@ -73,24 +99,22 @@ defineExpose<WidgetComponentExpose>({ }); </script> -<style lang="scss" scoped> -.ekmkgxbj { - > .feed { - padding: 0; - font-size: 0.9em; +<style lang="scss" module> +.feed { + padding: 0; + font-size: 0.9em; +} - > .item { - display: block; - padding: 8px 16px; - color: var(--fg); - white-space: nowrap; - text-overflow: ellipsis; - overflow: hidden; +.item { + display: block; + padding: 8px 16px; + color: var(--fg); + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; - &:nth-child(even) { - background: rgba(#000, 0.05); - } - } + &:nth-child(even) { + background: rgba(#000, 0.05); } } </style> From 5d904b05dd09c635f9bfe4582eede3727b0aa382 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Thu, 5 Jan 2023 03:29:50 +0900 Subject: [PATCH 12/60] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ce996f5acd..83d013ce99 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -57,6 +57,7 @@ You should also include the user name that made the change. - Client: Make widgets of universal/classic sync between devices @tamaina - Client: Implement the button to subscribe push notification @tamaina - Client: Implement the toggle to or not to close push notifications when notifications or messages are read @tamaina +- Client: Improve RSS widget @tamaina - Client: show Unicode emoji tooltip with its name in MkReactionsViewer.reaction @saschanaz - Client: OpenSearch support @SoniEx2 @chaoticryptidz - Client: add user list widget @syuilo From ebe340d5105595abe2406e8f386c3ab69703b73b Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Thu, 5 Jan 2023 13:59:48 +0900 Subject: [PATCH 13/60] MisskeyPlay (#9467) * wip * wip * wip * wip * wip * Update ui.ts * wip * wip * wip * wip * wip * wip * wip * wip * Update CHANGELOG.md * wip * wip * wip * wip * :art: * wip * :v: --- CHANGELOG.md | 10 +- locales/ja-JP.yml | 21 + .../backend/migration/1672822262496-Flash.js | 29 + packages/backend/src/core/CoreModule.ts | 12 + .../src/core/entities/FlashEntityService.ts | 55 ++ .../core/entities/FlashLikeEntityService.ts | 44 ++ packages/backend/src/di-symbols.ts | 2 + .../backend/src/models/RepositoryModule.ts | 18 +- packages/backend/src/models/entities/Flash.ts | 60 ++ .../backend/src/models/entities/FlashLike.ts | 33 ++ packages/backend/src/models/index.ts | 6 + packages/backend/src/postgre.ts | 4 + .../backend/src/server/api/EndpointsModule.ts | 36 ++ packages/backend/src/server/api/endpoints.ts | 18 + .../src/server/api/endpoints/flash/create.ts | 66 +++ .../src/server/api/endpoints/flash/delete.ts | 56 ++ .../server/api/endpoints/flash/featured.ts | 48 ++ .../src/server/api/endpoints/flash/like.ts | 87 +++ .../server/api/endpoints/flash/my-likes.ts | 68 +++ .../src/server/api/endpoints/flash/my.ts | 57 ++ .../src/server/api/endpoints/flash/show.ts | 60 ++ .../src/server/api/endpoints/flash/unlike.ts | 68 +++ .../src/server/api/endpoints/flash/update.ts | 78 +++ packages/frontend/src/components/MkAsUi.vue | 107 ++++ packages/frontend/src/components/MkButton.vue | 40 +- .../frontend/src/components/MkChartLegend.vue | 2 +- .../src/components/MkFlashPreview.vue | 112 ++++ .../frontend/src/components/MkLaunchPad.vue | 2 +- .../frontend/src/components/MkPagePreview.vue | 19 +- .../frontend/src/components/form/select.vue | 2 +- packages/frontend/src/navbar.ts | 39 +- .../frontend/src/pages/flash/flash-edit.vue | 111 ++++ .../frontend/src/pages/flash/flash-index.vue | 99 ++++ packages/frontend/src/pages/flash/flash.vue | 291 ++++++++++ packages/frontend/src/pages/page.vue | 18 +- packages/frontend/src/pages/scratchpad.vue | 104 ++-- .../frontend/src/pages/settings/navbar.vue | 2 +- packages/frontend/src/router.ts | 14 + packages/frontend/src/scripts/aiscript/ui.ts | 526 ++++++++++++++++++ .../src/ui/_common_/navbar-for-mobile.vue | 2 +- packages/frontend/src/ui/_common_/navbar.vue | 4 +- packages/frontend/src/ui/classic.header.vue | 2 +- packages/frontend/src/ui/classic.sidebar.vue | 2 +- .../frontend/src/widgets/aiscript-app.vue | 122 ++++ packages/frontend/src/widgets/index.ts | 2 + 45 files changed, 2465 insertions(+), 93 deletions(-) create mode 100644 packages/backend/migration/1672822262496-Flash.js create mode 100644 packages/backend/src/core/entities/FlashEntityService.ts create mode 100644 packages/backend/src/core/entities/FlashLikeEntityService.ts create mode 100644 packages/backend/src/models/entities/Flash.ts create mode 100644 packages/backend/src/models/entities/FlashLike.ts create mode 100644 packages/backend/src/server/api/endpoints/flash/create.ts create mode 100644 packages/backend/src/server/api/endpoints/flash/delete.ts create mode 100644 packages/backend/src/server/api/endpoints/flash/featured.ts create mode 100644 packages/backend/src/server/api/endpoints/flash/like.ts create mode 100644 packages/backend/src/server/api/endpoints/flash/my-likes.ts create mode 100644 packages/backend/src/server/api/endpoints/flash/my.ts create mode 100644 packages/backend/src/server/api/endpoints/flash/show.ts create mode 100644 packages/backend/src/server/api/endpoints/flash/unlike.ts create mode 100644 packages/backend/src/server/api/endpoints/flash/update.ts create mode 100644 packages/frontend/src/components/MkAsUi.vue create mode 100644 packages/frontend/src/components/MkFlashPreview.vue create mode 100644 packages/frontend/src/pages/flash/flash-edit.vue create mode 100644 packages/frontend/src/pages/flash/flash-index.vue create mode 100644 packages/frontend/src/pages/flash/flash.vue create mode 100644 packages/frontend/src/scripts/aiscript/ui.ts create mode 100644 packages/frontend/src/widgets/aiscript-app.vue diff --git a/CHANGELOG.md b/CHANGELOG.md index 83d013ce99..005b011592 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,7 +19,7 @@ You should also include the user name that made the change. - Migrate to Yarn Berry (v3.2.1) @ThatOneCalculator - You may have to `yarn run clean-all`, `sudo corepack enable` and `yarn set version berry` before running `yarn install` if you're still on yarn classic - 新たに動的なPagesを作ることはできなくなりました - - 代わりに今後AiScriptを用いてより柔軟に動的なコンテンツを作成できるMisskey Play機能の実装を予定しています。 + - 代わりにAiScriptを用いてより柔軟に動的なコンテンツを作成できるMisskey Play機能が実装されています。 - AiScriptが0.12.0にアップデートされました - 0.12.0の変更点についてはこちら https://github.com/syuilo/aiscript/blob/master/CHANGELOG.md#0120 - 0.12.0未満のプラグインは読み込むことはできません @@ -33,12 +33,13 @@ You should also include the user name that made the change. - API: `instance`エンティティに`latestStatus`、`lastCommunicatedAt`、`latestRequestSentAt`プロパティが含まれなくなりました ### Improvements -- Push notification of Antenna note @tamaina -- AVIF support @tamaina -- Add Cloudflare Turnstile CAPTCHA support @CyberRex0 +- Misskey Play @syuilo - Introduce retention-rate aggregation @syuilo - Make possible to export favorited notes @syuilo - Add per user pv chart @syuilo +- Push notification of Antenna note @tamaina +- AVIF support @tamaina +- Add Cloudflare Turnstile CAPTCHA support @CyberRex0 - Server: signToActivityPubGet is set to true by default @syuilo - Server: improve syslog performance @syuilo - Server: improve note scoring for featured notes @CyberRex0 @@ -47,6 +48,7 @@ You should also include the user name that made the change. - Server: delete outdated notes of antenna regularly to improve db performance @syuilo - Server: improve activitypub deliver performance @syuilo - Client: use tabler-icons instead of fontawesome to better design @syuilo +- Client: Add AiScript App widget - Client: Add new gabber kick sounds (thanks for noizenecio) - Client: Add link to user RSS feed in profile menu @ssmucny - Client: Compress non-animated PNG files @saschanaz diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index d6a5518196..32bafcd661 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -916,6 +916,10 @@ loggedInAsBot: "Botアカウントでログイン中" tools: "ツール" cannotLoad: "読み込めません" numberOfProfileView: "プロフィール表示回数" +like: "いいね!" +unlike: "いいねを解除" +numberOfLikes: "いいね数" +show: "表示" _sensitiveMediaDetection: description: "機械学習を使って自動でセンシティブなメディアを検出し、モデレーションに役立てることができます。サーバーの負荷が少し増えます。" @@ -1348,6 +1352,7 @@ _widgets: jobQueue: "ジョブキュー" serverMetric: "サーバーメトリクス" aiscript: "AiScriptコンソール" + aiscriptApp: "AiScript App" aichan: "藍" userList: "ユーザーリスト" _userList: @@ -1463,6 +1468,22 @@ _timelines: social: "ソーシャル" global: "グローバル" +_play: + new: "Playの作成" + edit: "Playの編集" + created: "Playを作成しました" + updated: "Playを更新しました" + deleted: "Playを削除しました" + pageSetting: "Play設定" + editThisPage: "このPlayを編集" + viewSource: "ソースを表示" + my: "自分のPlay" + liked: "いいねしたPlay" + featured: "人気" + title: "タイトル" + script: "スクリプト" + summary: "説明" + _pages: newPage: "ページの作成" editPage: "ページの編集" diff --git a/packages/backend/migration/1672822262496-Flash.js b/packages/backend/migration/1672822262496-Flash.js new file mode 100644 index 0000000000..6c2338fab2 --- /dev/null +++ b/packages/backend/migration/1672822262496-Flash.js @@ -0,0 +1,29 @@ +export class Flash1672822262496 { + name = 'Flash1672822262496' + + async up(queryRunner) { + await queryRunner.query(`CREATE TABLE "flash" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL, "title" character varying(256) NOT NULL, "summary" character varying(1024) NOT NULL, "userId" character varying(32) NOT NULL, "script" character varying(16384) NOT NULL, "permissions" character varying(256) array NOT NULL DEFAULT '{}', "likedCount" integer NOT NULL DEFAULT '0', CONSTRAINT "PK_0c01a2c1c5f2266942dd1b3fdbc" PRIMARY KEY ("id")); COMMENT ON COLUMN "flash"."createdAt" IS 'The created date of the Flash.'; COMMENT ON COLUMN "flash"."updatedAt" IS 'The updated date of the Flash.'; COMMENT ON COLUMN "flash"."userId" IS 'The ID of author.'`); + await queryRunner.query(`CREATE INDEX "IDX_149d2e44785707548c82999b01" ON "flash" ("createdAt") `); + await queryRunner.query(`CREATE INDEX "IDX_3aa8ea9a8f15214ad91638c0a7" ON "flash" ("updatedAt") `); + await queryRunner.query(`CREATE INDEX "IDX_9b88250fc2fd009b8f1b5623ed" ON "flash" ("userId") `); + await queryRunner.query(`CREATE TABLE "flash_like" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32) NOT NULL, "flashId" character varying(32) NOT NULL, CONSTRAINT "PK_d110109ee310588d63d6183b233" PRIMARY KEY ("id"))`); + await queryRunner.query(`CREATE INDEX "IDX_60c4af1c19a7a75f1592f93b28" ON "flash_like" ("userId") `); + await queryRunner.query(`CREATE UNIQUE INDEX "IDX_cfbfeeccb0cbedcd660b17eb07" ON "flash_like" ("userId", "flashId") `); + await queryRunner.query(`ALTER TABLE "flash" ADD CONSTRAINT "FK_9b88250fc2fd009b8f1b5623ed5" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); + await queryRunner.query(`ALTER TABLE "flash_like" ADD CONSTRAINT "FK_60c4af1c19a7a75f1592f93b287" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); + await queryRunner.query(`ALTER TABLE "flash_like" ADD CONSTRAINT "FK_6c16fe0e93b7a1951eca624b76a" FOREIGN KEY ("flashId") REFERENCES "flash"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "flash_like" DROP CONSTRAINT "FK_6c16fe0e93b7a1951eca624b76a"`); + await queryRunner.query(`ALTER TABLE "flash_like" DROP CONSTRAINT "FK_60c4af1c19a7a75f1592f93b287"`); + await queryRunner.query(`ALTER TABLE "flash" DROP CONSTRAINT "FK_9b88250fc2fd009b8f1b5623ed5"`); + await queryRunner.query(`DROP INDEX "public"."IDX_cfbfeeccb0cbedcd660b17eb07"`); + await queryRunner.query(`DROP INDEX "public"."IDX_60c4af1c19a7a75f1592f93b28"`); + await queryRunner.query(`DROP TABLE "flash_like"`); + await queryRunner.query(`DROP INDEX "public"."IDX_9b88250fc2fd009b8f1b5623ed"`); + await queryRunner.query(`DROP INDEX "public"."IDX_3aa8ea9a8f15214ad91638c0a7"`); + await queryRunner.query(`DROP INDEX "public"."IDX_149d2e44785707548c82999b01"`); + await queryRunner.query(`DROP TABLE "flash"`); + } +} diff --git a/packages/backend/src/core/CoreModule.ts b/packages/backend/src/core/CoreModule.ts index 7c6d12abf8..2f17fa389a 100644 --- a/packages/backend/src/core/CoreModule.ts +++ b/packages/backend/src/core/CoreModule.ts @@ -95,6 +95,8 @@ import { UserEntityService } from './entities/UserEntityService.js'; import { UserGroupEntityService } from './entities/UserGroupEntityService.js'; import { UserGroupInvitationEntityService } from './entities/UserGroupInvitationEntityService.js'; import { UserListEntityService } from './entities/UserListEntityService.js'; +import { FlashEntityService } from './entities/FlashEntityService.js'; +import { FlashLikeEntityService } from './entities/FlashLikeEntityService.js'; import { ApAudienceService } from './activitypub/ApAudienceService.js'; import { ApDbResolverService } from './activitypub/ApDbResolverService.js'; import { ApDeliverManagerService } from './activitypub/ApDeliverManagerService.js'; @@ -216,6 +218,8 @@ const $UserEntityService: Provider = { provide: 'UserEntityService', useExisting const $UserGroupEntityService: Provider = { provide: 'UserGroupEntityService', useExisting: UserGroupEntityService }; const $UserGroupInvitationEntityService: Provider = { provide: 'UserGroupInvitationEntityService', useExisting: UserGroupInvitationEntityService }; const $UserListEntityService: Provider = { provide: 'UserListEntityService', useExisting: UserListEntityService }; +const $FlashEntityService: Provider = { provide: 'FlashEntityService', useExisting: FlashEntityService }; +const $FlashLikeEntityService: Provider = { provide: 'FlashLikeEntityService', useExisting: FlashLikeEntityService }; const $ApAudienceService: Provider = { provide: 'ApAudienceService', useExisting: ApAudienceService }; const $ApDbResolverService: Provider = { provide: 'ApDbResolverService', useExisting: ApDbResolverService }; @@ -338,6 +342,8 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting UserGroupEntityService, UserGroupInvitationEntityService, UserListEntityService, + FlashEntityService, + FlashLikeEntityService, ApAudienceService, ApDbResolverService, ApDeliverManagerService, @@ -455,6 +461,8 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting $UserGroupEntityService, $UserGroupInvitationEntityService, $UserListEntityService, + $FlashEntityService, + $FlashLikeEntityService, $ApAudienceService, $ApDbResolverService, $ApDeliverManagerService, @@ -572,6 +580,8 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting UserGroupEntityService, UserGroupInvitationEntityService, UserListEntityService, + FlashEntityService, + FlashLikeEntityService, ApAudienceService, ApDbResolverService, ApDeliverManagerService, @@ -688,6 +698,8 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting $UserGroupEntityService, $UserGroupInvitationEntityService, $UserListEntityService, + $FlashEntityService, + $FlashLikeEntityService, $ApAudienceService, $ApDbResolverService, $ApDeliverManagerService, diff --git a/packages/backend/src/core/entities/FlashEntityService.ts b/packages/backend/src/core/entities/FlashEntityService.ts new file mode 100644 index 0000000000..61bd18c04f --- /dev/null +++ b/packages/backend/src/core/entities/FlashEntityService.ts @@ -0,0 +1,55 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { DI } from '@/di-symbols.js'; +import type { FlashsRepository, FlashLikesRepository } from '@/models/index.js'; +import { awaitAll } from '@/misc/prelude/await-all.js'; +import type { Packed } from '@/misc/schema.js'; +import type { } from '@/models/entities/Blocking.js'; +import type { User } from '@/models/entities/User.js'; +import type { Flash } from '@/models/entities/Flash.js'; +import { bindThis } from '@/decorators.js'; +import { UserEntityService } from './UserEntityService.js'; + +@Injectable() +export class FlashEntityService { + constructor( + @Inject(DI.flashsRepository) + private flashsRepository: FlashsRepository, + + @Inject(DI.flashLikesRepository) + private flashLikesRepository: FlashLikesRepository, + + private userEntityService: UserEntityService, + ) { + } + + @bindThis + public async pack( + src: Flash['id'] | Flash, + me?: { id: User['id'] } | null | undefined, + ): Promise<Packed<'Flash'>> { + const meId = me ? me.id : null; + const flash = typeof src === 'object' ? src : await this.flashsRepository.findOneByOrFail({ id: src }); + + return await awaitAll({ + id: flash.id, + createdAt: flash.createdAt.toISOString(), + updatedAt: flash.updatedAt.toISOString(), + userId: flash.userId, + user: this.userEntityService.pack(flash.user ?? flash.userId, me), // { detail: true } すると無限ループするので注意 + title: flash.title, + summary: flash.summary, + script: flash.script, + likedCount: flash.likedCount, + isLiked: meId ? await this.flashLikesRepository.findOneBy({ flashId: flash.id, userId: meId }).then(x => x != null) : undefined, + }); + } + + @bindThis + public packMany( + flashs: Flash[], + me?: { id: User['id'] } | null | undefined, + ) { + return Promise.all(flashs.map(x => this.pack(x, me))); + } +} + diff --git a/packages/backend/src/core/entities/FlashLikeEntityService.ts b/packages/backend/src/core/entities/FlashLikeEntityService.ts new file mode 100644 index 0000000000..dcf12d53ea --- /dev/null +++ b/packages/backend/src/core/entities/FlashLikeEntityService.ts @@ -0,0 +1,44 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { DI } from '@/di-symbols.js'; +import type { FlashLikesRepository } from '@/models/index.js'; +import { awaitAll } from '@/misc/prelude/await-all.js'; +import type { Packed } from '@/misc/schema.js'; +import type { } from '@/models/entities/Blocking.js'; +import type { User } from '@/models/entities/User.js'; +import type { FlashLike } from '@/models/entities/FlashLike.js'; +import { bindThis } from '@/decorators.js'; +import { UserEntityService } from './UserEntityService.js'; +import { FlashEntityService } from './FlashEntityService.js'; + +@Injectable() +export class FlashLikeEntityService { + constructor( + @Inject(DI.flashLikesRepository) + private flashLikesRepository: FlashLikesRepository, + + private flashEntityService: FlashEntityService, + ) { + } + + @bindThis + public async pack( + src: FlashLike['id'] | FlashLike, + me?: { id: User['id'] } | null | undefined, + ) { + const like = typeof src === 'object' ? src : await this.flashLikesRepository.findOneByOrFail({ id: src }); + + return { + id: like.id, + flash: await this.flashEntityService.pack(like.flash ?? like.flashId, me), + }; + } + + @bindThis + public packMany( + likes: any[], + me: { id: User['id'] }, + ) { + return Promise.all(likes.map(x => this.pack(x, me))); + } +} + diff --git a/packages/backend/src/di-symbols.ts b/packages/backend/src/di-symbols.ts index d2a361405f..9719d773ca 100644 --- a/packages/backend/src/di-symbols.ts +++ b/packages/backend/src/di-symbols.ts @@ -69,5 +69,7 @@ export const DI = { adsRepository: Symbol('adsRepository'), passwordResetRequestsRepository: Symbol('passwordResetRequestsRepository'), retentionAggregationsRepository: Symbol('retentionAggregationsRepository'), + flashsRepository: Symbol('flashsRepository'), + flashLikesRepository: Symbol('flashLikesRepository'), //#endregion }; diff --git a/packages/backend/src/models/RepositoryModule.ts b/packages/backend/src/models/RepositoryModule.ts index e22f0517ca..a5d5a63931 100644 --- a/packages/backend/src/models/RepositoryModule.ts +++ b/packages/backend/src/models/RepositoryModule.ts @@ -1,6 +1,6 @@ import { Module } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import { User, Note, Announcement, AnnouncementRead, App, NoteFavorite, NoteThreadMuting, NoteReaction, NoteUnread, Notification, Poll, PollVote, UserProfile, UserKeypair, UserPending, AttestationChallenge, UserSecurityKey, UserPublickey, UserList, UserListJoining, UserGroup, UserGroupJoining, UserGroupInvitation, UserNotePining, UserIp, UsedUsername, Following, FollowRequest, Instance, Emoji, DriveFile, DriveFolder, Meta, Muting, Blocking, SwSubscription, Hashtag, AbuseUserReport, RegistrationTicket, AuthSession, AccessToken, Signin, MessagingMessage, Page, PageLike, GalleryPost, GalleryLike, ModerationLog, Clip, ClipNote, Antenna, AntennaNote, PromoNote, PromoRead, Relay, MutedNote, Channel, ChannelFollowing, ChannelNotePining, RegistryItem, Webhook, Ad, PasswordResetRequest, RetentionAggregation } from './index.js'; +import { User, Note, Announcement, AnnouncementRead, App, NoteFavorite, NoteThreadMuting, NoteReaction, NoteUnread, Notification, Poll, PollVote, UserProfile, UserKeypair, UserPending, AttestationChallenge, UserSecurityKey, UserPublickey, UserList, UserListJoining, UserGroup, UserGroupJoining, UserGroupInvitation, UserNotePining, UserIp, UsedUsername, Following, FollowRequest, Instance, Emoji, DriveFile, DriveFolder, Meta, Muting, Blocking, SwSubscription, Hashtag, AbuseUserReport, RegistrationTicket, AuthSession, AccessToken, Signin, MessagingMessage, Page, PageLike, GalleryPost, GalleryLike, ModerationLog, Clip, ClipNote, Antenna, AntennaNote, PromoNote, PromoRead, Relay, MutedNote, Channel, ChannelFollowing, ChannelNotePining, RegistryItem, Webhook, Ad, PasswordResetRequest, RetentionAggregation, FlashLike, Flash } from './index.js'; import type { DataSource } from 'typeorm'; import type { Provider } from '@nestjs/common'; @@ -388,6 +388,18 @@ const $retentionAggregationsRepository: Provider = { inject: [DI.db], }; +const $flashsRepository: Provider = { + provide: DI.flashsRepository, + useFactory: (db: DataSource) => db.getRepository(Flash), + inject: [DI.db], +}; + +const $flashLikesRepository: Provider = { + provide: DI.flashLikesRepository, + useFactory: (db: DataSource) => db.getRepository(FlashLike), + inject: [DI.db], +}; + @Module({ imports: [ ], @@ -456,6 +468,8 @@ const $retentionAggregationsRepository: Provider = { $adsRepository, $passwordResetRequestsRepository, $retentionAggregationsRepository, + $flashsRepository, + $flashLikesRepository, ], exports: [ $usersRepository, @@ -522,6 +536,8 @@ const $retentionAggregationsRepository: Provider = { $adsRepository, $passwordResetRequestsRepository, $retentionAggregationsRepository, + $flashsRepository, + $flashLikesRepository, ], }) export class RepositoryModule {} diff --git a/packages/backend/src/models/entities/Flash.ts b/packages/backend/src/models/entities/Flash.ts new file mode 100644 index 0000000000..d9a6ac987c --- /dev/null +++ b/packages/backend/src/models/entities/Flash.ts @@ -0,0 +1,60 @@ +import { Entity, Index, JoinColumn, Column, PrimaryColumn, ManyToOne } from 'typeorm'; +import { id } from '../id.js'; +import { User } from './User.js'; +import { DriveFile } from './DriveFile.js'; + +@Entity() +export class Flash { + @PrimaryColumn(id()) + public id: string; + + @Index() + @Column('timestamp with time zone', { + comment: 'The created date of the Flash.', + }) + public createdAt: Date; + + @Index() + @Column('timestamp with time zone', { + comment: 'The updated date of the Flash.', + }) + public updatedAt: Date; + + @Column('varchar', { + length: 256, + }) + public title: string; + + @Column('varchar', { + length: 1024, + }) + public summary: string; + + @Index() + @Column({ + ...id(), + comment: 'The ID of author.', + }) + public userId: User['id']; + + @ManyToOne(type => User, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public user: User | null; + + @Column('varchar', { + length: 16384, + }) + public script: string; + + @Column('varchar', { + length: 256, array: true, default: '{}', + }) + public permissions: string[]; + + @Column('integer', { + default: 0, + }) + public likedCount: number; +} diff --git a/packages/backend/src/models/entities/FlashLike.ts b/packages/backend/src/models/entities/FlashLike.ts new file mode 100644 index 0000000000..81d39191ca --- /dev/null +++ b/packages/backend/src/models/entities/FlashLike.ts @@ -0,0 +1,33 @@ +import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; +import { id } from '../id.js'; +import { User } from './User.js'; +import { Flash } from './Flash.js'; + +@Entity() +@Index(['userId', 'flashId'], { unique: true }) +export class FlashLike { + @PrimaryColumn(id()) + public id: string; + + @Column('timestamp with time zone') + public createdAt: Date; + + @Index() + @Column(id()) + public userId: User['id']; + + @ManyToOne(type => User, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public user: User | null; + + @Column(id()) + public flashId: Flash['id']; + + @ManyToOne(type => Flash, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public flash: Flash | null; +} diff --git a/packages/backend/src/models/index.ts b/packages/backend/src/models/index.ts index ca7a7c9e56..b132475747 100644 --- a/packages/backend/src/models/index.ts +++ b/packages/backend/src/models/index.ts @@ -62,6 +62,8 @@ import { UserSecurityKey } from '@/models/entities/UserSecurityKey.js'; import { Webhook } from '@/models/entities/Webhook.js'; import { Channel } from '@/models/entities/Channel.js'; import { RetentionAggregation } from '@/models/entities/RetentionAggregation.js'; +import { Flash } from '@/models/entities/Flash.js'; +import { FlashLike } from '@/models/entities/FlashLike.js'; import type { Repository } from 'typeorm'; export { @@ -129,6 +131,8 @@ export { Webhook, Channel, RetentionAggregation, + Flash, + FlashLike, }; export type AbuseUserReportsRepository = Repository<AbuseUserReport>; @@ -195,3 +199,5 @@ export type UserSecurityKeysRepository = Repository<UserSecurityKey>; export type WebhooksRepository = Repository<Webhook>; export type ChannelsRepository = Repository<Channel>; export type RetentionAggregationsRepository = Repository<RetentionAggregation>; +export type FlashsRepository = Repository<Flash>; +export type FlashLikesRepository = Repository<FlashLike>; diff --git a/packages/backend/src/postgre.ts b/packages/backend/src/postgre.ts index 4b4490a0c3..4f6b157d80 100644 --- a/packages/backend/src/postgre.ts +++ b/packages/backend/src/postgre.ts @@ -70,6 +70,8 @@ import { UserSecurityKey } from '@/models/entities/UserSecurityKey.js'; import { Webhook } from '@/models/entities/Webhook.js'; import { Channel } from '@/models/entities/Channel.js'; import { RetentionAggregation } from '@/models/entities/RetentionAggregation.js'; +import { Flash } from '@/models/entities/Flash.js'; +import { FlashLike } from '@/models/entities/FlashLike.js'; import { Config } from '@/config.js'; import MisskeyLogger from '@/logger.js'; @@ -184,6 +186,8 @@ export const entities = [ Webhook, UserIp, RetentionAggregation, + Flash, + FlashLike, ...charts, ]; diff --git a/packages/backend/src/server/api/EndpointsModule.ts b/packages/backend/src/server/api/EndpointsModule.ts index 32eff7f312..60beca4f47 100644 --- a/packages/backend/src/server/api/EndpointsModule.ts +++ b/packages/backend/src/server/api/EndpointsModule.ts @@ -266,6 +266,15 @@ import * as ep___pages_like from './endpoints/pages/like.js'; import * as ep___pages_show from './endpoints/pages/show.js'; import * as ep___pages_unlike from './endpoints/pages/unlike.js'; import * as ep___pages_update from './endpoints/pages/update.js'; +import * as ep___flash_create from './endpoints/flash/create.js'; +import * as ep___flash_delete from './endpoints/flash/delete.js'; +import * as ep___flash_featured from './endpoints/flash/featured.js'; +import * as ep___flash_like from './endpoints/flash/like.js'; +import * as ep___flash_show from './endpoints/flash/show.js'; +import * as ep___flash_unlike from './endpoints/flash/unlike.js'; +import * as ep___flash_update from './endpoints/flash/update.js'; +import * as ep___flash_my from './endpoints/flash/my.js'; +import * as ep___flash_myLikes from './endpoints/flash/my-likes.js'; import * as ep___ping from './endpoints/ping.js'; import * as ep___pinnedUsers from './endpoints/pinned-users.js'; import * as ep___promo_read from './endpoints/promo/read.js'; @@ -587,6 +596,15 @@ const $pages_like: Provider = { provide: 'ep:pages/like', useClass: ep___pages_l const $pages_show: Provider = { provide: 'ep:pages/show', useClass: ep___pages_show.default }; const $pages_unlike: Provider = { provide: 'ep:pages/unlike', useClass: ep___pages_unlike.default }; const $pages_update: Provider = { provide: 'ep:pages/update', useClass: ep___pages_update.default }; +const $flash_create: Provider = { provide: 'ep:flash/create', useClass: ep___flash_create.default }; +const $flash_delete: Provider = { provide: 'ep:flash/delete', useClass: ep___flash_delete.default }; +const $flash_featured: Provider = { provide: 'ep:flash/featured', useClass: ep___flash_featured.default }; +const $flash_like: Provider = { provide: 'ep:flash/like', useClass: ep___flash_like.default }; +const $flash_show: Provider = { provide: 'ep:flash/show', useClass: ep___flash_show.default }; +const $flash_unlike: Provider = { provide: 'ep:flash/unlike', useClass: ep___flash_unlike.default }; +const $flash_update: Provider = { provide: 'ep:flash/update', useClass: ep___flash_update.default }; +const $flash_my: Provider = { provide: 'ep:flash/my', useClass: ep___flash_my.default }; +const $flash_myLikes: Provider = { provide: 'ep:flash/my-likes', useClass: ep___flash_myLikes.default }; const $ping: Provider = { provide: 'ep:ping', useClass: ep___ping.default }; const $pinnedUsers: Provider = { provide: 'ep:pinned-users', useClass: ep___pinnedUsers.default }; const $promo_read: Provider = { provide: 'ep:promo/read', useClass: ep___promo_read.default }; @@ -912,6 +930,15 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $pages_show, $pages_unlike, $pages_update, + $flash_create, + $flash_delete, + $flash_featured, + $flash_like, + $flash_show, + $flash_unlike, + $flash_update, + $flash_my, + $flash_myLikes, $ping, $pinnedUsers, $promo_read, @@ -1231,6 +1258,15 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $pages_show, $pages_unlike, $pages_update, + $flash_create, + $flash_delete, + $flash_featured, + $flash_like, + $flash_show, + $flash_unlike, + $flash_update, + $flash_my, + $flash_myLikes, $ping, $pinnedUsers, $promo_read, diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts index 49dc3b224f..d4f8be5b85 100644 --- a/packages/backend/src/server/api/endpoints.ts +++ b/packages/backend/src/server/api/endpoints.ts @@ -265,6 +265,15 @@ import * as ep___pages_like from './endpoints/pages/like.js'; import * as ep___pages_show from './endpoints/pages/show.js'; import * as ep___pages_unlike from './endpoints/pages/unlike.js'; import * as ep___pages_update from './endpoints/pages/update.js'; +import * as ep___flash_create from './endpoints/flash/create.js'; +import * as ep___flash_delete from './endpoints/flash/delete.js'; +import * as ep___flash_featured from './endpoints/flash/featured.js'; +import * as ep___flash_like from './endpoints/flash/like.js'; +import * as ep___flash_show from './endpoints/flash/show.js'; +import * as ep___flash_unlike from './endpoints/flash/unlike.js'; +import * as ep___flash_update from './endpoints/flash/update.js'; +import * as ep___flash_my from './endpoints/flash/my.js'; +import * as ep___flash_myLikes from './endpoints/flash/my-likes.js'; import * as ep___ping from './endpoints/ping.js'; import * as ep___pinnedUsers from './endpoints/pinned-users.js'; import * as ep___promo_read from './endpoints/promo/read.js'; @@ -584,6 +593,15 @@ const eps = [ ['pages/show', ep___pages_show], ['pages/unlike', ep___pages_unlike], ['pages/update', ep___pages_update], + ['flash/create', ep___flash_create], + ['flash/delete', ep___flash_delete], + ['flash/featured', ep___flash_featured], + ['flash/like', ep___flash_like], + ['flash/show', ep___flash_show], + ['flash/unlike', ep___flash_unlike], + ['flash/update', ep___flash_update], + ['flash/my', ep___flash_my], + ['flash/my-likes', ep___flash_myLikes], ['ping', ep___ping], ['pinned-users', ep___pinnedUsers], ['promo/read', ep___promo_read], diff --git a/packages/backend/src/server/api/endpoints/flash/create.ts b/packages/backend/src/server/api/endpoints/flash/create.ts new file mode 100644 index 0000000000..a652047d98 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/flash/create.ts @@ -0,0 +1,66 @@ +import ms from 'ms'; +import { Inject, Injectable } from '@nestjs/common'; +import type { DriveFilesRepository, FlashsRepository, PagesRepository } from '@/models/index.js'; +import { IdService } from '@/core/IdService.js'; +import { Page } from '@/models/entities/Page.js'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { PageEntityService } from '@/core/entities/PageEntityService.js'; +import { DI } from '@/di-symbols.js'; +import { FlashEntityService } from '@/core/entities/FlashEntityService.js'; +import { ApiError } from '../../error.js'; + +export const meta = { + tags: ['flash'], + + requireCredential: true, + + kind: 'write:flash', + + limit: { + duration: ms('1hour'), + max: 10, + }, + + errors: { + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + title: { type: 'string' }, + summary: { type: 'string' }, + script: { type: 'string' }, + permissions: { type: 'array', items: { + type: 'string', + } }, + }, + required: ['title', 'summary', 'script', 'permissions'], +} as const; + +// eslint-disable-next-line import/no-default-export +@Injectable() +export default class extends Endpoint<typeof meta, typeof paramDef> { + constructor( + @Inject(DI.flashsRepository) + private flashsRepository: FlashsRepository, + + private flashEntityService: FlashEntityService, + private idService: IdService, + ) { + super(meta, paramDef, async (ps, me) => { + const flash = await this.flashsRepository.insert({ + id: this.idService.genId(), + userId: me.id, + createdAt: new Date(), + updatedAt: new Date(), + title: ps.title, + summary: ps.summary, + script: ps.script, + permissions: ps.permissions, + }).then(x => this.flashsRepository.findOneByOrFail(x.identifiers[0])); + + return await this.flashEntityService.pack(flash); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/flash/delete.ts b/packages/backend/src/server/api/endpoints/flash/delete.ts new file mode 100644 index 0000000000..e94ede9f68 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/flash/delete.ts @@ -0,0 +1,56 @@ +import { Inject, Injectable } from '@nestjs/common'; +import type { FlashsRepository } from '@/models/index.js'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { DI } from '@/di-symbols.js'; +import { ApiError } from '../../error.js'; + +export const meta = { + tags: ['flashs'], + + requireCredential: true, + + kind: 'write:flash', + + errors: { + noSuchFlash: { + message: 'No such flash.', + code: 'NO_SUCH_FLASH', + id: 'de1623ef-bbb3-4289-a71e-14cfa83d9740', + }, + + accessDenied: { + message: 'Access denied.', + code: 'ACCESS_DENIED', + id: '1036ad7b-9f92-4fff-89c3-0e50dc941704', + }, + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + flashId: { type: 'string', format: 'misskey:id' }, + }, + required: ['flashId'], +} as const; + +// eslint-disable-next-line import/no-default-export +@Injectable() +export default class extends Endpoint<typeof meta, typeof paramDef> { + constructor( + @Inject(DI.flashsRepository) + private flashsRepository: FlashsRepository, + ) { + super(meta, paramDef, async (ps, me) => { + const flash = await this.flashsRepository.findOneBy({ id: ps.flashId }); + if (flash == null) { + throw new ApiError(meta.errors.noSuchFlash); + } + if (flash.userId !== me.id) { + throw new ApiError(meta.errors.accessDenied); + } + + await this.flashsRepository.delete(flash.id); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/flash/featured.ts b/packages/backend/src/server/api/endpoints/flash/featured.ts new file mode 100644 index 0000000000..570aef96d2 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/flash/featured.ts @@ -0,0 +1,48 @@ +import { Inject, Injectable } from '@nestjs/common'; +import type { FlashsRepository } from '@/models/index.js'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { FlashEntityService } from '@/core/entities/FlashEntityService.js'; +import { DI } from '@/di-symbols.js'; + +export const meta = { + tags: ['flash'], + + requireCredential: false, + + res: { + type: 'array', + optional: false, nullable: false, + items: { + type: 'object', + optional: false, nullable: false, + ref: 'Flash', + }, + }, +} as const; + +export const paramDef = { + type: 'object', + properties: {}, + required: [], +} as const; + +// eslint-disable-next-line import/no-default-export +@Injectable() +export default class extends Endpoint<typeof meta, typeof paramDef> { + constructor( + @Inject(DI.flashsRepository) + private flashsRepository: FlashsRepository, + + private flashEntityService: FlashEntityService, + ) { + super(meta, paramDef, async (ps, me) => { + const query = this.flashsRepository.createQueryBuilder('flash') + .andWhere('flash.likedCount > 0') + .orderBy('flash.likedCount', 'DESC'); + + const flashs = await query.take(10).getMany(); + + return await this.flashEntityService.packMany(flashs, me); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/flash/like.ts b/packages/backend/src/server/api/endpoints/flash/like.ts new file mode 100644 index 0000000000..5581b8ec60 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/flash/like.ts @@ -0,0 +1,87 @@ +import { Inject, Injectable } from '@nestjs/common'; +import type { FlashsRepository, FlashLikesRepository } from '@/models/index.js'; +import { IdService } from '@/core/IdService.js'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { DI } from '@/di-symbols.js'; +import { ApiError } from '../../error.js'; + +export const meta = { + tags: ['flash'], + + requireCredential: true, + + kind: 'write:flash-likes', + + errors: { + noSuchFlash: { + message: 'No such flash.', + code: 'NO_SUCH_FLASH', + id: 'c07c1491-9161-4c5c-9d75-01906f911f73', + }, + + yourFlash: { + message: 'You cannot like your flash.', + code: 'YOUR_FLASH', + id: '3fd8a0e7-5955-4ba9-85bb-bf3e0c30e13b', + }, + + alreadyLiked: { + message: 'The flash has already been liked.', + code: 'ALREADY_LIKED', + id: '010065cf-ad43-40df-8067-abff9f4686e3', + }, + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + flashId: { type: 'string', format: 'misskey:id' }, + }, + required: ['flashId'], +} as const; + +// eslint-disable-next-line import/no-default-export +@Injectable() +export default class extends Endpoint<typeof meta, typeof paramDef> { + constructor( + @Inject(DI.flashsRepository) + private flashsRepository: FlashsRepository, + + @Inject(DI.flashLikesRepository) + private flashLikesRepository: FlashLikesRepository, + + private idService: IdService, + ) { + super(meta, paramDef, async (ps, me) => { + const flash = await this.flashsRepository.findOneBy({ id: ps.flashId }); + if (flash == null) { + throw new ApiError(meta.errors.noSuchFlash); + } + + if (flash.userId === me.id) { + throw new ApiError(meta.errors.yourFlash); + } + + // if already liked + const exist = await this.flashLikesRepository.findOneBy({ + flashId: flash.id, + userId: me.id, + }); + + if (exist != null) { + throw new ApiError(meta.errors.alreadyLiked); + } + + // Create like + await this.flashLikesRepository.insert({ + id: this.idService.genId(), + createdAt: new Date(), + flashId: flash.id, + userId: me.id, + }); + + this.flashsRepository.increment({ id: flash.id }, 'likedCount', 1); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/flash/my-likes.ts b/packages/backend/src/server/api/endpoints/flash/my-likes.ts new file mode 100644 index 0000000000..f7716ea74a --- /dev/null +++ b/packages/backend/src/server/api/endpoints/flash/my-likes.ts @@ -0,0 +1,68 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import type { FlashLikesRepository } from '@/models/index.js'; +import { QueryService } from '@/core/QueryService.js'; +import { FlashLikeEntityService } from '@/core/entities/FlashLikeEntityService.js'; +import { DI } from '@/di-symbols.js'; + +export const meta = { + tags: ['account', 'flash'], + + requireCredential: true, + + kind: 'read:flash-likes', + + res: { + type: 'array', + optional: false, nullable: false, + items: { + type: 'object', + properties: { + id: { + type: 'string', + optional: false, nullable: false, + format: 'id', + }, + flash: { + type: 'object', + optional: false, nullable: false, + ref: 'Flash', + }, + }, + }, + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, + sinceId: { type: 'string', format: 'misskey:id' }, + untilId: { type: 'string', format: 'misskey:id' }, + }, + required: [], +} as const; + +// eslint-disable-next-line import/no-default-export +@Injectable() +export default class extends Endpoint<typeof meta, typeof paramDef> { + constructor( + @Inject(DI.flashLikesRepository) + private flashLikesRepository: FlashLikesRepository, + + private flashLikeEntityService: FlashLikeEntityService, + private queryService: QueryService, + ) { + super(meta, paramDef, async (ps, me) => { + const query = this.queryService.makePaginationQuery(this.flashLikesRepository.createQueryBuilder('like'), ps.sinceId, ps.untilId) + .andWhere('like.userId = :meId', { meId: me.id }) + .leftJoinAndSelect('like.flash', 'flash'); + + const likes = await query + .take(ps.limit) + .getMany(); + + return this.flashLikeEntityService.packMany(likes, me); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/flash/my.ts b/packages/backend/src/server/api/endpoints/flash/my.ts new file mode 100644 index 0000000000..baed7f000f --- /dev/null +++ b/packages/backend/src/server/api/endpoints/flash/my.ts @@ -0,0 +1,57 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import type { FlashsRepository } from '@/models/index.js'; +import { QueryService } from '@/core/QueryService.js'; +import { FlashEntityService } from '@/core/entities/FlashEntityService.js'; +import { DI } from '@/di-symbols.js'; + +export const meta = { + tags: ['account', 'flash'], + + requireCredential: true, + + kind: 'read:flash', + + res: { + type: 'array', + optional: false, nullable: false, + items: { + type: 'object', + optional: false, nullable: false, + ref: 'Flash', + }, + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, + sinceId: { type: 'string', format: 'misskey:id' }, + untilId: { type: 'string', format: 'misskey:id' }, + }, + required: [], +} as const; + +// eslint-disable-next-line import/no-default-export +@Injectable() +export default class extends Endpoint<typeof meta, typeof paramDef> { + constructor( + @Inject(DI.flashsRepository) + private flashsRepository: FlashsRepository, + + private flashEntityService: FlashEntityService, + private queryService: QueryService, + ) { + super(meta, paramDef, async (ps, me) => { + const query = this.queryService.makePaginationQuery(this.flashsRepository.createQueryBuilder('flash'), ps.sinceId, ps.untilId) + .andWhere('flash.userId = :meId', { meId: me.id }); + + const flashs = await query + .take(ps.limit) + .getMany(); + + return await this.flashEntityService.packMany(flashs); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/flash/show.ts b/packages/backend/src/server/api/endpoints/flash/show.ts new file mode 100644 index 0000000000..48114c5a60 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/flash/show.ts @@ -0,0 +1,60 @@ +import { IsNull } from 'typeorm'; +import { Inject, Injectable } from '@nestjs/common'; +import type { UsersRepository, FlashsRepository } from '@/models/index.js'; +import type { Flash } from '@/models/entities/Flash.js'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { FlashEntityService } from '@/core/entities/FlashEntityService.js'; +import { DI } from '@/di-symbols.js'; +import { ApiError } from '../../error.js'; + +export const meta = { + tags: ['flashs'], + + requireCredential: false, + + res: { + type: 'object', + optional: false, nullable: false, + ref: 'Flash', + }, + + errors: { + noSuchFlash: { + message: 'No such flash.', + code: 'NO_SUCH_FLASH', + id: 'f0d34a1a-d29a-401d-90ba-1982122b5630', + }, + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + flashId: { type: 'string', format: 'misskey:id' }, + }, + required: ['flashId'], +} as const; + +// eslint-disable-next-line import/no-default-export +@Injectable() +export default class extends Endpoint<typeof meta, typeof paramDef> { + constructor( + @Inject(DI.usersRepository) + private usersRepository: UsersRepository, + + @Inject(DI.flashsRepository) + private flashsRepository: FlashsRepository, + + private flashEntityService: FlashEntityService, + ) { + super(meta, paramDef, async (ps, me) => { + const flash = await this.flashsRepository.findOneBy({ id: ps.flashId }); + + if (flash == null) { + throw new ApiError(meta.errors.noSuchFlash); + } + + return await this.flashEntityService.pack(flash, me); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/flash/unlike.ts b/packages/backend/src/server/api/endpoints/flash/unlike.ts new file mode 100644 index 0000000000..b994f5d347 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/flash/unlike.ts @@ -0,0 +1,68 @@ +import { Inject, Injectable } from '@nestjs/common'; +import type { FlashsRepository, FlashLikesRepository } from '@/models/index.js'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { DI } from '@/di-symbols.js'; +import { ApiError } from '../../error.js'; + +export const meta = { + tags: ['flash'], + + requireCredential: true, + + kind: 'write:flash-likes', + + errors: { + noSuchFlash: { + message: 'No such flash.', + code: 'NO_SUCH_FLASH', + id: 'afe8424a-a69e-432d-a5f2-2f0740c62410', + }, + + notLiked: { + message: 'You have not liked that flash.', + code: 'NOT_LIKED', + id: '755f25a7-9871-4f65-9f34-51eaad9ae0ac', + }, + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + flashId: { type: 'string', format: 'misskey:id' }, + }, + required: ['flashId'], +} as const; + +// eslint-disable-next-line import/no-default-export +@Injectable() +export default class extends Endpoint<typeof meta, typeof paramDef> { + constructor( + @Inject(DI.flashsRepository) + private flashsRepository: FlashsRepository, + + @Inject(DI.flashLikesRepository) + private flashLikesRepository: FlashLikesRepository, + ) { + super(meta, paramDef, async (ps, me) => { + const flash = await this.flashsRepository.findOneBy({ id: ps.flashId }); + if (flash == null) { + throw new ApiError(meta.errors.noSuchFlash); + } + + const exist = await this.flashLikesRepository.findOneBy({ + flashId: flash.id, + userId: me.id, + }); + + if (exist == null) { + throw new ApiError(meta.errors.notLiked); + } + + // Delete like + await this.flashLikesRepository.delete(exist.id); + + this.flashsRepository.decrement({ id: flash.id }, 'likedCount', 1); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/flash/update.ts b/packages/backend/src/server/api/endpoints/flash/update.ts new file mode 100644 index 0000000000..9ab17a61e8 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/flash/update.ts @@ -0,0 +1,78 @@ +import ms from 'ms'; +import { Not } from 'typeorm'; +import { Inject, Injectable } from '@nestjs/common'; +import type { FlashsRepository, DriveFilesRepository } from '@/models/index.js'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { DI } from '@/di-symbols.js'; +import { ApiError } from '../../error.js'; + +export const meta = { + tags: ['flash'], + + requireCredential: true, + + kind: 'write:flash', + + limit: { + duration: ms('1hour'), + max: 300, + }, + + errors: { + noSuchFlash: { + message: 'No such flash.', + code: 'NO_SUCH_FLASH', + id: '611e13d2-309e-419a-a5e4-e0422da39b02', + }, + + accessDenied: { + message: 'Access denied.', + code: 'ACCESS_DENIED', + id: '08e60c88-5948-478e-a132-02ec701d67b2', + }, + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + flashId: { type: 'string', format: 'misskey:id' }, + title: { type: 'string' }, + summary: { type: 'string' }, + script: { type: 'string' }, + permissions: { type: 'array', items: { + type: 'string', + } }, + }, + required: ['flashId', 'title', 'summary', 'script', 'permissions'], +} as const; + +// eslint-disable-next-line import/no-default-export +@Injectable() +export default class extends Endpoint<typeof meta, typeof paramDef> { + constructor( + @Inject(DI.flashsRepository) + private flashsRepository: FlashsRepository, + + @Inject(DI.driveFilesRepository) + private driveFilesRepository: DriveFilesRepository, + ) { + super(meta, paramDef, async (ps, me) => { + const flash = await this.flashsRepository.findOneBy({ id: ps.flashId }); + if (flash == null) { + throw new ApiError(meta.errors.noSuchFlash); + } + if (flash.userId !== me.id) { + throw new ApiError(meta.errors.accessDenied); + } + + await this.flashsRepository.update(flash.id, { + updatedAt: new Date(), + title: ps.title, + summary: ps.summary, + script: ps.script, + permissions: ps.permissions, + }); + }); + } +} diff --git a/packages/frontend/src/components/MkAsUi.vue b/packages/frontend/src/components/MkAsUi.vue new file mode 100644 index 0000000000..bc1e25957b --- /dev/null +++ b/packages/frontend/src/components/MkAsUi.vue @@ -0,0 +1,107 @@ +<template> +<div> + <div v-if="c.type === 'root'" :class="$style.root"> + <template v-for="child in c.children" :key="child"> + <MkAsUi v-if="!g(child).hidden" :component="g(child)" :components="props.components" :size="size"/> + </template> + </div> + <span v-else-if="c.type === 'text'" :class="{ [$style.fontSerif]: c.font === 'serif', [$style.fontMonospace]: c.font === 'monospace' }" :style="{ fontSize: c.size ? `${c.size * 100}%` : null, fontWeight: c.bold ? 'bold' : null, color: c.color ?? null }">{{ c.text }}</span> + <Mfm v-else-if="c.type === 'mfm'" :class="{ [$style.fontSerif]: c.font === 'serif', [$style.fontMonospace]: c.font === 'monospace' }" :style="{ fontSize: c.size ? `${c.size * 100}%` : null, color: c.color ?? null }" :text="c.text"/> + <MkButton v-else-if="c.type === 'button'" :primary="c.primary" :rounded="c.rounded" :small="size === 'small'" @click="c.onClick">{{ c.text }}</MkButton> + <div v-else-if="c.type === 'buttons'" style="display: flex; gap: 8px; flex-wrap: wrap;"> + <MkButton v-for="button in c.buttons" :primary="button.primary" :rounded="button.rounded" :small="size === 'small'" @click="button.onClick">{{ button.text }}</MkButton> + </div> + <MkSwitch v-else-if="c.type === 'switch'" :model-value="valueForSwitch" @update:model-value="onSwitchUpdate"> + <template v-if="c.label" #label>{{ c.label }}</template> + <template v-if="c.caption" #caption>{{ c.caption }}</template> + </MkSwitch> + <MkTextarea v-else-if="c.type === 'textarea'" :model-value="c.default" @update:model-value="c.onInput"> + <template v-if="c.label" #label>{{ c.label }}</template> + <template v-if="c.caption" #caption>{{ c.caption }}</template> + </MkTextarea> + <MkInput v-else-if="c.type === 'textInput'" :small="size === 'small'" :model-value="c.default" @update:model-value="c.onInput"> + <template v-if="c.label" #label>{{ c.label }}</template> + <template v-if="c.caption" #caption>{{ c.caption }}</template> + </MkInput> + <MkInput v-else-if="c.type === 'numberInput'" :small="size === 'small'" :model-value="c.default" type="number" @update:model-value="c.onInput"> + <template v-if="c.label" #label>{{ c.label }}</template> + <template v-if="c.caption" #caption>{{ c.caption }}</template> + </MkInput> + <MkSelect v-else-if="c.type === 'select'" :small="size === 'small'" :model-value="c.default" @update:model-value="c.onChange"> + <template v-if="c.label" #label>{{ c.label }}</template> + <template v-if="c.caption" #caption>{{ c.caption }}</template> + <option v-for="item in c.items" :key="item.value" :value="item.value">{{ item.text }}</option> + </MkSelect> + <MkButton v-else-if="c.type === 'postFormButton'" :primary="c.primary" :rounded="c.rounded" :small="size === 'small'" @click="openPostForm">{{ c.text }}</MkButton> + <div v-else-if="c.type === 'container'" :class="[$style.container, { [$style.fontSerif]: c.font === 'serif', [$style.fontMonospace]: c.font === 'monospace', [$style.containerCenter]: c.align === 'center' }]" :style="{ backgroundColor: c.bgColor ?? null, color: c.fgColor ?? null, borderWidth: c.borderWidth ? `${c.borderWidth}px` : 0, borderColor: c.borderColor ?? 'var(--divider)', padding: c.padding ? `${c.padding}px` : 0, borderRadius: c.rounded ? '8px' : 0 }"> + <template v-for="child in c.children" :key="child"> + <MkAsUi v-if="!g(child).hidden" :component="g(child)" :components="props.components" :size="size"/> + </template> + </div> +</div> +</template> + +<script lang="ts" setup> +import { computed, defineAsyncComponent, onMounted, onUnmounted, Ref } from 'vue'; +import * as os from '@/os'; +import MkButton from '@/components/MkButton.vue'; +import MkInput from '@/components/form/input.vue'; +import MkSwitch from '@/components/form/switch.vue'; +import MkTextarea from '@/components/form/textarea.vue'; +import MkSelect from '@/components/form/select.vue'; +import { AsUiComponent } from '@/scripts/aiscript/ui'; + +const props = withDefaults(defineProps<{ + component: AsUiComponent; + components: Ref<AsUiComponent>[]; + size: 'small' | 'medium' | 'large'; +}>(), { + size: 'medium', +}); + +const c = props.component; + +function g(id) { + return props.components.find(x => x.value.id === id).value; +} + +let valueForSwitch = $ref(c.default ?? false); + +function onSwitchUpdate(v) { + valueForSwitch = v; + if (c.onChange) c.onChange(v); +} + +function openPostForm() { + os.post({ + initialText: c.form.text, + instant: true, + }); +} +</script> + +<style lang="scss" module> +.root { + display: flex; + flex-direction: column; + gap: 12px; +} + +.container { + display: flex; + flex-direction: column; + gap: 12px; +} + +.containerCenter { + text-align: center; +} + +.fontSerif { + font-family: serif; +} + +.fontMonospace { + font-family: Fira code, Fira Mono, Consolas, Menlo, Courier, monospace; +} +</style> diff --git a/packages/frontend/src/components/MkButton.vue b/packages/frontend/src/components/MkButton.vue index daf47e12d4..f9602de787 100644 --- a/packages/frontend/src/components/MkButton.vue +++ b/packages/frontend/src/components/MkButton.vue @@ -2,7 +2,7 @@ <button v-if="!link" ref="el" class="bghgjjyj _button" - :class="{ inline, primary, gradate, danger, rounded, full, small }" + :class="{ inline, primary, gradate, danger, rounded, full, small, large, asLike }" :type="type" @click="emit('click', $event)" @mousedown="onMousedown" @@ -41,6 +41,8 @@ const props = defineProps<{ danger?: boolean; full?: boolean; small?: boolean; + large?: boolean; + asLike?: boolean; }>(); const emit = defineEmits<{ @@ -131,6 +133,11 @@ function onMousedown(evt: MouseEvent): void { padding: 6px 12px; } + &.large { + font-size: 100%; + padding: 8px 16px; + } + &.full { width: 100%; } @@ -153,6 +160,37 @@ function onMousedown(evt: MouseEvent): void { } } + &.asLike { + background: rgba(255, 86, 125, 0.07); + color: #ff002f; + + &:not(:disabled):hover { + background: rgba(255, 74, 116, 0.11); + } + + &:not(:disabled):active { + background: rgba(224, 57, 96, 0.125); + } + + > .ripples { + ::v-deep(div) { + background: rgba(255, 60, 106, 0.15); + } + } + + &.primary { + background: rgb(241 97 132); + + &:not(:disabled):hover { + background: rgb(241 92 128); + } + + &:not(:disabled):active { + background: rgb(241 92 128); + } + } + } + &.gradate { font-weight: bold; color: var(--fgOnAccent) !important; diff --git a/packages/frontend/src/components/MkChartLegend.vue b/packages/frontend/src/components/MkChartLegend.vue index f33f753723..8d2a2be8e8 100644 --- a/packages/frontend/src/components/MkChartLegend.vue +++ b/packages/frontend/src/components/MkChartLegend.vue @@ -59,7 +59,7 @@ defineExpose({ &.disabled { text-decoration: line-through; - opacity: 0.6; + opacity: 0.5; } > .box { diff --git a/packages/frontend/src/components/MkFlashPreview.vue b/packages/frontend/src/components/MkFlashPreview.vue new file mode 100644 index 0000000000..1a82ffe5ab --- /dev/null +++ b/packages/frontend/src/components/MkFlashPreview.vue @@ -0,0 +1,112 @@ +<template> +<MkA :to="`/play/${flash.id}`" class="vhpxefrk _block" tabindex="-1"> + <article> + <header> + <h1 :title="flash.title">{{ flash.title }}</h1> + </header> + <p v-if="flash.summary" :title="flash.summary">{{ flash.summary.length > 85 ? flash.summary.slice(0, 85) + '…' : flash.summary }}</p> + <footer> + <img class="icon" :src="flash.user.avatarUrl"/> + <p>{{ userName(flash.user) }}</p> + </footer> + </article> +</MkA> +</template> + +<script lang="ts" setup> +import { } from 'vue'; +import * as misskey from 'misskey-js'; +import { userName } from '@/filters/user'; +import * as os from '@/os'; + +const props = defineProps<{ + //flash: misskey.entities.Flash; + flash: any; +}>(); +</script> + +<style lang="scss" scoped> +.vhpxefrk { + display: block; + + &:hover { + text-decoration: none; + color: var(--accent); + } + + > article { + padding: 16px; + + > header { + margin-bottom: 8px; + + > h1 { + margin: 0; + font-size: 1em; + color: var(--urlPreviewTitle); + } + } + + > p { + margin: 0; + color: var(--urlPreviewText); + font-size: 0.8em; + } + + > footer { + margin-top: 8px; + height: 16px; + + > img { + display: inline-block; + width: 16px; + height: 16px; + margin-right: 4px; + vertical-align: top; + } + + > p { + display: inline-block; + margin: 0; + color: var(--urlPreviewInfo); + font-size: 0.8em; + line-height: 16px; + vertical-align: top; + } + } + } + + @media (max-width: 700px) { + } + + @media (max-width: 550px) { + font-size: 12px; + + > article { + padding: 12px; + } + } + + @media (max-width: 500px) { + font-size: 10px; + + > article { + padding: 8px; + + > header { + margin-bottom: 4px; + } + + > footer { + margin-top: 4px; + + > img { + width: 12px; + height: 12px; + } + } + } + } +} + +</style> diff --git a/packages/frontend/src/components/MkLaunchPad.vue b/packages/frontend/src/components/MkLaunchPad.vue index 3ea90712a0..aab7631e36 100644 --- a/packages/frontend/src/components/MkLaunchPad.vue +++ b/packages/frontend/src/components/MkLaunchPad.vue @@ -50,7 +50,7 @@ const menu = defaultStore.state.menu; const items = Object.keys(navbarItemDef).filter(k => !menu.includes(k)).map(k => navbarItemDef[k]).filter(def => def.show == null ? true : def.show).map(def => ({ type: def.to ? 'link' : 'button', - text: i18n.ts[def.title], + text: def.title, icon: def.icon, to: def.to, action: def.action, diff --git a/packages/frontend/src/components/MkPagePreview.vue b/packages/frontend/src/components/MkPagePreview.vue index 009582e540..1eb61d4344 100644 --- a/packages/frontend/src/components/MkPagePreview.vue +++ b/packages/frontend/src/components/MkPagePreview.vue @@ -14,22 +14,15 @@ </MkA> </template> -<script lang="ts"> -import { defineComponent } from 'vue'; +<script lang="ts" setup> +import { } from 'vue'; +import * as misskey from 'misskey-js'; import { userName } from '@/filters/user'; import * as os from '@/os'; -export default defineComponent({ - props: { - page: { - type: Object, - required: true, - }, - }, - methods: { - userName, - }, -}); +const props = defineProps<{ + page: misskey.entities.Page; +}>(); </script> <style lang="scss" scoped> diff --git a/packages/frontend/src/components/form/select.vue b/packages/frontend/src/components/form/select.vue index c8cdd9e508..068ca2ebc6 100644 --- a/packages/frontend/src/components/form/select.vue +++ b/packages/frontend/src/components/form/select.vue @@ -126,7 +126,7 @@ const onClick = (ev: MouseEvent) => { const pushOption = (option: VNode) => { menu.push({ text: option.children, - active: v.value === option.props.value, + active: computed(() => v.value === option.props.value), action: () => { v.value = option.props.value; }, diff --git a/packages/frontend/src/navbar.ts b/packages/frontend/src/navbar.ts index 31e6cd64a4..efc0abfc6e 100644 --- a/packages/frontend/src/navbar.ts +++ b/packages/frontend/src/navbar.ts @@ -8,97 +8,102 @@ import { unisonReload } from '@/scripts/unison-reload'; export const navbarItemDef = reactive({ notifications: { - title: 'notifications', + title: i18n.ts.notifications, icon: 'ti ti-bell', show: computed(() => $i != null), indicated: computed(() => $i != null && $i.hasUnreadNotification), to: '/my/notifications', }, messaging: { - title: 'messaging', + title: i18n.ts.messaging, icon: 'ti ti-messages', show: computed(() => $i != null), indicated: computed(() => $i != null && $i.hasUnreadMessagingMessage), to: '/my/messaging', }, drive: { - title: 'drive', + title: i18n.ts.drive, icon: 'ti ti-cloud', show: computed(() => $i != null), to: '/my/drive', }, followRequests: { - title: 'followRequests', + title: i18n.ts.followRequests, icon: 'ti ti-user-plus', show: computed(() => $i != null && $i.isLocked), indicated: computed(() => $i != null && $i.hasPendingReceivedFollowRequest), to: '/my/follow-requests', }, explore: { - title: 'explore', + title: i18n.ts.explore, icon: 'ti ti-hash', to: '/explore', }, announcements: { - title: 'announcements', + title: i18n.ts.announcements, icon: 'ti ti-speakerphone', indicated: computed(() => $i != null && $i.hasUnreadAnnouncement), to: '/announcements', }, search: { - title: 'search', + title: i18n.ts.search, icon: 'ti ti-search', action: () => search(), }, lists: { - title: 'lists', + title: i18n.ts.lists, icon: 'ti ti-list', show: computed(() => $i != null), to: '/my/lists', }, /* groups: { - title: 'groups', + title: i18n.ts.groups, icon: 'ti ti-users', show: computed(() => $i != null), to: '/my/groups', }, */ antennas: { - title: 'antennas', + title: i18n.ts.antennas, icon: 'ti ti-antenna', show: computed(() => $i != null), to: '/my/antennas', }, favorites: { - title: 'favorites', + title: i18n.ts.favorites, icon: 'ti ti-star', show: computed(() => $i != null), to: '/my/favorites', }, pages: { - title: 'pages', + title: i18n.ts.pages, icon: 'ti ti-news', to: '/pages', }, + play: { + title: 'Play', + icon: 'ti ti-player-play', + to: '/play', + }, gallery: { - title: 'gallery', + title: i18n.ts.gallery, icon: 'ti ti-icons', to: '/gallery', }, clips: { - title: 'clip', + title: i18n.ts.clip, icon: 'ti ti-paperclip', show: computed(() => $i != null), to: '/my/clips', }, channels: { - title: 'channel', + title: i18n.ts.channel, icon: 'ti ti-device-tv', to: '/channels', }, ui: { - title: 'switchUi', + title: i18n.ts.switchUi, icon: 'ti ti-devices', action: (ev) => { os.popupMenu([{ @@ -126,7 +131,7 @@ export const navbarItemDef = reactive({ }, }, reload: { - title: 'reload', + title: i18n.ts.reload, icon: 'ti ti-refresh', action: (ev) => { location.reload(); diff --git a/packages/frontend/src/pages/flash/flash-edit.vue b/packages/frontend/src/pages/flash/flash-edit.vue new file mode 100644 index 0000000000..561331e002 --- /dev/null +++ b/packages/frontend/src/pages/flash/flash-edit.vue @@ -0,0 +1,111 @@ +<template> +<MkStickyContainer> + <template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template> + <MkSpacer :content-max="700"> + <MkInput v-model="title" class="_formBlock"> + <template #label>{{ i18n.ts._play.title }}</template> + </MkInput> + <MkTextarea v-model="summary" class="_formBlock"> + <template #label>{{ i18n.ts._play.summary }}</template> + </MkTextarea> + <MkTextarea v-model="script" class="_formBlock _monospace" tall spellcheck="false"> + <template #label>{{ i18n.ts._play.script }}</template> + </MkTextarea> + <div style="display: flex; gap: var(--margin); flex-wrap: wrap;"> + <MkButton primary @click="save"><i class="ti ti-check"></i> {{ i18n.ts.save }}</MkButton> + <MkButton @click="show"><i class="ti ti-eye"></i> {{ i18n.ts.show }}</MkButton> + </div> + </MkSpacer> +</MkStickyContainer> +</template> + +<script lang="ts" setup> +import { computed, onDeactivated, onUnmounted, Ref, ref, watch } from 'vue'; +import MkButton from '@/components/MkButton.vue'; +import * as os from '@/os'; +import { url } from '@/config'; +import { i18n } from '@/i18n'; +import { definePageMetadata } from '@/scripts/page-metadata'; +import MkTextarea from '@/components/form/textarea.vue'; +import MkInput from '@/components/form/input.vue'; +import { useRouter } from '@/router'; + +const router = useRouter(); + +const props = defineProps<{ + id?: string; +}>(); + +let flash = $ref(null); + +if (props.id) { + flash = await os.api('flash/show', { + flashId: props.id, + }); +} + +let title = $ref(flash?.title ?? 'New Play'); +let summary = $ref(flash?.summary ?? ''); +let permissions = $ref(flash?.permissions ?? []); +let script = $ref(flash?.script ?? `/// @ 0.12.0 + +var name = "" + +Ui:render([ + Ui:C:textInput({ + label: "Your name" + onInput: @(v) { name = v } + }) + Ui:C:button({ + text: "Hello" + onClick: @() { + Mk:dialog(null \`Hello, {name}!\`) + } + }) +]) +`); + +async function save() { + if (flash) { + os.apiWithDialog('flash/update', { + flashId: props.id, + title, + summary, + permissions, + script, + }); + } else { + const created = await os.apiWithDialog('flash/create', { + title, + summary, + permissions, + script, + }); + router.push('/play/' + created.id + '/edit'); + } +} + +function show() { + if (flash == null) { + os.alert({ + text: 'Please save', + }); + } else { + os.pageWindow(`/play/${flash.id}`); + } +} + +const headerActions = $computed(() => []); + +const headerTabs = $computed(() => []); + +definePageMetadata(computed(() => flash ? { + title: i18n.ts._play.edit + ': ' + flash.title, +} : { + title: i18n.ts._play.new, +})); +</script> + +<style lang="scss" scoped> + +</style> diff --git a/packages/frontend/src/pages/flash/flash-index.vue b/packages/frontend/src/pages/flash/flash-index.vue new file mode 100644 index 0000000000..bc4828f416 --- /dev/null +++ b/packages/frontend/src/pages/flash/flash-index.vue @@ -0,0 +1,99 @@ +<template> +<MkStickyContainer> + <template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template> + <MkSpacer :content-max="700"> + <div v-if="tab === 'featured'" class=""> + <MkPagination v-slot="{items}" :pagination="featuredFlashsPagination"> + <MkFlashPreview v-for="flash in items" :key="flash.id" class="" :flash="flash"/> + </MkPagination> + </div> + + <div v-else-if="tab === 'my'" class="my"> + <MkButton class="new" @click="create()"><i class="ti ti-plus"></i></MkButton> + <MkPagination v-slot="{items}" :pagination="myFlashsPagination"> + <MkFlashPreview v-for="flash in items" :key="flash.id" class="" :flash="flash"/> + </MkPagination> + </div> + + <div v-else-if="tab === 'liked'" class=""> + <MkPagination v-slot="{items}" :pagination="likedFlashsPagination"> + <MkFlashPreview v-for="like in items" :key="like.flash.id" class="" :flash="like.flash"/> + </MkPagination> + </div> + </MkSpacer> +</MkStickyContainer> +</template> + +<script lang="ts" setup> +import { computed, inject } from 'vue'; +import MkFlashPreview from '@/components/MkFlashPreview.vue'; +import MkPagination from '@/components/MkPagination.vue'; +import MkButton from '@/components/MkButton.vue'; +import { useRouter } from '@/router'; +import { i18n } from '@/i18n'; +import { definePageMetadata } from '@/scripts/page-metadata'; + +const router = useRouter(); + +let tab = $ref('featured'); + +const featuredFlashsPagination = { + endpoint: 'flash/featured' as const, + noPaging: true, +}; +const myFlashsPagination = { + endpoint: 'flash/my' as const, + limit: 5, +}; +const likedFlashsPagination = { + endpoint: 'flash/my-likes' as const, + limit: 5, +}; + +function create() { + router.push('/play/new'); +} + +const headerActions = $computed(() => [{ + icon: 'ti ti-plus', + text: i18n.ts.create, + handler: create, +}]); + +const headerTabs = $computed(() => [{ + key: 'featured', + title: i18n.ts._play.featured, + icon: 'fas fa-fire-alt', +}, { + key: 'my', + title: i18n.ts._play.my, + icon: 'ti ti-edit', +}, { + key: 'liked', + title: i18n.ts._play.liked, + icon: 'ti ti-heart', +}]); + +definePageMetadata(computed(() => ({ + title: 'Play', + icon: 'ti ti-player-play', +}))); +</script> + +<style lang="scss" scoped> +.rknalgpo { + &.my .ckltabjg:first-child { + margin-top: 16px; + } + + .ckltabjg:not(:last-child) { + margin-bottom: 8px; + } + + @media (min-width: 500px) { + .ckltabjg:not(:last-child) { + margin-bottom: 16px; + } + } +} +</style> diff --git a/packages/frontend/src/pages/flash/flash.vue b/packages/frontend/src/pages/flash/flash.vue new file mode 100644 index 0000000000..9495206c54 --- /dev/null +++ b/packages/frontend/src/pages/flash/flash.vue @@ -0,0 +1,291 @@ +<template> +<MkStickyContainer> + <template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template> + <MkSpacer :content-max="700"> + <Transition :name="$store.state.animation ? 'fade' : ''" mode="out-in"> + <div v-if="flash" :key="flash.id"> + <Transition :name="$store.state.animation ? 'zoom' : ''" mode="out-in"> + <div v-if="started" :class="$style.started"> + <div class="main _panel"> + <MkAsUi v-if="root" :component="root" :components="components"/> + </div> + <div class="actions _panel"> + <MkButton v-if="flash.isLiked" v-tooltip="i18n.ts.unlike" as-like class="button" rounded primary @click="unlike()"><i class="ti ti-heart-off"></i><span v-if="flash.likedCount > 0" class="count">{{ flash.likedCount }}</span></MkButton> + <MkButton v-else v-tooltip="i18n.ts.like" as-like class="button" rounded @click="like()"><i class="ti ti-heart"></i><span v-if="flash.likedCount > 0" class="count">{{ flash.likedCount }}</span></MkButton> + <MkButton v-tooltip="i18n.ts.shareWithNote" class="button" rounded @click="shareWithNote"><i class="ti ti-repeat ti-fw"></i></MkButton> + <MkButton v-tooltip="i18n.ts.share" class="button" rounded @click="share"><i class="ti ti-share ti-fw"></i></MkButton> + </div> + </div> + <div v-else :class="$style.ready"> + <div class="_panel main"> + <div class="title">{{ flash.title }}</div> + <div class="summary">{{ flash.summary }}</div> + <MkButton class="start" gradate rounded large @click="start">Play</MkButton> + <div class="info"> + <span v-tooltip="i18n.ts.numberOfLikes"><i class="ti ti-heart"></i> {{ flash.likedCount }}</span> + </div> + </div> + </div> + </Transition> + <FormFolder class="_formBlock"> + <template #icon><i class="ti ti-code"></i></template> + <template #label>{{ i18n.ts._play.viewSource }}</template> + + <MkTextarea :model-value="flash.script" readonly tall class="_monospace" spellcheck="false"></MkTextarea> + </FormFolder> + <div :class="$style.footer"> + <Mfm :text="`By @${flash.user.username}`"/> + <div class="date"> + <div v-if="flash.createdAt != flash.updatedAt"><i class="ti ti-clock"></i> {{ i18n.ts.updatedAt }}: <MkTime :time="flash.updatedAt" mode="detail"/></div> + <div><i class="ti ti-clock"></i> {{ i18n.ts.createdAt }}: <MkTime :time="flash.createdAt" mode="detail"/></div> + </div> + </div> + <MkA v-if="$i && $i.id === flash.userId" :to="`/play/${flash.id}/edit`" style="color: var(--accent);">{{ i18n.ts._play.editThisPage }}</MkA> + <MkAd :prefer="['horizontal', 'horizontal-big']"/> + </div> + <MkError v-else-if="error" @retry="fetchPage()"/> + <MkLoading v-else/> + </Transition> + </MkSpacer> +</MkStickyContainer> +</template> + +<script lang="ts" setup> +import { computed, onDeactivated, onUnmounted, Ref, ref, watch } from 'vue'; +import { Interpreter, Parser, utils, values } from '@syuilo/aiscript'; +import MkButton from '@/components/MkButton.vue'; +import * as os from '@/os'; +import { url } from '@/config'; +import MkFollowButton from '@/components/MkFollowButton.vue'; +import MkContainer from '@/components/MkContainer.vue'; +import MkPagination from '@/components/MkPagination.vue'; +import MkPagePreview from '@/components/MkPagePreview.vue'; +import { i18n } from '@/i18n'; +import { definePageMetadata } from '@/scripts/page-metadata'; +import MkAsUi from '@/components/MkAsUi.vue'; +import { AsUiComponent, AsUiRoot, patch, registerAsUiLib, render } from '@/scripts/aiscript/ui'; +import { createAiScriptEnv } from '@/scripts/aiscript/api'; +import FormFolder from '@/components/form/folder.vue'; +import MkTextarea from '@/components/form/textarea.vue'; + +const props = defineProps<{ + id: string; +}>(); + +let flash = $ref(null); +let error = $ref(null); + +function fetchFlash() { + flash = null; + os.api('flash/show', { + flashId: props.id, + }).then(_flash => { + flash = _flash; + }).catch(err => { + error = err; + }); +} + +function share() { + navigator.share({ + title: flash.title, + text: flash.summary, + url: `${url}/play/${flash.id}`, + }); +} + +function shareWithNote() { + os.post({ + initialText: `${flash.title} ${url}/play/${flash.id}`, + }); +} + +function like() { + os.apiWithDialog('flash/like', { + flashId: flash.id, + }).then(() => { + flash.isLiked = true; + flash.likedCount++; + }); +} + +async function unlike() { + const confirm = await os.confirm({ + type: 'warning', + text: i18n.ts.unlikeConfirm, + }); + if (confirm.canceled) return; + os.apiWithDialog('flash/unlike', { + flashId: flash.id, + }).then(() => { + flash.isLiked = false; + flash.likedCount--; + }); +} + +watch(() => props.id, fetchFlash, { immediate: true }); + +const parser = new Parser(); + +let started = $ref(false); +let aiscript = $shallowRef<Interpreter | null>(null); +const root = ref<AsUiRoot>(); +const components: Ref<AsUiComponent>[] = []; + +function start() { + started = true; + run(); +} + +async function run() { + if (aiscript) aiscript.abort(); + + aiscript = new Interpreter({ + ...createAiScriptEnv({ + storageKey: 'flash:' + flash.id, + }), + ...registerAsUiLib(components, (_root) => { + root.value = _root.value; + }), + }, { + in: (q) => { + return new Promise(ok => { + os.inputText({ + title: q, + }).then(({ canceled, result: a }) => { + ok(a); + }); + }); + }, + out: (value) => { + // nop + }, + log: (type, params) => { + // nop + }, + }); + + let ast; + try { + ast = parser.parse(flash.script); + } catch (err) { + os.alert({ + type: 'error', + text: 'Syntax error :(', + }); + return; + } + try { + await aiscript.exec(ast); + } catch (err) { + os.alert({ + type: 'error', + title: 'AiScript Error', + text: err.message, + }); + } +} + +onDeactivated(() => { + if (aiscript) aiscript.abort(); +}); + +onUnmounted(() => { + if (aiscript) aiscript.abort(); +}); + +const headerActions = $computed(() => []); + +const headerTabs = $computed(() => []); + +definePageMetadata(computed(() => flash ? { + title: flash.title, + avatar: flash.user, + path: `/play/${flash.id}`, + share: { + title: flash.title, + text: flash.summary, + }, +} : null)); +</script> + +<style lang="scss" module> +.ready { + &:global { + > .main { + padding: 32px; + + > .title { + font-size: 1.4em; + font-weight: bold; + margin-bottom: 1rem; + text-align: center; + } + + > .summary { + font-size: 1.1em; + text-align: center; + } + + > .start { + margin: 1em auto 1em auto; + } + + > .info { + text-align: center; + } + } + } +} + +.footer { + margin-top: 16px; + + &:global { + > .date { + margin: 8px 0; + opacity: 0.6; + } + } +} + +.started { + &:global { + > .main { + padding: 32px; + } + + > .actions { + display: flex; + justify-content: center; + gap: 12px; + margin-top: 16px; + padding: 16px; + } + } +} +</style> + +<style lang="scss" scoped> +.fade-enter-active, +.fade-leave-active { + transition: opacity 0.125s ease; +} +.fade-enter-from, +.fade-leave-to { + opacity: 0; +} + +.zoom-enter-active, +.zoom-leave-active { + transition: opacity 0.3s cubic-bezier(0,0,.35,1), transform 0.3s cubic-bezier(0,0,.35,1); +} +.zoom-enter-from { + opacity: 0; + transform: scale(0.7); +} +.zoom-leave-to { + opacity: 0; + transform: scale(1.3); +} +</style> diff --git a/packages/frontend/src/pages/page.vue b/packages/frontend/src/pages/page.vue index e01dae2cd9..11ed8f9f44 100644 --- a/packages/frontend/src/pages/page.vue +++ b/packages/frontend/src/pages/page.vue @@ -18,8 +18,8 @@ </div> <div class="actions"> <div class="like"> - <MkButton v-if="page.isLiked" v-tooltip="i18n.ts._pages.unlike" class="button" primary @click="unlike()"><i class="ti ti-heart-off"></i><span v-if="page.likedCount > 0" class="count">{{ page.likedCount }}</span></MkButton> - <MkButton v-else v-tooltip="i18n.ts._pages.like" class="button" @click="like()"><i class="ti ti-heart"></i><span v-if="page.likedCount > 0" class="count">{{ page.likedCount }}</span></MkButton> + <MkButton v-if="page.isLiked" v-tooltip="i18n.ts._pages.unlike" class="button" as-like primary @click="unlike()"><i class="ti ti-heart-off"></i><span v-if="page.likedCount > 0" class="count">{{ page.likedCount }}</span></MkButton> + <MkButton v-else v-tooltip="i18n.ts._pages.like" class="button" as-like @click="like()"><i class="ti ti-heart"></i><span v-if="page.likedCount > 0" class="count">{{ page.likedCount }}</span></MkButton> </div> <div class="other"> <button v-tooltip="i18n.ts.shareWithNote" v-click-anime class="_button" @click="shareWithNote"><i class="ti ti-repeat ti-fw"></i></button> @@ -207,20 +207,6 @@ definePageMetadata(computed(() => page ? { padding: 16px 0 0 0; border-top: solid 0.5px var(--divider); - > .like { - > .button { - --accent: rgb(241 97 132); - --X8: rgb(241 92 128); - --buttonBg: rgb(216 71 106 / 5%); - --buttonHoverBg: rgb(216 71 106 / 10%); - color: #ff002f; - - ::v-deep(.count) { - margin-left: 0.5em; - } - } - } - > .other { margin-left: auto; diff --git a/packages/frontend/src/pages/scratchpad.vue b/packages/frontend/src/pages/scratchpad.vue index 9db17efc03..7d097fbaaa 100644 --- a/packages/frontend/src/pages/scratchpad.vue +++ b/packages/frontend/src/pages/scratchpad.vue @@ -1,25 +1,34 @@ <template> -<div class="iltifgqe"> - <div class="editor _panel _gap"> - <PrismEditor v-model="code" class="_code code" :highlight="highlighter" :line-numbers="false"/> - <MkButton style="position: absolute; top: 8px; right: 8px;" primary @click="run()"><i class="ti ti-player-play"></i></MkButton> - </div> - - <MkContainer :foldable="true" class="_gap"> - <template #header>{{ i18n.ts.output }}</template> - <div class="bepmlvbi"> - <div v-for="log in logs" :key="log.id" class="log" :class="{ print: log.print }">{{ log.text }}</div> +<MkSpacer :content-max="800"> + <div :class="$style.root"> + <div :class="$style.editor" class="_panel"> + <PrismEditor v-model="code" class="_code code" :highlight="highlighter" :line-numbers="false"/> + <MkButton style="position: absolute; top: 8px; right: 8px;" primary @click="run()"><i class="ti ti-player-play"></i></MkButton> </div> - </MkContainer> - <div class="_gap"> - {{ i18n.ts.scratchpadDescription }} + <MkContainer v-if="root && components.length > 0" :key="uiKey" :foldable="true"> + <template #header>UI</template> + <div :class="$style.ui"> + <MkAsUi :component="root" :components="components" size="small"/> + </div> + </MkContainer> + + <MkContainer :foldable="true" class=""> + <template #header>{{ i18n.ts.output }}</template> + <div :class="$style.logs"> + <div v-for="log in logs" :key="log.id" class="log" :class="{ print: log.print }">{{ log.text }}</div> + </div> + </MkContainer> + + <div class=""> + {{ i18n.ts.scratchpadDescription }} + </div> </div> -</div> +</MkSpacer> </template> <script lang="ts" setup> -import { ref, watch } from 'vue'; +import { onDeactivated, onUnmounted, Ref, ref, watch } from 'vue'; import 'prismjs'; import { highlight, languages } from 'prismjs/components/prism-core'; import 'prismjs/components/prism-clike'; @@ -35,11 +44,16 @@ import * as os from '@/os'; import { $i } from '@/account'; import { i18n } from '@/i18n'; import { definePageMetadata } from '@/scripts/page-metadata'; +import { AsUiComponent, AsUiRoot, patch, registerAsUiLib, render } from '@/scripts/aiscript/ui'; +import MkAsUi from '@/components/MkAsUi.vue'; const parser = new Parser(); - +let aiscript: Interpreter; const code = ref(''); const logs = ref<any[]>([]); +const root = ref<AsUiRoot>(); +let components: Ref<AsUiComponent>[] = []; +let uiKey = $ref(0); const saved = localStorage.getItem('scratchpad'); if (saved) { @@ -51,10 +65,19 @@ watch(code, () => { }); async function run() { + if (aiscript) aiscript.abort(); + root.value = undefined; + components = []; + uiKey++; logs.value = []; - const aiscript = new Interpreter(createAiScriptEnv({ - storageKey: 'scratchpad', - token: $i?.token, + aiscript = new Interpreter(({ + ...createAiScriptEnv({ + storageKey: 'widget', + token: $i?.token, + }), + ...registerAsUiLib(components, (_root) => { + root.value = _root.value; + }), }), { in: (q) => { return new Promise(ok => { @@ -96,10 +119,11 @@ async function run() { } try { await aiscript.exec(ast); - } catch (error: any) { + } catch (err: any) { os.alert({ type: 'error', - text: error.message, + title: 'AiScript Error', + text: err.message, }); } } @@ -108,6 +132,14 @@ function highlighter(code) { return highlight(code, languages.js, 'javascript'); } +onDeactivated(() => { + if (aiscript) aiscript.abort(); +}); + +onUnmounted(() => { + if (aiscript) aiscript.abort(); +}); + const headerActions = $computed(() => []); const headerTabs = $computed(() => []); @@ -118,21 +150,29 @@ definePageMetadata({ }); </script> -<style lang="scss" scoped> -.iltifgqe { - padding: 16px; - - > .editor { - position: relative; - } +<style lang="scss" module> +.root { + display: flex; + flex-direction: column; + gap: var(--margin); } -.bepmlvbi { +.editor { + position: relative; +} + +.ui { + padding: 32px; +} + +.logs { padding: 16px; - > .log { - &:not(.print) { - opacity: 0.7; + &:global { + > .log { + &:not(.print) { + opacity: 0.7; + } } } } diff --git a/packages/frontend/src/pages/settings/navbar.vue b/packages/frontend/src/pages/settings/navbar.vue index 0b2776ec90..9ab8700b01 100644 --- a/packages/frontend/src/pages/settings/navbar.vue +++ b/packages/frontend/src/pages/settings/navbar.vue @@ -49,7 +49,7 @@ async function addItem() { const { canceled, result: item } = await os.select({ title: i18n.ts.addItem, items: [...menu.map(k => ({ - value: k, text: i18n.ts[navbarItemDef[k].title], + value: k, text: navbarItemDef[k].title, })), { value: '-', text: i18n.ts.divider, }], diff --git a/packages/frontend/src/router.ts b/packages/frontend/src/router.ts index 9001f0f37f..63c753de22 100644 --- a/packages/frontend/src/router.ts +++ b/packages/frontend/src/router.ts @@ -262,6 +262,20 @@ export const routes = [{ }, { path: '/pages', component: page(() => import('./pages/pages.vue')), +}, { + path: '/play/:id/edit', + component: page(() => import('./pages/flash/flash-edit.vue')), + loginRequired: true, +}, { + path: '/play/new', + component: page(() => import('./pages/flash/flash-edit.vue')), + loginRequired: true, +}, { + path: '/play/:id', + component: page(() => import('./pages/flash/flash.vue')), +}, { + path: '/play', + component: page(() => import('./pages/flash/flash-index.vue')), }, { path: '/gallery/:postId/edit', component: page(() => import('./pages/gallery/edit.vue')), diff --git a/packages/frontend/src/scripts/aiscript/ui.ts b/packages/frontend/src/scripts/aiscript/ui.ts new file mode 100644 index 0000000000..f4c89b8276 --- /dev/null +++ b/packages/frontend/src/scripts/aiscript/ui.ts @@ -0,0 +1,526 @@ +import { Interpreter, Parser, utils, values } from '@syuilo/aiscript'; +import { v4 as uuid } from 'uuid'; +import { ref, Ref } from 'vue'; + +export type AsUiComponentBase = { + id: string; + hidden?: boolean; +}; + +export type AsUiRoot = AsUiComponentBase & { + type: 'root'; + children: AsUiComponent['id'][]; +}; + +export type AsUiContainer = AsUiComponentBase & { + type: 'container'; + children?: AsUiComponent['id'][]; + align?: 'left' | 'center' | 'right'; + bgColor?: string; + fgColor?: string; + font?: 'serif' | 'sans-serif' | 'monospace'; + borderWidth?: number; + borderColor?: string; + padding?: number; + rounded?: boolean; + hidden?: boolean; +}; + +export type AsUiText = AsUiComponentBase & { + type: 'text'; + text?: string; + size?: number; + bold?: boolean; + color?: string; + font?: 'serif' | 'sans-serif' | 'monospace'; +}; + +export type AsUiMfm = AsUiComponentBase & { + type: 'mfm'; + text?: string; + size?: number; + color?: string; + font?: 'serif' | 'sans-serif' | 'monospace'; +}; + +export type AsUiButton = AsUiComponentBase & { + type: 'button'; + text?: string; + onClick?: () => void; + primary?: boolean; + rounded?: boolean; +}; + +export type AsUiButtons = AsUiComponentBase & { + type: 'buttons'; + buttons?: AsUiButton[]; +}; + +export type AsUiSwitch = AsUiComponentBase & { + type: 'switch'; + onChange?: (v: boolean) => void; + default?: boolean; + label?: string; + caption?: string; +}; + +export type AsUiTextarea = AsUiComponentBase & { + type: 'textarea'; + onInput?: (v: string) => void; + default?: string; + label?: string; + caption?: string; +}; + +export type AsUiTextInput = AsUiComponentBase & { + type: 'textInput'; + onInput?: (v: string) => void; + default?: string; + label?: string; + caption?: string; +}; + +export type AsUiNumberInput = AsUiComponentBase & { + type: 'numberInput'; + onInput?: (v: number) => void; + default?: number; + label?: string; + caption?: string; +}; + +export type AsUiSelect = AsUiComponentBase & { + type: 'select'; + items?: { + text: string; + value: string; + }[]; + onChange?: (v: string) => void; + default?: string; + label?: string; + caption?: string; +}; + +export type AsUiPostFormButton = AsUiComponentBase & { + type: 'postFormButton'; + text?: string; + primary?: boolean; + rounded?: boolean; + form?: { + text: string; + }; +}; + +export type AsUiComponent = AsUiRoot | AsUiContainer | AsUiText | AsUiMfm | AsUiButton | AsUiButtons | AsUiSwitch | AsUiTextarea | AsUiTextInput | AsUiNumberInput | AsUiSelect | AsUiPostFormButton; + +export function patch(id: string, def: values.Value, call: (fn: values.VFn, args: values.Value[]) => Promise<values.Value>) { + // TODO +} + +function getRootOptions(def: values.Value | undefined): Omit<AsUiRoot, 'id' | 'type'> { + utils.assertObject(def); + + const children = def.value.get('children'); + utils.assertArray(children); + + return { + children: children.value.map(v => { + utils.assertObject(v); + return v.value.get('id').value; + }), + }; +} + +function getContainerOptions(def: values.Value | undefined): Omit<AsUiContainer, 'id' | 'type'> { + utils.assertObject(def); + + const children = def.value.get('children'); + if (children) utils.assertArray(children); + const align = def.value.get('align'); + if (align) utils.assertString(align); + const bgColor = def.value.get('bgColor'); + if (bgColor) utils.assertString(bgColor); + const fgColor = def.value.get('fgColor'); + if (fgColor) utils.assertString(fgColor); + const font = def.value.get('font'); + if (font) utils.assertString(font); + const borderWidth = def.value.get('borderWidth'); + if (borderWidth) utils.assertNumber(borderWidth); + const borderColor = def.value.get('borderColor'); + if (borderColor) utils.assertString(borderColor); + const padding = def.value.get('padding'); + if (padding) utils.assertNumber(padding); + const rounded = def.value.get('rounded'); + if (rounded) utils.assertBoolean(rounded); + const hidden = def.value.get('hidden'); + if (hidden) utils.assertBoolean(hidden); + + return { + children: children ? children.value.map(v => { + utils.assertObject(v); + return v.value.get('id').value; + }) : [], + align: align?.value, + fgColor: fgColor?.value, + bgColor: bgColor?.value, + font: font?.value, + borderWidth: borderWidth?.value, + borderColor: borderColor?.value, + padding: padding?.value, + rounded: rounded?.value, + hidden: hidden?.value, + }; +} + +function getTextOptions(def: values.Value | undefined): Omit<AsUiText, 'id' | 'type'> { + utils.assertObject(def); + + const text = def.value.get('text'); + if (text) utils.assertString(text); + const size = def.value.get('size'); + if (size) utils.assertNumber(size); + const bold = def.value.get('bold'); + if (bold) utils.assertBoolean(bold); + const color = def.value.get('color'); + if (color) utils.assertString(color); + const font = def.value.get('font'); + if (font) utils.assertString(font); + + return { + text: text?.value, + size: size?.value, + bold: bold?.value, + color: color?.value, + font: font?.value, + }; +} + +function getMfmOptions(def: values.Value | undefined): Omit<AsUiMfm, 'id' | 'type'> { + utils.assertObject(def); + + const text = def.value.get('text'); + if (text) utils.assertString(text); + const size = def.value.get('size'); + if (size) utils.assertNumber(size); + const color = def.value.get('color'); + if (color) utils.assertString(color); + const font = def.value.get('font'); + if (font) utils.assertString(font); + + return { + text: text?.value, + size: size?.value, + color: color?.value, + font: font?.value, + }; +} + +function getTextInputOptions(def: values.Value | undefined, call: (fn: values.VFn, args: values.Value[]) => Promise<values.Value>): Omit<AsUiTextInput, 'id' | 'type'> { + utils.assertObject(def); + + const onInput = def.value.get('onInput'); + if (onInput) utils.assertFunction(onInput); + const defaultValue = def.value.get('default'); + if (defaultValue) utils.assertString(defaultValue); + const label = def.value.get('label'); + if (label) utils.assertString(label); + const caption = def.value.get('caption'); + if (caption) utils.assertString(caption); + + return { + onInput: (v) => { + if (onInput) call(onInput, [utils.jsToVal(v)]); + }, + default: defaultValue?.value, + label: label?.value, + caption: caption?.value, + }; +} + +function getTextareaOptions(def: values.Value | undefined, call: (fn: values.VFn, args: values.Value[]) => Promise<values.Value>): Omit<AsUiTextarea, 'id' | 'type'> { + utils.assertObject(def); + + const onInput = def.value.get('onInput'); + if (onInput) utils.assertFunction(onInput); + const defaultValue = def.value.get('default'); + if (defaultValue) utils.assertString(defaultValue); + const label = def.value.get('label'); + if (label) utils.assertString(label); + const caption = def.value.get('caption'); + if (caption) utils.assertString(caption); + + return { + onInput: (v) => { + if (onInput) call(onInput, [utils.jsToVal(v)]); + }, + default: defaultValue?.value, + label: label?.value, + caption: caption?.value, + }; +} + +function getNumberInputOptions(def: values.Value | undefined, call: (fn: values.VFn, args: values.Value[]) => Promise<values.Value>): Omit<AsUiNumberInput, 'id' | 'type'> { + utils.assertObject(def); + + const onInput = def.value.get('onInput'); + if (onInput) utils.assertFunction(onInput); + const defaultValue = def.value.get('default'); + if (defaultValue) utils.assertNumber(defaultValue); + const label = def.value.get('label'); + if (label) utils.assertString(label); + const caption = def.value.get('caption'); + if (caption) utils.assertString(caption); + + return { + onInput: (v) => { + if (onInput) call(onInput, [utils.jsToVal(v)]); + }, + default: defaultValue?.value, + label: label?.value, + caption: caption?.value, + }; +} + +function getButtonOptions(def: values.Value | undefined, call: (fn: values.VFn, args: values.Value[]) => Promise<values.Value>): Omit<AsUiButton, 'id' | 'type'> { + utils.assertObject(def); + + const text = def.value.get('text'); + if (text) utils.assertString(text); + const onClick = def.value.get('onClick'); + if (onClick) utils.assertFunction(onClick); + const primary = def.value.get('primary'); + if (primary) utils.assertBoolean(primary); + const rounded = def.value.get('rounded'); + if (rounded) utils.assertBoolean(rounded); + + return { + text: text?.value, + onClick: () => { + if (onClick) call(onClick, []); + }, + primary: primary?.value, + rounded: rounded?.value, + }; +} + +function getButtonsOptions(def: values.Value | undefined, call: (fn: values.VFn, args: values.Value[]) => Promise<values.Value>): Omit<AsUiButtons, 'id' | 'type'> { + utils.assertObject(def); + + const buttons = def.value.get('buttons'); + if (buttons) utils.assertArray(buttons); + + return { + buttons: buttons ? buttons.value.map(button => { + utils.assertObject(button); + const text = button.value.get('text'); + utils.assertString(text); + const onClick = button.value.get('onClick'); + utils.assertFunction(onClick); + const primary = button.value.get('primary'); + if (primary) utils.assertBoolean(primary); + const rounded = button.value.get('rounded'); + if (rounded) utils.assertBoolean(rounded); + + return { + text: text.value, + onClick: () => { + call(onClick, []); + }, + primary: primary?.value, + rounded: rounded?.value, + }; + }) : [], + }; +} + +function getSwitchOptions(def: values.Value | undefined, call: (fn: values.VFn, args: values.Value[]) => Promise<values.Value>): Omit<AsUiSwitch, 'id' | 'type'> { + utils.assertObject(def); + + const onChange = def.value.get('onChange'); + if (onChange) utils.assertFunction(onChange); + const defaultValue = def.value.get('default'); + if (defaultValue) utils.assertBoolean(defaultValue); + const label = def.value.get('label'); + if (label) utils.assertString(label); + const caption = def.value.get('caption'); + if (caption) utils.assertString(caption); + + return { + onChange: (v) => { + if (onChange) call(onChange, [utils.jsToVal(v)]); + }, + default: defaultValue?.value, + label: label?.value, + caption: caption?.value, + }; +} + +function getSelectOptions(def: values.Value | undefined, call: (fn: values.VFn, args: values.Value[]) => Promise<values.Value>): Omit<AsUiSelect, 'id' | 'type'> { + utils.assertObject(def); + + const items = def.value.get('items'); + if (items) utils.assertArray(items); + const onChange = def.value.get('onChange'); + if (onChange) utils.assertFunction(onChange); + const defaultValue = def.value.get('default'); + if (defaultValue) utils.assertString(defaultValue); + const label = def.value.get('label'); + if (label) utils.assertString(label); + const caption = def.value.get('caption'); + if (caption) utils.assertString(caption); + + return { + items: items ? items.value.map(item => { + utils.assertObject(item); + const text = item.value.get('text'); + utils.assertString(text); + const value = item.value.get('value'); + if (value) utils.assertString(value); + return { + text: text.value, + value: value ? value.value : text.value, + }; + }) : [], + onChange: (v) => { + if (onChange) call(onChange, [utils.jsToVal(v)]); + }, + default: defaultValue?.value, + label: label?.value, + caption: caption?.value, + }; +} + +function getPostFormButtonOptions(def: values.Value | undefined, call: (fn: values.VFn, args: values.Value[]) => Promise<values.Value>): Omit<AsUiPostFormButton, 'id' | 'type'> { + utils.assertObject(def); + + const text = def.value.get('text'); + if (text) utils.assertString(text); + const primary = def.value.get('primary'); + if (primary) utils.assertBoolean(primary); + const rounded = def.value.get('rounded'); + if (rounded) utils.assertBoolean(rounded); + const form = def.value.get('form'); + if (form) utils.assertObject(form); + + const getForm = () => { + const text = form!.value.get('text'); + utils.assertString(text); + return { + text: text.value, + }; + }; + + return { + text: text?.value, + primary: primary?.value, + rounded: rounded?.value, + form: form ? getForm() : { + text: '', + }, + }; +} + +export function registerAsUiLib(components: Ref<AsUiComponent>[], done: (root: Ref<AsUiRoot>) => void) { + const instances = {}; + + function createComponentInstance(type: AsUiComponent['type'], def: values.Value | undefined, id: values.Value | undefined, getOptions: (def: values.Value | undefined, call: (fn: values.VFn, args: values.Value[]) => Promise<values.Value>) => any, call: (fn: values.VFn, args: values.Value[]) => Promise<values.Value>) { + if (id) utils.assertString(id); + const _id = id?.value ?? uuid(); + const component = ref({ + ...getOptions(def, call), + type, + id: _id, + }); + components.push(component); + const instance = values.OBJ(new Map([ + ['id', values.STR(_id)], + ['update', values.FN_NATIVE(async ([def], opts) => { + utils.assertObject(def); + const updates = getOptions(def, call); + for (const update of def.value.keys()) { + if (!Object.hasOwn(updates, update)) continue; + component.value[update] = updates[update]; + } + })], + ])); + instances[_id] = instance; + return instance; + } + + const rootInstance = createComponentInstance('root', utils.jsToVal({ children: [] }), utils.jsToVal('___root___'), getRootOptions, () => {}); + const rootComponent = components[0] as Ref<AsUiRoot>; + done(rootComponent); + + return { + 'Ui:root': rootInstance, + + 'Ui:patch': values.FN_NATIVE(async ([id, val], opts) => { + utils.assertString(id); + utils.assertArray(val); + patch(id.value, val.value, opts.call); + }), + + 'Ui:get': values.FN_NATIVE(async ([id], opts) => { + utils.assertString(id); + const instance = instances[id.value]; + if (instance) { + return instance; + } else { + return values.NULL; + } + }), + + // Ui:root.update({ children: [...] }) の糖衣構文 + 'Ui:render': values.FN_NATIVE(async ([children], opts) => { + utils.assertArray(children); + + rootComponent.value.children = children.value.map(v => { + utils.assertObject(v); + return v.value.get('id').value; + }); + }), + + 'Ui:C:container': values.FN_NATIVE(async ([def, id], opts) => { + return createComponentInstance('container', def, id, getContainerOptions, opts.call); + }), + + 'Ui:C:text': values.FN_NATIVE(async ([def, id], opts) => { + return createComponentInstance('text', def, id, getTextOptions, opts.call); + }), + + 'Ui:C:mfm': values.FN_NATIVE(async ([def, id], opts) => { + return createComponentInstance('mfm', def, id, getMfmOptions, opts.call); + }), + + 'Ui:C:textarea': values.FN_NATIVE(async ([def, id], opts) => { + return createComponentInstance('textarea', def, id, getTextareaOptions, opts.call); + }), + + 'Ui:C:textInput': values.FN_NATIVE(async ([def, id], opts) => { + return createComponentInstance('textInput', def, id, getTextInputOptions, opts.call); + }), + + 'Ui:C:numberInput': values.FN_NATIVE(async ([def, id], opts) => { + return createComponentInstance('numberInput', def, id, getNumberInputOptions, opts.call); + }), + + 'Ui:C:button': values.FN_NATIVE(async ([def, id], opts) => { + return createComponentInstance('button', def, id, getButtonOptions, opts.call); + }), + + 'Ui:C:buttons': values.FN_NATIVE(async ([def, id], opts) => { + return createComponentInstance('buttons', def, id, getButtonsOptions, opts.call); + }), + + 'Ui:C:switch': values.FN_NATIVE(async ([def, id], opts) => { + return createComponentInstance('switch', def, id, getSwitchOptions, opts.call); + }), + + 'Ui:C:select': values.FN_NATIVE(async ([def, id], opts) => { + return createComponentInstance('select', def, id, getSelectOptions, opts.call); + }), + + 'Ui:C:postFormButton': values.FN_NATIVE(async ([def, id], opts) => { + return createComponentInstance('postFormButton', def, id, getPostFormButtonOptions, opts.call); + }), + }; +} diff --git a/packages/frontend/src/ui/_common_/navbar-for-mobile.vue b/packages/frontend/src/ui/_common_/navbar-for-mobile.vue index ac109d9def..989d861d27 100644 --- a/packages/frontend/src/ui/_common_/navbar-for-mobile.vue +++ b/packages/frontend/src/ui/_common_/navbar-for-mobile.vue @@ -14,7 +14,7 @@ <template v-for="item in menu"> <div v-if="item === '-'" class="divider"></div> <component :is="navbarItemDef[item].to ? 'MkA' : 'button'" v-else-if="navbarItemDef[item] && (navbarItemDef[item].show !== false)" v-click-anime class="item _button" :class="[item, { active: navbarItemDef[item].active }]" active-class="active" :to="navbarItemDef[item].to" v-on="navbarItemDef[item].action ? { click: navbarItemDef[item].action } : {}"> - <i class="icon ti-fw" :class="navbarItemDef[item].icon"></i><span class="text">{{ i18n.ts[navbarItemDef[item].title] }}</span> + <i class="icon ti-fw" :class="navbarItemDef[item].icon"></i><span class="text">{{ navbarItemDef[item].title }}</span> <span v-if="navbarItemDef[item].indicated" class="indicator"><i class="icon _indicatorCircle"></i></span> </component> </template> diff --git a/packages/frontend/src/ui/_common_/navbar.vue b/packages/frontend/src/ui/_common_/navbar.vue index 7c859bf3aa..e90098397a 100644 --- a/packages/frontend/src/ui/_common_/navbar.vue +++ b/packages/frontend/src/ui/_common_/navbar.vue @@ -17,14 +17,14 @@ :is="navbarItemDef[item].to ? 'MkA' : 'button'" v-else-if="navbarItemDef[item] && (navbarItemDef[item].show !== false)" v-click-anime - v-tooltip.noDelay.right="i18n.ts[navbarItemDef[item].title]" + v-tooltip.noDelay.right="navbarItemDef[item].title" class="item _button" :class="[item, { active: navbarItemDef[item].active }]" active-class="active" :to="navbarItemDef[item].to" v-on="navbarItemDef[item].action ? { click: navbarItemDef[item].action } : {}" > - <i class="icon ti-fw" :class="navbarItemDef[item].icon"></i><span class="text">{{ i18n.ts[navbarItemDef[item].title] }}</span> + <i class="icon ti-fw" :class="navbarItemDef[item].icon"></i><span class="text">{{ navbarItemDef[item].title }}</span> <span v-if="navbarItemDef[item].indicated" class="indicator"><i class="icon _indicatorCircle"></i></span> </component> </template> diff --git a/packages/frontend/src/ui/classic.header.vue b/packages/frontend/src/ui/classic.header.vue index 77a64aac37..34ddfa1d32 100644 --- a/packages/frontend/src/ui/classic.header.vue +++ b/packages/frontend/src/ui/classic.header.vue @@ -10,7 +10,7 @@ </MkA> <template v-for="item in menu"> <div v-if="item === '-'" class="divider"></div> - <component :is="navbarItemDef[item].to ? 'MkA' : 'button'" v-else-if="navbarItemDef[item] && (navbarItemDef[item].show !== false)" v-click-anime v-tooltip="$ts[navbarItemDef[item].title]" class="item _button" :class="item" active-class="active" :to="navbarItemDef[item].to" v-on="navbarItemDef[item].action ? { click: navbarItemDef[item].action } : {}"> + <component :is="navbarItemDef[item].to ? 'MkA' : 'button'" v-else-if="navbarItemDef[item] && (navbarItemDef[item].show !== false)" v-click-anime v-tooltip="navbarItemDef[item].title" class="item _button" :class="item" active-class="active" :to="navbarItemDef[item].to" v-on="navbarItemDef[item].action ? { click: navbarItemDef[item].action } : {}"> <i class="ti-fw" :class="navbarItemDef[item].icon"></i> <span v-if="navbarItemDef[item].indicated" class="indicator"><i class="_indicatorCircle"></i></span> </component> diff --git a/packages/frontend/src/ui/classic.sidebar.vue b/packages/frontend/src/ui/classic.sidebar.vue index ec379fbaa7..a11c2ba10e 100644 --- a/packages/frontend/src/ui/classic.sidebar.vue +++ b/packages/frontend/src/ui/classic.sidebar.vue @@ -15,7 +15,7 @@ <template v-for="item in menu"> <div v-if="item === '-'" class="divider"></div> <component :is="navbarItemDef[item].to ? 'MkA' : 'button'" v-else-if="navbarItemDef[item] && (navbarItemDef[item].show !== false)" v-click-anime class="item _button" :class="item" active-class="active" :to="navbarItemDef[item].to" v-on="navbarItemDef[item].action ? { click: navbarItemDef[item].action } : {}"> - <i class="ti-fw" :class="navbarItemDef[item].icon"></i><span class="text">{{ $ts[navbarItemDef[item].title] }}</span> + <i class="ti-fw" :class="navbarItemDef[item].icon"></i><span class="text">{{ navbarItemDef[item].title }}</span> <span v-if="navbarItemDef[item].indicated" class="indicator"><i class="_indicatorCircle"></i></span> </component> </template> diff --git a/packages/frontend/src/widgets/aiscript-app.vue b/packages/frontend/src/widgets/aiscript-app.vue new file mode 100644 index 0000000000..1445e5b1ad --- /dev/null +++ b/packages/frontend/src/widgets/aiscript-app.vue @@ -0,0 +1,122 @@ +<template> +<MkContainer :show-header="widgetProps.showHeader" class="mkw-aiscriptApp"> + <template #header>App</template> + <div :class="$style.root"> + <MkAsUi v-if="root" :component="root" :components="components" size="small"/> + </div> +</MkContainer> +</template> + +<script lang="ts" setup> +import { onMounted, onUnmounted, Ref, ref, watch } from 'vue'; +import { Interpreter, Parser, utils, values } from '@syuilo/aiscript'; +import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget'; +import { GetFormResultType } from '@/scripts/form'; +import * as os from '@/os'; +import { createAiScriptEnv } from '@/scripts/aiscript/api'; +import { $i } from '@/account'; +import MkAsUi from '@/components/MkAsUi.vue'; +import MkContainer from '@/components/MkContainer.vue'; +import { AsUiComponent, AsUiRoot, patch, registerAsUiLib, render } from '@/scripts/aiscript/ui'; + +const name = 'aiscriptApp'; + +const widgetPropsDef = { + script: { + type: 'string' as const, + multiline: true, + default: '', + }, + showHeader: { + type: 'boolean' as const, + default: true, + }, +}; + +type WidgetProps = GetFormResultType<typeof widgetPropsDef>; + +// 現時点ではvueの制限によりimportしたtypeをジェネリックに渡せない +//const props = defineProps<WidgetComponentProps<WidgetProps>>(); +//const emit = defineEmits<WidgetComponentEmits<WidgetProps>>(); +const props = defineProps<{ widget?: Widget<WidgetProps>; }>(); +const emit = defineEmits<{ (ev: 'updateProps', props: WidgetProps); }>(); + +const { widgetProps, configure } = useWidgetPropsManager(name, + widgetPropsDef, + props, + emit, +); + +const parser = new Parser(); + +const root = ref<AsUiRoot>(); +const components: Ref<AsUiComponent>[] = []; + +async function run() { + const aiscript = new Interpreter({ + ...createAiScriptEnv({ + storageKey: 'widget', + token: $i?.token, + }), + ...registerAsUiLib(components, (_root) => { + root.value = _root.value; + }), + }, { + in: (q) => { + return new Promise(ok => { + os.inputText({ + title: q, + }).then(({ canceled, result: a }) => { + ok(a); + }); + }); + }, + out: (value) => { + // nop + }, + log: (type, params) => { + // nop + }, + }); + + let ast; + try { + ast = parser.parse(widgetProps.script); + } catch (err) { + os.alert({ + type: 'error', + text: 'Syntax error :(', + }); + return; + } + try { + await aiscript.exec(ast); + } catch (err) { + os.alert({ + type: 'error', + title: 'AiScript Error', + text: err.message, + }); + } +} + +watch(() => widgetProps.script, () => { + run(); +}); + +onMounted(() => { + run(); +}); + +defineExpose<WidgetComponentExpose>({ + name, + configure, + id: props.widget ? props.widget.id : null, +}); +</script> + +<style lang="scss" module> +.root { + padding: 16px; +} +</style> diff --git a/packages/frontend/src/widgets/index.ts b/packages/frontend/src/widgets/index.ts index 39826f13c8..3966649da4 100644 --- a/packages/frontend/src/widgets/index.ts +++ b/packages/frontend/src/widgets/index.ts @@ -22,6 +22,7 @@ export default function(app: App) { app.component('MkwInstanceCloud', defineAsyncComponent(() => import('./instance-cloud.vue'))); app.component('MkwButton', defineAsyncComponent(() => import('./button.vue'))); app.component('MkwAiscript', defineAsyncComponent(() => import('./aiscript.vue'))); + app.component('MkwAiscriptApp', defineAsyncComponent(() => import('./aiscript-app.vue'))); app.component('MkwAichan', defineAsyncComponent(() => import('./aichan.vue'))); app.component('MkwUserList', defineAsyncComponent(() => import('./user-list.vue'))); } @@ -48,6 +49,7 @@ export const widgets = [ 'jobQueue', 'button', 'aiscript', + 'aiscriptApp', 'aichan', 'userList', ]; From 5de699e233820644b7112e0641cad9008efda842 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Thu, 5 Jan 2023 14:00:28 +0900 Subject: [PATCH 14/60] 13.0.0-beta.22 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5ab38a56ec..f5c033c457 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "13.0.0-beta.21", + "version": "13.0.0-beta.22", "codename": "indigo", "repository": { "type": "git", From df291b00d86b7271f28e808be80910e3ae72ea93 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Thu, 5 Jan 2023 14:08:21 +0900 Subject: [PATCH 15/60] Update CHANGELOG.md --- CHANGELOG.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 005b011592..5da590cab9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,18 +12,23 @@ You should also include the user name that made the change. ## 13.0.0 (unreleased) ### Changes +#### For sabakans - Node.js 18.x or later is required - Elasticsearchのサポートが削除されました - 代わりに今後任意の検索プロバイダを設定できる仕組みを構想しています。その仕組みを使えば今まで通りElasticsearchも利用できます -- ノートのウォッチ機能が削除されました - Migrate to Yarn Berry (v3.2.1) @ThatOneCalculator - You may have to `yarn run clean-all`, `sudo corepack enable` and `yarn set version berry` before running `yarn install` if you're still on yarn classic + +#### For users +- ノートのウォッチ機能が削除されました - 新たに動的なPagesを作ることはできなくなりました - 代わりにAiScriptを用いてより柔軟に動的なコンテンツを作成できるMisskey Play機能が実装されています。 - AiScriptが0.12.0にアップデートされました - 0.12.0の変更点についてはこちら https://github.com/syuilo/aiscript/blob/master/CHANGELOG.md#0120 - 0.12.0未満のプラグインは読み込むことはできません - iOS15以下のデバイスはサポートされなくなりました + +#### For developers - API: カスタム絵文字エンティティに`url`プロパティが含まれなくなりました - 絵文字画像を表示するには、`<instance host>/emoji/<emoji name>.webp`にリクエストすると画像が返ります。 - e.g. `https://p1.a9z.dev/emoji/misskey.webp` From 6f9aa94e3ae59a5ab8b38a6cb9c179f96033efa8 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Thu, 5 Jan 2023 16:38:12 +0900 Subject: [PATCH 16/60] fix instance sort --- .../backend/src/server/api/endpoints/federation/instances.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/backend/src/server/api/endpoints/federation/instances.ts b/packages/backend/src/server/api/endpoints/federation/instances.ts index 180887285a..5e2f204661 100644 --- a/packages/backend/src/server/api/endpoints/federation/instances.ts +++ b/packages/backend/src/server/api/endpoints/federation/instances.ts @@ -64,8 +64,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { case '-followers': query.orderBy('instance.followersCount', 'ASC'); break; case '+caughtAt': query.orderBy('instance.caughtAt', 'DESC'); break; case '-caughtAt': query.orderBy('instance.caughtAt', 'ASC'); break; - case '+latestRequestReceivedAt': query.orderBy('instance.latestRequestReceivedAt', 'DESC'); break; - case '-latestRequestReceivedAt': query.orderBy('instance.latestRequestReceivedAt', 'ASC'); break; + case '+latestRequestReceivedAt': query.orderBy('instance.latestRequestReceivedAt', 'DESC', 'NULLS LAST'); break; + case '-latestRequestReceivedAt': query.orderBy('instance.latestRequestReceivedAt', 'ASC', 'NULLS FIRST'); break; default: query.orderBy('instance.id', 'DESC'); break; } From 1cae688ccbb3fc298d9c95db4c0670de206899d9 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Thu, 5 Jan 2023 16:57:49 +0900 Subject: [PATCH 17/60] update aiscript --- CHANGELOG.md | 6 ++-- packages/frontend/package.json | 2 +- .../frontend/src/pages/flash/flash-edit.vue | 2 +- .../src/pages/settings/plugin.install.vue | 2 +- yarn.lock | 30 +++++++++---------- 5 files changed, 21 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5da590cab9..8adf88bae3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,9 +23,9 @@ You should also include the user name that made the change. - ノートのウォッチ機能が削除されました - 新たに動的なPagesを作ることはできなくなりました - 代わりにAiScriptを用いてより柔軟に動的なコンテンツを作成できるMisskey Play機能が実装されています。 -- AiScriptが0.12.0にアップデートされました - - 0.12.0の変更点についてはこちら https://github.com/syuilo/aiscript/blob/master/CHANGELOG.md#0120 - - 0.12.0未満のプラグインは読み込むことはできません +- AiScriptが0.12.1にアップデートされました + - 0.12.xの変更点についてはこちら https://github.com/syuilo/aiscript/blob/master/CHANGELOG.md#0120 + - 0.12.1未満のプラグインは読み込むことはできません - iOS15以下のデバイスはサポートされなくなりました #### For developers diff --git a/packages/frontend/package.json b/packages/frontend/package.json index 2506e8e9d3..d151a7b3af 100644 --- a/packages/frontend/package.json +++ b/packages/frontend/package.json @@ -11,7 +11,7 @@ "@rollup/plugin-alias": "4.0.2", "@rollup/plugin-json": "6.0.0", "@rollup/pluginutils": "5.0.2", - "@syuilo/aiscript": "0.12.0", + "@syuilo/aiscript": "0.12.1", "@tabler/icons": "^1.118.0", "@vitejs/plugin-vue": "4.0.0", "@vue/compiler-sfc": "3.2.45", diff --git a/packages/frontend/src/pages/flash/flash-edit.vue b/packages/frontend/src/pages/flash/flash-edit.vue index 561331e002..b2eba292f2 100644 --- a/packages/frontend/src/pages/flash/flash-edit.vue +++ b/packages/frontend/src/pages/flash/flash-edit.vue @@ -47,7 +47,7 @@ if (props.id) { let title = $ref(flash?.title ?? 'New Play'); let summary = $ref(flash?.summary ?? ''); let permissions = $ref(flash?.permissions ?? []); -let script = $ref(flash?.script ?? `/// @ 0.12.0 +let script = $ref(flash?.script ?? `/// @ 0.12.1 var name = "" diff --git a/packages/frontend/src/pages/settings/plugin.install.vue b/packages/frontend/src/pages/settings/plugin.install.vue index 40ad9a95dd..3d77cadaaa 100644 --- a/packages/frontend/src/pages/settings/plugin.install.vue +++ b/packages/frontend/src/pages/settings/plugin.install.vue @@ -49,7 +49,7 @@ async function install() { text: 'No language version annotation found :(', }); return; - } else if (lv !== '0.12.0') { + } else if (!lv.startsWith('0.12.')) { os.alert({ type: 'error', text: `aiscript version '${lv}' is not supported :(`, diff --git a/yarn.lock b/yarn.lock index 7ce29e1543..208f0724d5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1973,15 +1973,15 @@ __metadata: languageName: node linkType: hard -"@syuilo/aiscript@npm:0.12.0": - version: 0.12.0 - resolution: "@syuilo/aiscript@npm:0.12.0" +"@syuilo/aiscript@npm:0.12.1": + version: 0.12.1 + resolution: "@syuilo/aiscript@npm:0.12.1" dependencies: autobind-decorator: 2.4.0 seedrandom: 3.0.5 stringz: 2.1.0 - uuid: 8.3.2 - checksum: 82b52a6c602a8c3090b9457a0e9de99898b03cd8f054855b2f57439534257ef2780013a53eaeeef68c9893d96d3ec02fc6d0ede56396c2bcf054cf43b2297b67 + uuid: 9.0.0 + checksum: 0bee3031cbc8358e159fc8fde6e1ab7204e1a8e17e07f394f337d70cd3a8558e591145ca03afe37c76bbb91f84b2b2af70b892935e9c98507f9d1455c7be1107 languageName: node linkType: hard @@ -8026,7 +8026,7 @@ __metadata: "@rollup/plugin-alias": 4.0.2 "@rollup/plugin-json": 6.0.0 "@rollup/pluginutils": 5.0.2 - "@syuilo/aiscript": 0.12.0 + "@syuilo/aiscript": 0.12.1 "@tabler/icons": ^1.118.0 "@types/escape-regexp": 0.0.1 "@types/glob": 8.0.0 @@ -16912,15 +16912,6 @@ __metadata: languageName: node linkType: hard -"uuid@npm:8.3.2, uuid@npm:^8.3.0, uuid@npm:^8.3.2": - version: 8.3.2 - resolution: "uuid@npm:8.3.2" - bin: - uuid: dist/bin/uuid - checksum: 5575a8a75c13120e2f10e6ddc801b2c7ed7d8f3c8ac22c7ed0c7b2ba6383ec0abda88c905085d630e251719e0777045ae3236f04c812184b7c765f63a70e58df - languageName: node - linkType: hard - "uuid@npm:9.0.0": version: 9.0.0 resolution: "uuid@npm:9.0.0" @@ -16939,6 +16930,15 @@ __metadata: languageName: node linkType: hard +"uuid@npm:^8.3.0, uuid@npm:^8.3.2": + version: 8.3.2 + resolution: "uuid@npm:8.3.2" + bin: + uuid: dist/bin/uuid + checksum: 5575a8a75c13120e2f10e6ddc801b2c7ed7d8f3c8ac22c7ed0c7b2ba6383ec0abda88c905085d630e251719e0777045ae3236f04c812184b7c765f63a70e58df + languageName: node + linkType: hard + "v8-to-istanbul@npm:^9.0.1": version: 9.0.1 resolution: "v8-to-istanbul@npm:9.0.1" From d890383a008b542c46819a1ce98a2646725a10e7 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Thu, 5 Jan 2023 17:09:36 +0900 Subject: [PATCH 18/60] add Ui:C:folder for AiScript --- packages/frontend/src/components/MkAsUi.vue | 7 +++++ packages/frontend/src/scripts/aiscript/ui.ts | 33 +++++++++++++++++++- 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/packages/frontend/src/components/MkAsUi.vue b/packages/frontend/src/components/MkAsUi.vue index bc1e25957b..ec214f0396 100644 --- a/packages/frontend/src/components/MkAsUi.vue +++ b/packages/frontend/src/components/MkAsUi.vue @@ -33,6 +33,12 @@ <option v-for="item in c.items" :key="item.value" :value="item.value">{{ item.text }}</option> </MkSelect> <MkButton v-else-if="c.type === 'postFormButton'" :primary="c.primary" :rounded="c.rounded" :small="size === 'small'" @click="openPostForm">{{ c.text }}</MkButton> + <FormFolder v-else-if="c.type === 'folder'" :default-open="c.opened"> + <template #label>{{ c.title }}</template> + <template v-for="child in c.children" :key="child"> + <MkAsUi v-if="!g(child).hidden" :component="g(child)" :components="props.components" :size="size"/> + </template> + </FormFolder> <div v-else-if="c.type === 'container'" :class="[$style.container, { [$style.fontSerif]: c.font === 'serif', [$style.fontMonospace]: c.font === 'monospace', [$style.containerCenter]: c.align === 'center' }]" :style="{ backgroundColor: c.bgColor ?? null, color: c.fgColor ?? null, borderWidth: c.borderWidth ? `${c.borderWidth}px` : 0, borderColor: c.borderColor ?? 'var(--divider)', padding: c.padding ? `${c.padding}px` : 0, borderRadius: c.rounded ? '8px' : 0 }"> <template v-for="child in c.children" :key="child"> <MkAsUi v-if="!g(child).hidden" :component="g(child)" :components="props.components" :size="size"/> @@ -50,6 +56,7 @@ import MkSwitch from '@/components/form/switch.vue'; import MkTextarea from '@/components/form/textarea.vue'; import MkSelect from '@/components/form/select.vue'; import { AsUiComponent } from '@/scripts/aiscript/ui'; +import FormFolder from '@/components/form/folder.vue'; const props = withDefaults(defineProps<{ component: AsUiComponent; diff --git a/packages/frontend/src/scripts/aiscript/ui.ts b/packages/frontend/src/scripts/aiscript/ui.ts index f4c89b8276..6e0e312116 100644 --- a/packages/frontend/src/scripts/aiscript/ui.ts +++ b/packages/frontend/src/scripts/aiscript/ui.ts @@ -100,6 +100,13 @@ export type AsUiSelect = AsUiComponentBase & { caption?: string; }; +export type AsUiFolder = AsUiComponentBase & { + type: 'folder'; + children?: AsUiComponent['id'][]; + title?: string; + opened?: boolean; +}; + export type AsUiPostFormButton = AsUiComponentBase & { type: 'postFormButton'; text?: string; @@ -110,7 +117,7 @@ export type AsUiPostFormButton = AsUiComponentBase & { }; }; -export type AsUiComponent = AsUiRoot | AsUiContainer | AsUiText | AsUiMfm | AsUiButton | AsUiButtons | AsUiSwitch | AsUiTextarea | AsUiTextInput | AsUiNumberInput | AsUiSelect | AsUiPostFormButton; +export type AsUiComponent = AsUiRoot | AsUiContainer | AsUiText | AsUiMfm | AsUiButton | AsUiButtons | AsUiSwitch | AsUiTextarea | AsUiTextInput | AsUiNumberInput | AsUiSelect | AsUiFolder | AsUiPostFormButton; export function patch(id: string, def: values.Value, call: (fn: values.VFn, args: values.Value[]) => Promise<values.Value>) { // TODO @@ -389,6 +396,26 @@ function getSelectOptions(def: values.Value | undefined, call: (fn: values.VFn, }; } +function getFolderOptions(def: values.Value | undefined): Omit<AsUiFolder, 'id' | 'type'> { + utils.assertObject(def); + + const children = def.value.get('children'); + if (children) utils.assertArray(children); + const title = def.value.get('title'); + if (title) utils.assertString(title); + const opened = def.value.get('opened'); + if (opened) utils.assertBoolean(opened); + + return { + children: children ? children.value.map(v => { + utils.assertObject(v); + return v.value.get('id').value; + }) : [], + title: title?.value ?? '', + opened: opened?.value ?? true, + }; +} + function getPostFormButtonOptions(def: values.Value | undefined, call: (fn: values.VFn, args: values.Value[]) => Promise<values.Value>): Omit<AsUiPostFormButton, 'id' | 'type'> { utils.assertObject(def); @@ -519,6 +546,10 @@ export function registerAsUiLib(components: Ref<AsUiComponent>[], done: (root: R return createComponentInstance('select', def, id, getSelectOptions, opts.call); }), + 'Ui:C:folder': values.FN_NATIVE(async ([def, id], opts) => { + return createComponentInstance('folder', def, id, getFolderOptions, opts.call); + }), + 'Ui:C:postFormButton': values.FN_NATIVE(async ([def, id], opts) => { return createComponentInstance('postFormButton', def, id, getPostFormButtonOptions, opts.call); }), From 9734ad42a1ec2fdc2012f9e0b7d55ea985e85629 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Thu, 5 Jan 2023 17:17:00 +0900 Subject: [PATCH 19/60] :art: --- .../frontend/src/components/form/folder.vue | 28 +++++++++++++++++-- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/packages/frontend/src/components/form/folder.vue b/packages/frontend/src/components/form/folder.vue index 40bbc97002..961f1c1cac 100644 --- a/packages/frontend/src/components/form/folder.vue +++ b/packages/frontend/src/components/form/folder.vue @@ -1,5 +1,5 @@ <template> -<div class="dwzlatin" :class="{ opened }"> +<div ref="rootEl" class="dwzlatin" :class="{ opened }"> <div class="header _button" @click="toggle"> <span class="icon"><slot name="icon"></slot></span> <span class="text"><slot name="label"></slot></span> @@ -9,7 +9,7 @@ <i v-else class="ti ti-chevron-down icon"></i> </span> </div> - <div v-if="openedAtLeastOnce" class="body"> + <div v-if="openedAtLeastOnce" class="body" :class="{ bgSame }"> <Transition :name="$store.state.animation ? 'folder-toggle' : ''" @enter="enter" @@ -30,7 +30,7 @@ </template> <script lang="ts" setup> -import { nextTick } from 'vue'; +import { nextTick, onMounted } from 'vue'; const props = withDefaults(defineProps<{ defaultOpen: boolean; @@ -38,6 +38,17 @@ const props = withDefaults(defineProps<{ defaultOpen: false, }); +const getBgColor = (el: HTMLElement) => { + const style = window.getComputedStyle(el); + if (style.backgroundColor && !['rgba(0, 0, 0, 0)', 'rgba(0,0,0,0)', 'transparent'].includes(style.backgroundColor)) { + return style.backgroundColor; + } else { + return el.parentElement ? getBgColor(el.parentElement) : 'transparent'; + } +}; + +let rootEl = $ref<HTMLElement>(); +let bgSame = $ref(false); let opened = $ref(props.defaultOpen); let openedAtLeastOnce = $ref(props.defaultOpen); @@ -72,6 +83,13 @@ function toggle() { opened = !opened; }); } + +onMounted(() => { + const computedStyle = getComputedStyle(document.documentElement); + const parentBg = getBgColor(rootEl.parentElement); + const myBg = computedStyle.getPropertyValue('--panel'); + bgSame = parentBg === myBg; +}); </script> <style lang="scss" scoped> @@ -142,6 +160,10 @@ function toggle() { background: var(--panel); border-radius: 0 0 6px 6px; container-type: inline-size; + + &.bgSame { + background: var(--bg); + } } &.opened { From ded8584fdd522b21a9612de4ab72c97cbdd2525e Mon Sep 17 00:00:00 2001 From: tamaina <tamaina@hotmail.co.jp> Date: Thu, 5 Jan 2023 17:18:32 +0900 Subject: [PATCH 20/60] =?UTF-8?q?miauth=E3=81=AEcallback=20url=E3=82=92sea?= =?UTF-8?q?rchParams.set=E3=81=A7=E4=BD=9C=E3=82=8B=E3=82=88=E3=81=86?= =?UTF-8?q?=E3=81=AB=20(#9470)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/frontend/src/pages/miauth.vue | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/frontend/src/pages/miauth.vue b/packages/frontend/src/pages/miauth.vue index 5de072cbfa..11a8d30942 100644 --- a/packages/frontend/src/pages/miauth.vue +++ b/packages/frontend/src/pages/miauth.vue @@ -44,7 +44,6 @@ import MkSignin from '@/components/MkSignin.vue'; import MkButton from '@/components/MkButton.vue'; import * as os from '@/os'; import { $i, login } from '@/account'; -import { appendQuery, query } from '@/scripts/url'; import { i18n } from '@/i18n'; const props = defineProps<{ @@ -70,9 +69,9 @@ async function accept(): Promise<void> { state = 'accepted'; if (props.callback) { - location.href = appendQuery(props.callback, query({ - session: props.session, - })); + const cbUrl = new URL(props.callback); + cbUrl.searchParams.set('session', props.session); + location.href = cbUrl.href; } } From 8df7530b544d18da1dc6462e070e27b0785a37f2 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Thu, 5 Jan 2023 17:27:56 +0900 Subject: [PATCH 21/60] 13.0.0-beta.23 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f5c033c457..9036172f1c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "13.0.0-beta.22", + "version": "13.0.0-beta.23", "codename": "indigo", "repository": { "type": "git", From 29f6f5fa5c5b98eb0e72fee6a9c57746c5c278f2 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Thu, 5 Jan 2023 17:30:21 +0900 Subject: [PATCH 22/60] Update scratchpad.vue --- packages/frontend/src/pages/scratchpad.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frontend/src/pages/scratchpad.vue b/packages/frontend/src/pages/scratchpad.vue index 7d097fbaaa..316133b968 100644 --- a/packages/frontend/src/pages/scratchpad.vue +++ b/packages/frontend/src/pages/scratchpad.vue @@ -6,7 +6,7 @@ <MkButton style="position: absolute; top: 8px; right: 8px;" primary @click="run()"><i class="ti ti-player-play"></i></MkButton> </div> - <MkContainer v-if="root && components.length > 0" :key="uiKey" :foldable="true"> + <MkContainer v-if="root && components.length > 1" :key="uiKey" :foldable="true"> <template #header>UI</template> <div :class="$style.ui"> <MkAsUi :component="root" :components="components" size="small"/> From 58ae2ccbfa71f2ff6d6cb6101a92c0288d5836fd Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Thu, 5 Jan 2023 19:50:52 +0900 Subject: [PATCH 23/60] :art: --- .../frontend/src/components/MkSuperMenu.vue | 4 +-- .../src/components/MkTokenGenerateWindow.vue | 34 +++++++++++-------- packages/frontend/src/pages/settings/apps.vue | 28 ++++++++------- 3 files changed, 37 insertions(+), 29 deletions(-) diff --git a/packages/frontend/src/components/MkSuperMenu.vue b/packages/frontend/src/components/MkSuperMenu.vue index 36960bfc31..bb2a789b3f 100644 --- a/packages/frontend/src/components/MkSuperMenu.vue +++ b/packages/frontend/src/components/MkSuperMenu.vue @@ -141,8 +141,8 @@ export default defineComponent({ margin-right: 0; margin-bottom: 6px; font-size: 1.5em; - width: 54px; - height: 54px; + width: 60px; + height: 60px; aspect-ratio: 1; background: var(--panel); border-radius: 100%; diff --git a/packages/frontend/src/components/MkTokenGenerateWindow.vue b/packages/frontend/src/components/MkTokenGenerateWindow.vue index 8d5b6f8635..894336136c 100644 --- a/packages/frontend/src/components/MkTokenGenerateWindow.vue +++ b/packages/frontend/src/components/MkTokenGenerateWindow.vue @@ -11,20 +11,25 @@ @ok="ok()" > <template #header>{{ title || $ts.generateAccessToken }}</template> - <div v-if="information" class="_section"> - <MkInfo warn>{{ information }}</MkInfo> - </div> - <div class="_section"> - <MkInput v-model="name"> - <template #label>{{ $ts.name }}</template> - </MkInput> - </div> - <div class="_section"> - <div style="margin-bottom: 16px;"><b>{{ $ts.permission }}</b></div> - <MkButton inline @click="disableAll">{{ $ts.disableAll }}</MkButton> - <MkButton inline @click="enableAll">{{ $ts.enableAll }}</MkButton> - <MkSwitch v-for="kind in (initialPermissions || kinds)" :key="kind" v-model="permissions[kind]">{{ $t(`_permissions.${kind}`) }}</MkSwitch> - </div> + + <MkSpacer :margin-min="20" :margin-max="28"> + <div class="_formRoot"> + <div v-if="information" class="_formBlock"> + <MkInfo warn>{{ information }}</MkInfo> + </div> + <div class="_formBlock"> + <MkInput v-model="name"> + <template #label>{{ $ts.name }}</template> + </MkInput> + </div> + <div class="_formBlock"><b>{{ $ts.permission }}</b></div> + <div style="display: flex; gap: var(--margin); flex-wrap: wrap;"> + <MkButton inline @click="disableAll">{{ i18n.ts.disableAll }}</MkButton> + <MkButton inline @click="enableAll">{{ i18n.ts.enableAll }}</MkButton> + </div> + <MkSwitch v-for="kind in (initialPermissions || kinds)" :key="kind" v-model="permissions[kind]" class="_formBlock">{{ $t(`_permissions.${kind}`) }}</MkSwitch> + </div> + </MkSpacer> </XModalWindow> </template> @@ -36,6 +41,7 @@ import MkSwitch from './form/switch.vue'; import MkButton from './MkButton.vue'; import MkInfo from './MkInfo.vue'; import XModalWindow from '@/components/MkModalWindow.vue'; +import { i18n } from '@/i18n'; const props = withDefaults(defineProps<{ title?: string | null; diff --git a/packages/frontend/src/pages/settings/apps.vue b/packages/frontend/src/pages/settings/apps.vue index 0154c0c951..fe237de906 100644 --- a/packages/frontend/src/pages/settings/apps.vue +++ b/packages/frontend/src/pages/settings/apps.vue @@ -8,28 +8,28 @@ </div> </template> <template #default="{items}"> - <div v-for="token in items" :key="token.id" class="_panel bfomjevm"> + <div v-for="token in items" :key="token.id" class="_panel _formBlock bfomjevm"> <img v-if="token.iconUrl" class="icon" :src="token.iconUrl" alt=""/> <div class="body"> <div class="name">{{ token.name }}</div> <div class="description">{{ token.description }}</div> - <div class=""> - <div>{{ i18n.ts.installedDate }}:</div> - <div><MkTime :time="token.createdAt"/></div> - </div> - <div class=""> - <div>{{ i18n.ts.lastUsedDate }}:</div> - <div><MkTime :time="token.lastUsedAt"/></div> - </div> - <div class="actions"> - <button class="_button" @click="revoke(token)"><i class="ti ti-trash"></i></button> - </div> - <details> + <MkKeyValue class="_formBlock" oneline> + <template #key>{{ i18n.ts.installedDate }}</template> + <template #value><MkTime :time="token.createdAt"/></template> + </MkKeyValue> + <MkKeyValue class="_formBlock" oneline> + <template #key>{{ i18n.ts.lastUsedDate }}</template> + <template #value><MkTime :time="token.lastUsedAt"/></template> + </MkKeyValue> + <details class="_formBlock"> <summary>{{ i18n.ts.details }}</summary> <ul> <li v-for="p in token.permission" :key="p">{{ $t(`_permissions.${p}`) }}</li> </ul> </details> + <div class="actions"> + <MkButton inline danger @click="revoke(token)"><i class="ti ti-trash"></i></MkButton> + </div> </div> </div> </template> @@ -43,6 +43,8 @@ import FormPagination from '@/components/MkPagination.vue'; import * as os from '@/os'; import { i18n } from '@/i18n'; import { definePageMetadata } from '@/scripts/page-metadata'; +import MkKeyValue from '@/components/MkKeyValue.vue'; +import MkButton from '@/components/MkButton.vue'; const list = ref<any>(null); From 047262ab20c83232c01b102961dfdc299073ea1a Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Thu, 5 Jan 2023 21:04:56 +0900 Subject: [PATCH 24/60] refactor --- .../frontend/src/components/MkAbuseReport.vue | 4 +- .../src/components/MkFileListForAdmin.vue | 3 - .../src/components/MkForgotPassword.vue | 11 +- .../frontend/src/components/MkFormDialog.vue | 18 +- .../MkNotificationSettingWindow.vue | 8 +- .../frontend/src/components/MkPagination.vue | 4 +- packages/frontend/src/components/MkSignin.vue | 16 +- packages/frontend/src/components/MkSignup.vue | 22 +-- .../src/components/MkTokenGenerateWindow.vue | 10 +- .../frontend/src/components/form/section.vue | 34 ++-- .../frontend/src/components/form/split.vue | 2 +- packages/frontend/src/pages/about-misskey.vue | 9 +- packages/frontend/src/pages/about.vue | 48 ++--- packages/frontend/src/pages/admin-file.vue | 18 +- packages/frontend/src/pages/admin/ads.vue | 12 +- .../src/pages/admin/announcements.vue | 2 +- .../src/pages/admin/bot-protection.vue | 22 +-- .../src/pages/admin/email-settings.vue | 51 +++--- .../src/pages/admin/emoji-edit-dialog.vue | 12 +- .../src/pages/admin/instance-block.vue | 4 +- .../src/pages/admin/integrations.discord.vue | 12 +- .../src/pages/admin/integrations.github.vue | 12 +- .../src/pages/admin/integrations.twitter.vue | 12 +- .../frontend/src/pages/admin/integrations.vue | 50 +++--- .../src/pages/admin/object-storage.vue | 26 +-- .../src/pages/admin/proxy-account.vue | 24 +-- .../frontend/src/pages/admin/security.vue | 44 ++--- .../frontend/src/pages/admin/settings.vue | 168 ++++++++++-------- packages/frontend/src/pages/api-console.vue | 14 +- .../frontend/src/pages/channel-editor.vue | 8 +- packages/frontend/src/pages/channel.vue | 6 +- packages/frontend/src/pages/channels.vue | 6 +- packages/frontend/src/pages/explore.users.vue | 18 +- packages/frontend/src/pages/explore.vue | 6 +- .../frontend/src/pages/flash/flash-edit.vue | 26 +-- packages/frontend/src/pages/flash/flash.vue | 2 +- packages/frontend/src/pages/gallery/index.vue | 4 +- packages/frontend/src/pages/instance-info.vue | 42 +++-- .../frontend/src/pages/my-antennas/editor.vue | 93 +++++----- .../frontend/src/pages/my-clips/index.vue | 2 +- packages/frontend/src/pages/my-lists/list.vue | 2 +- packages/frontend/src/pages/note.vue | 12 +- .../src/pages/page-editor/page-editor.vue | 14 +- packages/frontend/src/pages/page.vue | 2 +- packages/frontend/src/pages/registry.keys.vue | 38 ++-- .../frontend/src/pages/registry.value.vue | 58 +++--- .../frontend/src/pages/reset-password.vue | 6 +- .../src/pages/settings/account-info.vue | 2 +- .../frontend/src/pages/settings/accounts.vue | 2 +- packages/frontend/src/pages/settings/api.vue | 8 +- packages/frontend/src/pages/settings/apps.vue | 10 +- .../src/pages/settings/custom-css.vue | 6 +- packages/frontend/src/pages/settings/deck.vue | 6 +- .../src/pages/settings/delete-account.vue | 8 +- .../frontend/src/pages/settings/drive.vue | 65 +++---- .../frontend/src/pages/settings/email.vue | 41 +++-- .../frontend/src/pages/settings/general.vue | 96 +++++----- .../src/pages/settings/import-export.vue | 22 +-- .../src/pages/settings/instance-mute.vue | 6 +- .../src/pages/settings/integration.vue | 2 +- .../src/pages/settings/mute-block.vue | 2 +- .../frontend/src/pages/settings/navbar.vue | 8 +- .../src/pages/settings/notifications.vue | 33 ++-- .../frontend/src/pages/settings/other.vue | 12 +- .../src/pages/settings/plugin.install.vue | 8 +- .../frontend/src/pages/settings/plugin.vue | 12 +- .../pages/settings/preferences-backups.vue | 26 +-- .../frontend/src/pages/settings/privacy.vue | 52 +++--- .../frontend/src/pages/settings/profile.vue | 32 ++-- .../frontend/src/pages/settings/reaction.vue | 12 +- .../frontend/src/pages/settings/security.vue | 2 +- .../src/pages/settings/sounds.sound.vue | 4 +- .../frontend/src/pages/settings/sounds.vue | 18 +- .../pages/settings/statusbar.statusbar.vue | 36 ++-- .../frontend/src/pages/settings/statusbar.vue | 4 +- .../src/pages/settings/theme.install.vue | 6 +- .../src/pages/settings/theme.manage.vue | 12 +- .../frontend/src/pages/settings/theme.vue | 10 +- .../src/pages/settings/webhook.edit.vue | 28 +-- .../src/pages/settings/webhook.new.vue | 26 +-- .../frontend/src/pages/settings/webhook.vue | 14 +- .../frontend/src/pages/settings/word-mute.vue | 20 +-- packages/frontend/src/pages/theme-editor.vue | 20 +-- packages/frontend/src/pages/user-info.vue | 98 +++++----- packages/frontend/src/pages/user/clips.vue | 2 +- packages/frontend/src/pages/user/home.vue | 2 +- packages/frontend/src/pages/user/pages.vue | 2 +- .../frontend/src/pages/user/reactions.vue | 2 +- packages/frontend/src/pages/welcome.setup.vue | 8 +- packages/frontend/src/style.scss | 31 ++-- 90 files changed, 949 insertions(+), 884 deletions(-) diff --git a/packages/frontend/src/components/MkAbuseReport.vue b/packages/frontend/src/components/MkAbuseReport.vue index c065792882..cdfe323d50 100644 --- a/packages/frontend/src/components/MkAbuseReport.vue +++ b/packages/frontend/src/components/MkAbuseReport.vue @@ -1,5 +1,5 @@ <template> -<div class="bcekxzvu _gap _panel"> +<div class="bcekxzvu _margin _panel"> <div class="target"> <MkA v-user-preview="report.targetUserId" class="info" :to="`/user-info/${report.targetUserId}`"> <MkAvatar class="avatar" :user="report.targetUser" :show-indicator="true" :disable-link="true"/> @@ -8,7 +8,7 @@ <MkAcct class="acct" :user="report.targetUser" style="display: block;"/> </div> </MkA> - <MkKeyValue class="_formBlock"> + <MkKeyValue> <template #key>{{ i18n.ts.registeredDate }}</template> <template #value>{{ dateString(report.targetUser.createdAt) }} (<MkTime :time="report.targetUser.createdAt"/>)</template> </MkKeyValue> diff --git a/packages/frontend/src/components/MkFileListForAdmin.vue b/packages/frontend/src/components/MkFileListForAdmin.vue index 1335f88a7c..f340acaf2d 100644 --- a/packages/frontend/src/components/MkFileListForAdmin.vue +++ b/packages/frontend/src/components/MkFileListForAdmin.vue @@ -54,8 +54,6 @@ const props = defineProps<{ } .urempief { - margin-top: var(--margin); - &.list { > .file { display: flex; @@ -89,7 +87,6 @@ const props = defineProps<{ display: grid; grid-template-columns: repeat(auto-fill, minmax(130px, 1fr)); grid-gap: 12px; - margin: var(--margin) 0; > .file { position: relative; diff --git a/packages/frontend/src/components/MkForgotPassword.vue b/packages/frontend/src/components/MkForgotPassword.vue index 1b55451c94..29bf355cc1 100644 --- a/packages/frontend/src/components/MkForgotPassword.vue +++ b/packages/frontend/src/components/MkForgotPassword.vue @@ -1,5 +1,6 @@ <template> -<XModalWindow ref="dialog" +<XModalWindow + ref="dialog" :width="370" :height="400" @close="dialog.close()" @@ -8,18 +9,18 @@ <template #header>{{ i18n.ts.forgotPassword }}</template> <form v-if="instance.enableEmail" class="bafeceda" @submit.prevent="onSubmit"> - <div class="main _formRoot"> - <MkInput v-model="username" class="_formBlock" type="text" pattern="^[a-zA-Z0-9_]+$" :spellcheck="false" autofocus required> + <div class="main _autoGap"> + <MkInput v-model="username" type="text" pattern="^[a-zA-Z0-9_]+$" :spellcheck="false" autofocus required> <template #label>{{ i18n.ts.username }}</template> <template #prefix>@</template> </MkInput> - <MkInput v-model="email" class="_formBlock" type="email" :spellcheck="false" required> + <MkInput v-model="email" type="email" :spellcheck="false" required> <template #label>{{ i18n.ts.emailAddress }}</template> <template #caption>{{ i18n.ts._forgotPassword.enterEmail }}</template> </MkInput> - <MkButton class="_formBlock" type="submit" :disabled="processing" primary style="margin: 0 auto;">{{ i18n.ts.send }}</MkButton> + <MkButton type="submit" :disabled="processing" primary style="margin: 0 auto;">{{ i18n.ts.send }}</MkButton> </div> <div class="sub"> <MkA to="/about" class="_link">{{ i18n.ts._forgotPassword.ifNoEmail }}</MkA> diff --git a/packages/frontend/src/components/MkFormDialog.vue b/packages/frontend/src/components/MkFormDialog.vue index b2bf76a8c7..29fc040092 100644 --- a/packages/frontend/src/components/MkFormDialog.vue +++ b/packages/frontend/src/components/MkFormDialog.vue @@ -15,37 +15,37 @@ </template> <MkSpacer :margin-min="20" :margin-max="32"> - <div class="xkpnjxcv _formRoot"> + <div class="xkpnjxcv _autoGap"> <template v-for="item in Object.keys(form).filter(item => !form[item].hidden)"> - <FormInput v-if="form[item].type === 'number'" v-model="values[item]" type="number" :step="form[item].step || 1" class="_formBlock"> + <FormInput v-if="form[item].type === 'number'" v-model="values[item]" type="number" :step="form[item].step || 1"> <template #label><span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ $ts.optional }})</span></template> <template v-if="form[item].description" #caption>{{ form[item].description }}</template> </FormInput> - <FormInput v-else-if="form[item].type === 'string' && !form[item].multiline" v-model="values[item]" type="text" class="_formBlock"> + <FormInput v-else-if="form[item].type === 'string' && !form[item].multiline" v-model="values[item]" type="text"> <template #label><span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ $ts.optional }})</span></template> <template v-if="form[item].description" #caption>{{ form[item].description }}</template> </FormInput> - <FormTextarea v-else-if="form[item].type === 'string' && form[item].multiline" v-model="values[item]" class="_formBlock"> + <FormTextarea v-else-if="form[item].type === 'string' && form[item].multiline" v-model="values[item]"> <template #label><span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ $ts.optional }})</span></template> <template v-if="form[item].description" #caption>{{ form[item].description }}</template> </FormTextarea> - <FormSwitch v-else-if="form[item].type === 'boolean'" v-model="values[item]" class="_formBlock"> + <FormSwitch v-else-if="form[item].type === 'boolean'" v-model="values[item]"> <span v-text="form[item].label || item"></span> <template v-if="form[item].description" #caption>{{ form[item].description }}</template> </FormSwitch> - <FormSelect v-else-if="form[item].type === 'enum'" v-model="values[item]" class="_formBlock"> + <FormSelect v-else-if="form[item].type === 'enum'" v-model="values[item]"> <template #label><span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ $ts.optional }})</span></template> <option v-for="item in form[item].enum" :key="item.value" :value="item.value">{{ item.label }}</option> </FormSelect> - <FormRadios v-else-if="form[item].type === 'radio'" v-model="values[item]" class="_formBlock"> + <FormRadios v-else-if="form[item].type === 'radio'" v-model="values[item]"> <template #label><span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ $ts.optional }})</span></template> <option v-for="item in form[item].options" :key="item.value" :value="item.value">{{ item.label }}</option> </FormRadios> - <FormRange v-else-if="form[item].type === 'range'" v-model="values[item]" :min="form[item].min" :max="form[item].max" :step="form[item].step" :text-converter="form[item].textConverter" class="_formBlock"> + <FormRange v-else-if="form[item].type === 'range'" v-model="values[item]" :min="form[item].min" :max="form[item].max" :step="form[item].step" :text-converter="form[item].textConverter"> <template #label><span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ $ts.optional }})</span></template> <template v-if="form[item].description" #caption>{{ form[item].description }}</template> </FormRange> - <MkButton v-else-if="form[item].type === 'button'" class="_formBlock" @click="form[item].action($event, values)"> + <MkButton v-else-if="form[item].type === 'button'" @click="form[item].action($event, values)"> <span v-text="form[item].content || item"></span> </MkButton> </template> diff --git a/packages/frontend/src/components/MkNotificationSettingWindow.vue b/packages/frontend/src/components/MkNotificationSettingWindow.vue index 754d8d687b..e58a11bf5c 100644 --- a/packages/frontend/src/components/MkNotificationSettingWindow.vue +++ b/packages/frontend/src/components/MkNotificationSettingWindow.vue @@ -12,20 +12,20 @@ <template #header>{{ i18n.ts.notificationSetting }}</template> <MkSpacer :margin-min="20" :margin-max="28"> - <div class="_formRoot"> + <div class="_autoGap"> <template v-if="showGlobalToggle"> - <MkSwitch v-model="useGlobalSetting" class="_formBlock"> + <MkSwitch v-model="useGlobalSetting"> {{ i18n.ts.useGlobalSetting }} <template #caption>{{ i18n.ts.useGlobalSettingDesc }}</template> </MkSwitch> </template> <template v-if="!useGlobalSetting"> - <MkInfo class="_formBlock">{{ i18n.ts.notificationSettingDesc }}</MkInfo> + <MkInfo>{{ i18n.ts.notificationSettingDesc }}</MkInfo> <div style="display: flex; gap: var(--margin); flex-wrap: wrap;"> <MkButton inline @click="disableAll">{{ i18n.ts.disableAll }}</MkButton> <MkButton inline @click="enableAll">{{ i18n.ts.enableAll }}</MkButton> </div> - <MkSwitch v-for="ntype in notificationTypes" class="_formBlock" :key="ntype" v-model="typesMap[ntype]">{{ i18n.t(`_notification._types.${ntype}`) }}</MkSwitch> + <MkSwitch v-for="ntype in notificationTypes" :key="ntype" v-model="typesMap[ntype]">{{ i18n.t(`_notification._types.${ntype}`) }}</MkSwitch> </template> </div> </MkSpacer> diff --git a/packages/frontend/src/components/MkPagination.vue b/packages/frontend/src/components/MkPagination.vue index 2c0a30a888..b92e6d2360 100644 --- a/packages/frontend/src/components/MkPagination.vue +++ b/packages/frontend/src/components/MkPagination.vue @@ -14,14 +14,14 @@ </div> <div v-else ref="rootEl"> - <div v-show="pagination.reversed && more" key="_more_" class="cxiknjgy _gap"> + <div v-show="pagination.reversed && more" key="_more_" class="cxiknjgy _margin"> <MkButton v-if="!moreFetching" class="button" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }" primary @click="fetchMoreAhead"> {{ i18n.ts.loadMore }} </MkButton> <MkLoading v-else class="loading"/> </div> <slot :items="items"></slot> - <div v-show="!pagination.reversed && more" key="_more_" class="cxiknjgy _gap"> + <div v-show="!pagination.reversed && more" key="_more_" class="cxiknjgy _margin"> <MkButton v-if="!moreFetching" v-appear="($store.state.enableInfiniteScroll && !disableAutoLoad) ? fetchMore : null" class="button" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }" primary @click="fetchMore"> {{ i18n.ts.loadMore }} </MkButton> diff --git a/packages/frontend/src/components/MkSignin.vue b/packages/frontend/src/components/MkSignin.vue index 96f18f8d61..281c935eb6 100644 --- a/packages/frontend/src/components/MkSignin.vue +++ b/packages/frontend/src/components/MkSignin.vue @@ -1,20 +1,20 @@ <template> <form class="eppvobhk _monolithic_" :class="{ signing, totpLogin }" @submit.prevent="onSubmit"> - <div class="auth _section _formRoot"> + <div class="auth _section _autoGap"> <div v-show="withAvatar" class="avatar" :style="{ backgroundImage: user ? `url('${ user.avatarUrl }')` : null, marginBottom: message ? '1.5em' : null }"></div> <MkInfo v-if="message"> {{ message }} </MkInfo> - <div v-if="!totpLogin" class="normal-signin"> - <MkInput v-model="username" class="_formBlock" :placeholder="i18n.ts.username" type="text" pattern="^[a-zA-Z0-9_]+$" :spellcheck="false" autofocus required data-cy-signin-username @update:model-value="onUsernameChange"> + <div v-if="!totpLogin" class="normal-signin _autoGap"> + <MkInput v-model="username" :placeholder="i18n.ts.username" type="text" pattern="^[a-zA-Z0-9_]+$" :spellcheck="false" autofocus required data-cy-signin-username @update:model-value="onUsernameChange"> <template #prefix>@</template> <template #suffix>@{{ host }}</template> </MkInput> - <MkInput v-if="!user || user && !user.usePasswordLessLogin" v-model="password" class="_formBlock" :placeholder="i18n.ts.password" type="password" :with-password-toggle="true" required data-cy-signin-password> + <MkInput v-if="!user || user && !user.usePasswordLessLogin" v-model="password" :placeholder="i18n.ts.password" type="password" :with-password-toggle="true" required data-cy-signin-password> <template #prefix><i class="ti ti-lock"></i></template> <template #caption><button class="_textButton" type="button" @click="resetPassword">{{ i18n.ts.forgotPassword }}</button></template> </MkInput> - <MkButton class="_formBlock" type="submit" primary :disabled="signing" style="margin: 0 auto;">{{ signing ? i18n.ts.loggingIn : i18n.ts.login }}</MkButton> + <MkButton type="submit" primary :disabled="signing" style="margin: 0 auto;">{{ signing ? i18n.ts.loggingIn : i18n.ts.login }}</MkButton> </div> <div v-if="totpLogin" class="2fa-signin" :class="{ securityKeys: user && user.securityKeys }"> <div v-if="user && user.securityKeys" class="twofa-group tap-group"> @@ -41,9 +41,9 @@ </div> </div> <div class="social _section"> - <a v-if="meta && meta.enableTwitterIntegration" class="_borderButton _gap" :href="`${apiUrl}/signin/twitter`"><i class="ti ti-brand-twitter" style="margin-right: 4px;"></i>{{ $t('signinWith', { x: 'Twitter' }) }}</a> - <a v-if="meta && meta.enableGithubIntegration" class="_borderButton _gap" :href="`${apiUrl}/signin/github`"><i class="ti ti-brand-github" style="margin-right: 4px;"></i>{{ $t('signinWith', { x: 'GitHub' }) }}</a> - <a v-if="meta && meta.enableDiscordIntegration" class="_borderButton _gap" :href="`${apiUrl}/signin/discord`"><i class="ti ti-brand-discord" style="margin-right: 4px;"></i>{{ $t('signinWith', { x: 'Discord' }) }}</a> + <a v-if="meta && meta.enableTwitterIntegration" class="_borderButton _margin" :href="`${apiUrl}/signin/twitter`"><i class="ti ti-brand-twitter" style="margin-right: 4px;"></i>{{ $t('signinWith', { x: 'Twitter' }) }}</a> + <a v-if="meta && meta.enableGithubIntegration" class="_borderButton _margin" :href="`${apiUrl}/signin/github`"><i class="ti ti-brand-github" style="margin-right: 4px;"></i>{{ $t('signinWith', { x: 'GitHub' }) }}</a> + <a v-if="meta && meta.enableDiscordIntegration" class="_borderButton _margin" :href="`${apiUrl}/signin/discord`"><i class="ti ti-brand-discord" style="margin-right: 4px;"></i>{{ $t('signinWith', { x: 'Discord' }) }}</a> </div> </form> </template> diff --git a/packages/frontend/src/components/MkSignup.vue b/packages/frontend/src/components/MkSignup.vue index d987425ca3..66a14e5608 100644 --- a/packages/frontend/src/components/MkSignup.vue +++ b/packages/frontend/src/components/MkSignup.vue @@ -1,10 +1,10 @@ <template> -<form class="qlvuhzng _formRoot" autocomplete="new-password" @submit.prevent="onSubmit"> - <MkInput v-if="instance.disableRegistration" v-model="invitationCode" class="_formBlock" type="text" :spellcheck="false" required> +<form class="qlvuhzng _autoGap" autocomplete="new-password" @submit.prevent="onSubmit"> + <MkInput v-if="instance.disableRegistration" v-model="invitationCode" type="text" :spellcheck="false" required> <template #label>{{ i18n.ts.invitationCode }}</template> <template #prefix><i class="ti ti-key"></i></template> </MkInput> - <MkInput v-model="username" class="_formBlock" type="text" pattern="^[a-zA-Z0-9_]{1,20}$" :spellcheck="false" required data-cy-signup-username @update:model-value="onChangeUsername"> + <MkInput v-model="username" type="text" pattern="^[a-zA-Z0-9_]{1,20}$" :spellcheck="false" required data-cy-signup-username @update:model-value="onChangeUsername"> <template #label>{{ i18n.ts.username }} <div v-tooltip:dialog="i18n.ts.usernameInfo" class="_button _help"><i class="ti ti-question-circle"></i></div></template> <template #prefix>@</template> <template #suffix>@{{ host }}</template> @@ -18,7 +18,7 @@ <span v-else-if="usernameState === 'max-range'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts.tooLong }}</span> </template> </MkInput> - <MkInput v-if="instance.emailRequiredForSignup" v-model="email" class="_formBlock" :debounce="true" type="email" :spellcheck="false" required data-cy-signup-email @update:model-value="onChangeEmail"> + <MkInput v-if="instance.emailRequiredForSignup" v-model="email" :debounce="true" type="email" :spellcheck="false" required data-cy-signup-email @update:model-value="onChangeEmail"> <template #label>{{ i18n.ts.emailAddress }} <div v-tooltip:dialog="i18n.ts._signup.emailAddressInfo" class="_button _help"><i class="ti ti-question-circle"></i></div></template> <template #prefix><i class="ti ti-mail"></i></template> <template #caption> @@ -33,7 +33,7 @@ <span v-else-if="emailState === 'error'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts.error }}</span> </template> </MkInput> - <MkInput v-model="password" class="_formBlock" type="password" autocomplete="new-password" required data-cy-signup-password @update:model-value="onChangePassword"> + <MkInput v-model="password" type="password" autocomplete="new-password" required data-cy-signup-password @update:model-value="onChangePassword"> <template #label>{{ i18n.ts.password }}</template> <template #prefix><i class="ti ti-lock"></i></template> <template #caption> @@ -42,7 +42,7 @@ <span v-if="passwordStrength == 'high'" style="color: var(--success)"><i class="ti ti-check ti-fw"></i> {{ i18n.ts.strongPassword }}</span> </template> </MkInput> - <MkInput v-model="retypedPassword" class="_formBlock" type="password" autocomplete="new-password" required data-cy-signup-password-retype @update:model-value="onChangePasswordRetype"> + <MkInput v-model="retypedPassword" type="password" autocomplete="new-password" required data-cy-signup-password-retype @update:model-value="onChangePasswordRetype"> <template #label>{{ i18n.ts.password }} ({{ i18n.ts.retype }})</template> <template #prefix><i class="ti ti-lock"></i></template> <template #caption> @@ -50,17 +50,17 @@ <span v-if="passwordRetypeState == 'not-match'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts.passwordNotMatched }}</span> </template> </MkInput> - <MkSwitch v-if="instance.tosUrl" v-model="ToSAgreement" class="_formBlock tou"> + <MkSwitch v-if="instance.tosUrl" v-model="ToSAgreement" class="tou"> <I18n :src="i18n.ts.agreeTo"> <template #0> <a :href="instance.tosUrl" class="_link" target="_blank">{{ i18n.ts.tos }}</a> </template> </I18n> </MkSwitch> - <MkCaptcha v-if="instance.enableHcaptcha" ref="hcaptcha" v-model="hCaptchaResponse" class="_formBlock captcha" provider="hcaptcha" :sitekey="instance.hcaptchaSiteKey"/> - <MkCaptcha v-if="instance.enableRecaptcha" ref="recaptcha" v-model="reCaptchaResponse" class="_formBlock captcha" provider="recaptcha" :sitekey="instance.recaptchaSiteKey"/> - <MkCaptcha v-if="instance.enableTurnstile" ref="turnstile" v-model="turnstileResponse" class="_formBlock captcha" provider="turnstile" :sitekey="instance.turnstileSiteKey"/> - <MkButton class="_formBlock" type="submit" :disabled="shouldDisableSubmitting" gradate data-cy-signup-submit>{{ i18n.ts.start }}</MkButton> + <MkCaptcha v-if="instance.enableHcaptcha" ref="hcaptcha" v-model="hCaptchaResponse" class="captcha" provider="hcaptcha" :sitekey="instance.hcaptchaSiteKey"/> + <MkCaptcha v-if="instance.enableRecaptcha" ref="recaptcha" v-model="reCaptchaResponse" class="captcha" provider="recaptcha" :sitekey="instance.recaptchaSiteKey"/> + <MkCaptcha v-if="instance.enableTurnstile" ref="turnstile" v-model="turnstileResponse" class="captcha" provider="turnstile" :sitekey="instance.turnstileSiteKey"/> + <MkButton type="submit" :disabled="shouldDisableSubmitting" gradate data-cy-signup-submit>{{ i18n.ts.start }}</MkButton> </form> </template> diff --git a/packages/frontend/src/components/MkTokenGenerateWindow.vue b/packages/frontend/src/components/MkTokenGenerateWindow.vue index 894336136c..1762a0d33e 100644 --- a/packages/frontend/src/components/MkTokenGenerateWindow.vue +++ b/packages/frontend/src/components/MkTokenGenerateWindow.vue @@ -13,21 +13,21 @@ <template #header>{{ title || $ts.generateAccessToken }}</template> <MkSpacer :margin-min="20" :margin-max="28"> - <div class="_formRoot"> - <div v-if="information" class="_formBlock"> + <div class="_autoGap"> + <div v-if="information"> <MkInfo warn>{{ information }}</MkInfo> </div> - <div class="_formBlock"> + <div> <MkInput v-model="name"> <template #label>{{ $ts.name }}</template> </MkInput> </div> - <div class="_formBlock"><b>{{ $ts.permission }}</b></div> + <div><b>{{ $ts.permission }}</b></div> <div style="display: flex; gap: var(--margin); flex-wrap: wrap;"> <MkButton inline @click="disableAll">{{ i18n.ts.disableAll }}</MkButton> <MkButton inline @click="enableAll">{{ i18n.ts.enableAll }}</MkButton> </div> - <MkSwitch v-for="kind in (initialPermissions || kinds)" :key="kind" v-model="permissions[kind]" class="_formBlock">{{ $t(`_permissions.${kind}`) }}</MkSwitch> + <MkSwitch v-for="kind in (initialPermissions || kinds)" :key="kind" v-model="permissions[kind]">{{ $t(`_permissions.${kind}`) }}</MkSwitch> </div> </MkSpacer> </XModalWindow> diff --git a/packages/frontend/src/components/form/section.vue b/packages/frontend/src/components/form/section.vue index c6e34ef1cc..a838164978 100644 --- a/packages/frontend/src/components/form/section.vue +++ b/packages/frontend/src/components/form/section.vue @@ -1,35 +1,27 @@ <template> -<div class="vrtktovh _formBlock"> +<div class="vrtktovh" :class="{ first }"> <div class="label"><slot name="label"></slot></div> - <div class="main _formRoot"> + <div class="main"> <slot></slot> </div> </div> </template> <script lang="ts" setup> +defineProps<{ + first?: boolean; +}>(); </script> <style lang="scss" scoped> .vrtktovh { border-top: solid 0.5px var(--divider); - border-bottom: solid 0.5px var(--divider); - - & + .vrtktovh { - border-top: none; - } - - &:first-child { - border-top: none; - } - - &:last-child { - border-bottom: none; - } + //border-bottom: solid 0.5px var(--divider); > .label { font-weight: bold; - margin: 1.5em 0 16px 0; + padding: 1.5em 0 0 0; + margin: 0 0 16px 0; &:empty { display: none; @@ -37,7 +29,15 @@ } > .main { - margin: 1.5em 0; + margin: 1.5em 0 0 0; + } + + &.first { + border-top: none; + + > .label { + padding-top: 0; + } } } </style> diff --git a/packages/frontend/src/components/form/split.vue b/packages/frontend/src/components/form/split.vue index 301a8a84e5..3cee41e3d6 100644 --- a/packages/frontend/src/components/form/split.vue +++ b/packages/frontend/src/components/form/split.vue @@ -1,5 +1,5 @@ <template> -<div class="terlnhxf _formBlock"> +<div class="terlnhxf"> <slot></slot> </div> </template> diff --git a/packages/frontend/src/pages/about-misskey.vue b/packages/frontend/src/pages/about-misskey.vue index 5085b12527..2f88dae738 100644 --- a/packages/frontend/src/pages/about-misskey.vue +++ b/packages/frontend/src/pages/about-misskey.vue @@ -3,18 +3,17 @@ <template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template> <div style="overflow: clip;"> <MkSpacer :content-max="600" :margin-min="20"> - <div class="_formRoot znqjceqz"> - <div id="debug"></div> - <div ref="containerEl" v-panel class="_formBlock about" :class="{ playing: easterEggEngine != null }"> + <div class="_autoGap znqjceqz"> + <div ref="containerEl" v-panel class="about" :class="{ playing: easterEggEngine != null }"> <img src="/client-assets/about-icon.png" alt="" class="icon" draggable="false" @load="iconLoaded" @click="gravity"/> <div class="misskey">Misskey</div> <div class="version">v{{ version }}</div> <span v-for="emoji in easterEggEmojis" :key="emoji.id" class="emoji" :data-physics-x="emoji.left" :data-physics-y="emoji.top" :class="{ _physics_circle_: !emoji.emoji.startsWith(':') }"><MkEmoji class="emoji" :emoji="emoji.emoji" :is-reaction="false" :normal="true" :no-style="true"/></span> </div> - <div class="_formBlock" style="text-align: center;"> + <div style="text-align: center;"> {{ i18n.ts._aboutMisskey.about }}<br><a href="https://misskey-hub.net/docs/misskey.html" target="_blank" class="_link">{{ i18n.ts.learnMore }}</a> </div> - <div class="_formBlock" style="text-align: center;"> + <div style="text-align: center;"> <MkButton primary rounded inline @click="iLoveMisskey">I <Mfm text="$[jelly ❤]"/> #Misskey</MkButton> </div> <FormSection> diff --git a/packages/frontend/src/pages/about.vue b/packages/frontend/src/pages/about.vue index 1d6a844fbd..27470d1a8e 100644 --- a/packages/frontend/src/pages/about.vue +++ b/packages/frontend/src/pages/about.vue @@ -2,8 +2,8 @@ <MkStickyContainer> <template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template> <MkSpacer v-if="tab === 'overview'" :content-max="600" :margin-min="20"> - <div class="_formRoot"> - <div class="_formBlock fwhjspax" :style="{ backgroundImage: `url(${ $instance.bannerUrl })` }"> + <div class="_autoGap"> + <div class="fwhjspax" :style="{ backgroundImage: `url(${ $instance.bannerUrl })` }"> <div class="content"> <img :src="$instance.iconUrl ?? $instance.faviconUrl ?? '/favicon.ico'" alt="" class="icon"/> <div class="name"> @@ -12,44 +12,48 @@ </div> </div> - <MkKeyValue class="_formBlock"> + <MkKeyValue> <template #key>{{ i18n.ts.description }}</template> <template #value><div v-html="$instance.description"></div></template> </MkKeyValue> <FormSection> - <MkKeyValue class="_formBlock" :copy="version"> - <template #key>Misskey</template> - <template #value>{{ version }}</template> - </MkKeyValue> - <div class="_formBlock" v-html="i18n.t('poweredByMisskeyDescription', { name: $instance.name ?? host })"> + <div class="_autoGap"> + <MkKeyValue :copy="version"> + <template #key>Misskey</template> + <template #value>{{ version }}</template> + </MkKeyValue> + <div v-html="i18n.t('poweredByMisskeyDescription', { name: $instance.name ?? host })"> + </div> + <FormLink to="/about-misskey">{{ i18n.ts.aboutMisskey }}</FormLink> </div> - <FormLink to="/about-misskey">{{ i18n.ts.aboutMisskey }}</FormLink> </FormSection> <FormSection> - <FormSplit> - <MkKeyValue class="_formBlock"> - <template #key>{{ i18n.ts.administrator }}</template> - <template #value>{{ $instance.maintainerName }}</template> - </MkKeyValue> - <MkKeyValue class="_formBlock"> - <template #key>{{ i18n.ts.contact }}</template> - <template #value>{{ $instance.maintainerEmail }}</template> - </MkKeyValue> - </FormSplit> - <FormLink v-if="$instance.tosUrl" :to="$instance.tosUrl" class="_formBlock" external>{{ i18n.ts.tos }}</FormLink> + <div class="_autoGap"> + <FormSplit> + <MkKeyValue> + <template #key>{{ i18n.ts.administrator }}</template> + <template #value>{{ $instance.maintainerName }}</template> + </MkKeyValue> + <MkKeyValue> + <template #key>{{ i18n.ts.contact }}</template> + <template #value>{{ $instance.maintainerEmail }}</template> + </MkKeyValue> + </FormSplit> + <FormLink v-if="$instance.tosUrl" :to="$instance.tosUrl" external>{{ i18n.ts.tos }}</FormLink> + </div> </FormSection> <FormSuspense :p="initStats"> <FormSection> <template #label>{{ i18n.ts.statistics }}</template> <FormSplit> - <MkKeyValue class="_formBlock"> + <MkKeyValue> <template #key>{{ i18n.ts.users }}</template> <template #value>{{ number(stats.originalUsersCount) }}</template> </MkKeyValue> - <MkKeyValue class="_formBlock"> + <MkKeyValue> <template #key>{{ i18n.ts.notes }}</template> <template #value>{{ number(stats.originalNotesCount) }}</template> </MkKeyValue> diff --git a/packages/frontend/src/pages/admin-file.vue b/packages/frontend/src/pages/admin-file.vue index a11249e75d..92972c1168 100644 --- a/packages/frontend/src/pages/admin-file.vue +++ b/packages/frontend/src/pages/admin-file.vue @@ -2,11 +2,11 @@ <MkStickyContainer> <template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template> <MkSpacer v-if="file" :content-max="600" :margin-min="16" :margin-max="32"> - <div v-if="tab === 'overview'" class="cxqhhsmd _formRoot"> - <a class="_formBlock thumbnail" :href="file.url" target="_blank"> + <div v-if="tab === 'overview'" class="cxqhhsmd _autoGap"> + <a class="thumbnail" :href="file.url" target="_blank"> <MkDriveFileThumbnail class="thumbnail" :file="file" fit="contain"/> </a> - <div class="_formBlock"> + <div> <MkKeyValue :copy="file.type" oneline style="margin: 1em 0;"> <template #key>MIME Type</template> <template #value><span class="_monospace">{{ file.type }}</span></template> @@ -31,29 +31,29 @@ <MkA v-if="file.user" class="user" :to="`/user-info/${file.user.id}`"> <MkUserCardMini :user="file.user"/> </MkA> - <div class="_formBlock"> + <div> <MkSwitch v-model="isSensitive" @update:model-value="toggleIsSensitive">NSFW</MkSwitch> </div> - <div class="_formBlock"> + <div> <MkButton danger @click="del"><i class="ti ti-trash"></i> {{ i18n.ts.delete }}</MkButton> </div> </div> - <div v-else-if="tab === 'ip' && info" class="_formRoot"> + <div v-else-if="tab === 'ip' && info" class="_autoGap"> <MkInfo v-if="!iAmAdmin" warn>{{ i18n.ts.requireAdminForView }}</MkInfo> - <MkKeyValue v-if="info.requestIp" class="_formBlock _monospace" :copy="info.requestIp" oneline> + <MkKeyValue v-if="info.requestIp" class="_monospace" :copy="info.requestIp" oneline> <template #key>IP</template> <template #value>{{ info.requestIp }}</template> </MkKeyValue> <FormSection v-if="info.requestHeaders"> <template #label>Headers</template> - <MkKeyValue v-for="(v, k) in info.requestHeaders" :key="k" class="_formBlock _monospace"> + <MkKeyValue v-for="(v, k) in info.requestHeaders" :key="k" class="_monospace"> <template #key>{{ k }}</template> <template #value>{{ v }}</template> </MkKeyValue> </FormSection> </div> - <div v-else-if="tab === 'raw'" class="_formRoot"> + <div v-else-if="tab === 'raw'" class="_autoGap"> <MkObjectView v-if="info" tall :value="info"> </MkObjectView> </div> diff --git a/packages/frontend/src/pages/admin/ads.vue b/packages/frontend/src/pages/admin/ads.vue index 2ec926c65c..e7ab1c8556 100644 --- a/packages/frontend/src/pages/admin/ads.vue +++ b/packages/frontend/src/pages/admin/ads.vue @@ -3,15 +3,15 @@ <template #header><XHeader :actions="headerActions" :tabs="headerTabs"/></template> <MkSpacer :content-max="900"> <div class="uqshojas"> - <div v-for="ad in ads" class="_panel _formRoot ad"> + <div v-for="ad in ads" class="_panel _autoGap ad"> <MkAd v-if="ad.url" :specify="ad"/> - <MkInput v-model="ad.url" type="url" class="_formBlock"> + <MkInput v-model="ad.url" type="url"> <template #label>URL</template> </MkInput> - <MkInput v-model="ad.imageUrl" class="_formBlock"> + <MkInput v-model="ad.imageUrl"> <template #label>{{ i18n.ts.imageUrl }}</template> </MkInput> - <FormRadios v-model="ad.place" class="_formBlock"> + <FormRadios v-model="ad.place"> <template #label>Form</template> <option value="square">square</option> <option value="horizontal">horizontal</option> @@ -33,10 +33,10 @@ <template #label>{{ i18n.ts.expiration }}</template> </MkInput> </FormSplit> - <MkTextarea v-model="ad.memo" class="_formBlock"> + <MkTextarea v-model="ad.memo"> <template #label>{{ i18n.ts.memo }}</template> </MkTextarea> - <div class="buttons _formBlock"> + <div class="buttons"> <MkButton class="button" inline primary style="margin-right: 12px;" @click="save(ad)"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</MkButton> <MkButton class="button" inline danger @click="remove(ad)"><i class="ti ti-trash"></i> {{ i18n.ts.remove }}</MkButton> </div> diff --git a/packages/frontend/src/pages/admin/announcements.vue b/packages/frontend/src/pages/admin/announcements.vue index 607ad8aa02..f5c2f11957 100644 --- a/packages/frontend/src/pages/admin/announcements.vue +++ b/packages/frontend/src/pages/admin/announcements.vue @@ -3,7 +3,7 @@ <template #header><XHeader :actions="headerActions" :tabs="headerTabs"/></template> <MkSpacer :content-max="900"> <div class="ztgjmzrw"> - <section v-for="announcement in announcements" class="_card _gap announcements"> + <section v-for="announcement in announcements" class="_card _margin announcements"> <div class="_content announcement"> <MkInput v-model="announcement.title"> <template #label>{{ i18n.ts.title }}</template> diff --git a/packages/frontend/src/pages/admin/bot-protection.vue b/packages/frontend/src/pages/admin/bot-protection.vue index d03961cf95..c213e9805b 100644 --- a/packages/frontend/src/pages/admin/bot-protection.vue +++ b/packages/frontend/src/pages/admin/bot-protection.vue @@ -1,8 +1,8 @@ <template> <div> <FormSuspense :p="init"> - <div class="_formRoot"> - <FormRadios v-model="provider" class="_formBlock"> + <div class="_autoGap"> + <FormRadios v-model="provider"> <option :value="null">{{ i18n.ts.none }} ({{ i18n.ts.notRecommended }})</option> <option value="hcaptcha">hCaptcha</option> <option value="recaptcha">reCAPTCHA</option> @@ -10,43 +10,43 @@ </FormRadios> <template v-if="provider === 'hcaptcha'"> - <FormInput v-model="hcaptchaSiteKey" class="_formBlock"> + <FormInput v-model="hcaptchaSiteKey"> <template #prefix><i class="ti ti-key"></i></template> <template #label>{{ i18n.ts.hcaptchaSiteKey }}</template> </FormInput> - <FormInput v-model="hcaptchaSecretKey" class="_formBlock"> + <FormInput v-model="hcaptchaSecretKey"> <template #prefix><i class="ti ti-key"></i></template> <template #label>{{ i18n.ts.hcaptchaSecretKey }}</template> </FormInput> - <FormSlot class="_formBlock"> + <FormSlot> <template #label>{{ i18n.ts.preview }}</template> <MkCaptcha provider="hcaptcha" :sitekey="hcaptchaSiteKey || '10000000-ffff-ffff-ffff-000000000001'"/> </FormSlot> </template> <template v-else-if="provider === 'recaptcha'"> - <FormInput v-model="recaptchaSiteKey" class="_formBlock"> + <FormInput v-model="recaptchaSiteKey"> <template #prefix><i class="ti ti-key"></i></template> <template #label>{{ i18n.ts.recaptchaSiteKey }}</template> </FormInput> - <FormInput v-model="recaptchaSecretKey" class="_formBlock"> + <FormInput v-model="recaptchaSecretKey"> <template #prefix><i class="ti ti-key"></i></template> <template #label>{{ i18n.ts.recaptchaSecretKey }}</template> </FormInput> - <FormSlot v-if="recaptchaSiteKey" class="_formBlock"> + <FormSlot v-if="recaptchaSiteKey"> <template #label>{{ i18n.ts.preview }}</template> <MkCaptcha provider="recaptcha" :sitekey="recaptchaSiteKey"/> </FormSlot> </template> <template v-else-if="provider === 'turnstile'"> - <FormInput v-model="turnstileSiteKey" class="_formBlock"> + <FormInput v-model="turnstileSiteKey"> <template #prefix><i class="ti ti-key"></i></template> <template #label>{{ i18n.ts.turnstileSiteKey }}</template> </FormInput> - <FormInput v-model="turnstileSecretKey" class="_formBlock"> + <FormInput v-model="turnstileSecretKey"> <template #prefix><i class="ti ti-key"></i></template> <template #label>{{ i18n.ts.turnstileSecretKey }}</template> </FormInput> - <FormSlot class="_formBlock"> + <FormSlot> <template #label>{{ i18n.ts.preview }}</template> <MkCaptcha provider="turnstile" :sitekey="turnstileSiteKey || '1x00000000000000000000AA'"/> </FormSlot> diff --git a/packages/frontend/src/pages/admin/email-settings.vue b/packages/frontend/src/pages/admin/email-settings.vue index 6c9dee1704..0bf6c79192 100644 --- a/packages/frontend/src/pages/admin/email-settings.vue +++ b/packages/frontend/src/pages/admin/email-settings.vue @@ -3,40 +3,43 @@ <template #header><XHeader :actions="headerActions" :tabs="headerTabs"/></template> <MkSpacer :content-max="700" :margin-min="16" :margin-max="32"> <FormSuspense :p="init"> - <div class="_formRoot"> - <FormSwitch v-model="enableEmail" class="_formBlock"> + <div class="_autoGap"> + <FormSwitch v-model="enableEmail"> <template #label>{{ i18n.ts.enableEmail }} ({{ i18n.ts.recommended }})</template> <template #caption>{{ i18n.ts.emailConfigInfo }}</template> </FormSwitch> <template v-if="enableEmail"> - <FormInput v-model="email" type="email" class="_formBlock"> + <FormInput v-model="email" type="email"> <template #label>{{ i18n.ts.emailAddress }}</template> </FormInput> <FormSection> <template #label>{{ i18n.ts.smtpConfig }}</template> - <FormSplit :min-width="280"> - <FormInput v-model="smtpHost" class="_formBlock"> - <template #label>{{ i18n.ts.smtpHost }}</template> - </FormInput> - <FormInput v-model="smtpPort" type="number" class="_formBlock"> - <template #label>{{ i18n.ts.smtpPort }}</template> - </FormInput> - </FormSplit> - <FormSplit :min-width="280"> - <FormInput v-model="smtpUser" class="_formBlock"> - <template #label>{{ i18n.ts.smtpUser }}</template> - </FormInput> - <FormInput v-model="smtpPass" type="password" class="_formBlock"> - <template #label>{{ i18n.ts.smtpPass }}</template> - </FormInput> - </FormSplit> - <FormInfo class="_formBlock">{{ i18n.ts.emptyToDisableSmtpAuth }}</FormInfo> - <FormSwitch v-model="smtpSecure" class="_formBlock"> - <template #label>{{ i18n.ts.smtpSecure }}</template> - <template #caption>{{ i18n.ts.smtpSecureInfo }}</template> - </FormSwitch> + + <div class="_autoGap"> + <FormSplit :min-width="280"> + <FormInput v-model="smtpHost"> + <template #label>{{ i18n.ts.smtpHost }}</template> + </FormInput> + <FormInput v-model="smtpPort" type="number"> + <template #label>{{ i18n.ts.smtpPort }}</template> + </FormInput> + </FormSplit> + <FormSplit :min-width="280"> + <FormInput v-model="smtpUser"> + <template #label>{{ i18n.ts.smtpUser }}</template> + </FormInput> + <FormInput v-model="smtpPass" type="password"> + <template #label>{{ i18n.ts.smtpPass }}</template> + </FormInput> + </FormSplit> + <FormInfo>{{ i18n.ts.emptyToDisableSmtpAuth }}</FormInfo> + <FormSwitch v-model="smtpSecure"> + <template #label>{{ i18n.ts.smtpSecure }}</template> + <template #caption>{{ i18n.ts.smtpSecureInfo }}</template> + </FormSwitch> + </div> </FormSection> </template> </div> diff --git a/packages/frontend/src/pages/admin/emoji-edit-dialog.vue b/packages/frontend/src/pages/admin/emoji-edit-dialog.vue index 610958e95e..115125bd4c 100644 --- a/packages/frontend/src/pages/admin/emoji-edit-dialog.vue +++ b/packages/frontend/src/pages/admin/emoji-edit-dialog.vue @@ -9,22 +9,22 @@ > <template #header>:{{ emoji.name }}:</template> - <div class="_monolithic_"> - <div class="yigymqpb _section"> + <MkSpacer :margin-min="20" :margin-max="28"> + <div class="yigymqpb _autoGap"> <img :src="`/emoji/${emoji.name}.webp`" class="img"/> - <MkInput v-model="name" class="_formBlock"> + <MkInput v-model="name"> <template #label>{{ i18n.ts.name }}</template> </MkInput> - <MkInput v-model="category" class="_formBlock" :datalist="categories"> + <MkInput v-model="category" :datalist="categories"> <template #label>{{ i18n.ts.category }}</template> </MkInput> - <MkInput v-model="aliases" class="_formBlock"> + <MkInput v-model="aliases"> <template #label>{{ i18n.ts.tags }}</template> <template #caption>{{ i18n.ts.setMultipleBySeparatingWithSpace }}</template> </MkInput> <MkButton danger @click="del()"><i class="ti ti-trash"></i> {{ i18n.ts.delete }}</MkButton> </div> - </div> + </MkSpacer> </XModalWindow> </template> diff --git a/packages/frontend/src/pages/admin/instance-block.vue b/packages/frontend/src/pages/admin/instance-block.vue index 1bdd174de4..6c6941daec 100644 --- a/packages/frontend/src/pages/admin/instance-block.vue +++ b/packages/frontend/src/pages/admin/instance-block.vue @@ -3,12 +3,12 @@ <template #header><XHeader :actions="headerActions" :tabs="headerTabs"/></template> <MkSpacer :content-max="700" :margin-min="16" :margin-max="32"> <FormSuspense :p="init"> - <FormTextarea v-model="blockedHosts" class="_formBlock"> + <FormTextarea v-model="blockedHosts"> <span>{{ i18n.ts.blockedInstances }}</span> <template #caption>{{ i18n.ts.blockedInstancesDescription }}</template> </FormTextarea> - <FormButton primary class="_formBlock" @click="save"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</FormButton> + <FormButton primary @click="save"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</FormButton> </FormSuspense> </MkSpacer> </MkStickyContainer> diff --git a/packages/frontend/src/pages/admin/integrations.discord.vue b/packages/frontend/src/pages/admin/integrations.discord.vue index 0a69c44c93..e5069b9f85 100644 --- a/packages/frontend/src/pages/admin/integrations.discord.vue +++ b/packages/frontend/src/pages/admin/integrations.discord.vue @@ -1,25 +1,25 @@ <template> <FormSuspense :p="init"> - <div class="_formRoot"> - <FormSwitch v-model="enableDiscordIntegration" class="_formBlock"> + <div class="_autoGap"> + <FormSwitch v-model="enableDiscordIntegration"> <template #label>{{ i18n.ts.enable }}</template> </FormSwitch> <template v-if="enableDiscordIntegration"> - <FormInfo class="_formBlock">Callback URL: {{ `${uri}/api/dc/cb` }}</FormInfo> + <FormInfo>Callback URL: {{ `${uri}/api/dc/cb` }}</FormInfo> - <FormInput v-model="discordClientId" class="_formBlock"> + <FormInput v-model="discordClientId"> <template #prefix><i class="ti ti-key"></i></template> <template #label>Client ID</template> </FormInput> - <FormInput v-model="discordClientSecret" class="_formBlock"> + <FormInput v-model="discordClientSecret"> <template #prefix><i class="ti ti-key"></i></template> <template #label>Client Secret</template> </FormInput> </template> - <FormButton primary class="_formBlock" @click="save"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</FormButton> + <FormButton primary @click="save"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</FormButton> </div> </FormSuspense> </template> diff --git a/packages/frontend/src/pages/admin/integrations.github.vue b/packages/frontend/src/pages/admin/integrations.github.vue index 66419d5891..8418e202ab 100644 --- a/packages/frontend/src/pages/admin/integrations.github.vue +++ b/packages/frontend/src/pages/admin/integrations.github.vue @@ -1,25 +1,25 @@ <template> <FormSuspense :p="init"> - <div class="_formRoot"> - <FormSwitch v-model="enableGithubIntegration" class="_formBlock"> + <div class="_autoGap"> + <FormSwitch v-model="enableGithubIntegration"> <template #label>{{ i18n.ts.enable }}</template> </FormSwitch> <template v-if="enableGithubIntegration"> - <FormInfo class="_formBlock">Callback URL: {{ `${uri}/api/gh/cb` }}</FormInfo> + <FormInfo>Callback URL: {{ `${uri}/api/gh/cb` }}</FormInfo> - <FormInput v-model="githubClientId" class="_formBlock"> + <FormInput v-model="githubClientId"> <template #prefix><i class="ti ti-key"></i></template> <template #label>Client ID</template> </FormInput> - <FormInput v-model="githubClientSecret" class="_formBlock"> + <FormInput v-model="githubClientSecret"> <template #prefix><i class="ti ti-key"></i></template> <template #label>Client Secret</template> </FormInput> </template> - <FormButton primary class="_formBlock" @click="save"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</FormButton> + <FormButton primary @click="save"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</FormButton> </div> </FormSuspense> </template> diff --git a/packages/frontend/src/pages/admin/integrations.twitter.vue b/packages/frontend/src/pages/admin/integrations.twitter.vue index 1e8d882b9c..8bd3c7c16d 100644 --- a/packages/frontend/src/pages/admin/integrations.twitter.vue +++ b/packages/frontend/src/pages/admin/integrations.twitter.vue @@ -1,25 +1,25 @@ <template> <FormSuspense :p="init"> - <div class="_formRoot"> - <FormSwitch v-model="enableTwitterIntegration" class="_formBlock"> + <div class="_autoGap"> + <FormSwitch v-model="enableTwitterIntegration"> <template #label>{{ i18n.ts.enable }}</template> </FormSwitch> <template v-if="enableTwitterIntegration"> - <FormInfo class="_formBlock">Callback URL: {{ `${uri}/api/tw/cb` }}</FormInfo> + <FormInfo>Callback URL: {{ `${uri}/api/tw/cb` }}</FormInfo> - <FormInput v-model="twitterConsumerKey" class="_formBlock"> + <FormInput v-model="twitterConsumerKey"> <template #prefix><i class="ti ti-key"></i></template> <template #label>Consumer Key</template> </FormInput> - <FormInput v-model="twitterConsumerSecret" class="_formBlock"> + <FormInput v-model="twitterConsumerSecret"> <template #prefix><i class="ti ti-key"></i></template> <template #label>Consumer Secret</template> </FormInput> </template> - <FormButton primary class="_formBlock" @click="save"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</FormButton> + <FormButton primary @click="save"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</FormButton> </div> </FormSuspense> </template> diff --git a/packages/frontend/src/pages/admin/integrations.vue b/packages/frontend/src/pages/admin/integrations.vue index 9cc35baefd..4ce0a29ae7 100644 --- a/packages/frontend/src/pages/admin/integrations.vue +++ b/packages/frontend/src/pages/admin/integrations.vue @@ -1,27 +1,31 @@ -<template><MkStickyContainer> +<template> +<MkStickyContainer> <template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template> - <MkSpacer :content-max="700" :margin-min="16" :margin-max="32"> - <FormSuspense :p="init"> - <FormFolder class="_formBlock"> - <template #icon><i class="ti ti-brand-twitter"></i></template> - <template #label>Twitter</template> - <template #suffix>{{ enableTwitterIntegration ? i18n.ts.enabled : i18n.ts.disabled }}</template> - <XTwitter/> - </FormFolder> - <FormFolder class="_formBlock"> - <template #icon><i class="ti ti-brand-github"></i></template> - <template #label>GitHub</template> - <template #suffix>{{ enableGithubIntegration ? i18n.ts.enabled : i18n.ts.disabled }}</template> - <XGithub/> - </FormFolder> - <FormFolder class="_formBlock"> - <template #icon><i class="ti ti-brand-discord"></i></template> - <template #label>Discord</template> - <template #suffix>{{ enableDiscordIntegration ? i18n.ts.enabled : i18n.ts.disabled }}</template> - <XDiscord/> - </FormFolder> - </FormSuspense> -</MkSpacer></MkStickyContainer> + <MkSpacer :content-max="700" :margin-min="16" :margin-max="32"> + <FormSuspense :p="init"> + <div class="_autoGap"> + <FormFolder> + <template #icon><i class="ti ti-brand-twitter"></i></template> + <template #label>Twitter</template> + <template #suffix>{{ enableTwitterIntegration ? i18n.ts.enabled : i18n.ts.disabled }}</template> + <XTwitter/> + </FormFolder> + <FormFolder> + <template #icon><i class="ti ti-brand-github"></i></template> + <template #label>GitHub</template> + <template #suffix>{{ enableGithubIntegration ? i18n.ts.enabled : i18n.ts.disabled }}</template> + <XGithub/> + </FormFolder> + <FormFolder> + <template #icon><i class="ti ti-brand-discord"></i></template> + <template #label>Discord</template> + <template #suffix>{{ enableDiscordIntegration ? i18n.ts.enabled : i18n.ts.disabled }}</template> + <XDiscord/> + </FormFolder> + </div> + </FormSuspense> + </MkSpacer> +</MkStickyContainer> </template> <script lang="ts" setup> diff --git a/packages/frontend/src/pages/admin/object-storage.vue b/packages/frontend/src/pages/admin/object-storage.vue index f2ab30eaa5..7effa16542 100644 --- a/packages/frontend/src/pages/admin/object-storage.vue +++ b/packages/frontend/src/pages/admin/object-storage.vue @@ -3,62 +3,62 @@ <template #header><XHeader :actions="headerActions" :tabs="headerTabs"/></template> <MkSpacer :content-max="700" :margin-min="16" :margin-max="32"> <FormSuspense :p="init"> - <div class="_formRoot"> - <FormSwitch v-model="useObjectStorage" class="_formBlock">{{ i18n.ts.useObjectStorage }}</FormSwitch> + <div class="_autoGap"> + <FormSwitch v-model="useObjectStorage">{{ i18n.ts.useObjectStorage }}</FormSwitch> <template v-if="useObjectStorage"> - <FormInput v-model="objectStorageBaseUrl" class="_formBlock"> + <FormInput v-model="objectStorageBaseUrl"> <template #label>{{ i18n.ts.objectStorageBaseUrl }}</template> <template #caption>{{ i18n.ts.objectStorageBaseUrlDesc }}</template> </FormInput> - <FormInput v-model="objectStorageBucket" class="_formBlock"> + <FormInput v-model="objectStorageBucket"> <template #label>{{ i18n.ts.objectStorageBucket }}</template> <template #caption>{{ i18n.ts.objectStorageBucketDesc }}</template> </FormInput> - <FormInput v-model="objectStoragePrefix" class="_formBlock"> + <FormInput v-model="objectStoragePrefix"> <template #label>{{ i18n.ts.objectStoragePrefix }}</template> <template #caption>{{ i18n.ts.objectStoragePrefixDesc }}</template> </FormInput> - <FormInput v-model="objectStorageEndpoint" class="_formBlock"> + <FormInput v-model="objectStorageEndpoint"> <template #label>{{ i18n.ts.objectStorageEndpoint }}</template> <template #caption>{{ i18n.ts.objectStorageEndpointDesc }}</template> </FormInput> - <FormInput v-model="objectStorageRegion" class="_formBlock"> + <FormInput v-model="objectStorageRegion"> <template #label>{{ i18n.ts.objectStorageRegion }}</template> <template #caption>{{ i18n.ts.objectStorageRegionDesc }}</template> </FormInput> <FormSplit :min-width="280"> - <FormInput v-model="objectStorageAccessKey" class="_formBlock"> + <FormInput v-model="objectStorageAccessKey"> <template #prefix><i class="ti ti-key"></i></template> <template #label>Access key</template> </FormInput> - <FormInput v-model="objectStorageSecretKey" class="_formBlock"> + <FormInput v-model="objectStorageSecretKey"> <template #prefix><i class="ti ti-key"></i></template> <template #label>Secret key</template> </FormInput> </FormSplit> - <FormSwitch v-model="objectStorageUseSSL" class="_formBlock"> + <FormSwitch v-model="objectStorageUseSSL"> <template #label>{{ i18n.ts.objectStorageUseSSL }}</template> <template #caption>{{ i18n.ts.objectStorageUseSSLDesc }}</template> </FormSwitch> - <FormSwitch v-model="objectStorageUseProxy" class="_formBlock"> + <FormSwitch v-model="objectStorageUseProxy"> <template #label>{{ i18n.ts.objectStorageUseProxy }}</template> <template #caption>{{ i18n.ts.objectStorageUseProxyDesc }}</template> </FormSwitch> - <FormSwitch v-model="objectStorageSetPublicRead" class="_formBlock"> + <FormSwitch v-model="objectStorageSetPublicRead"> <template #label>{{ i18n.ts.objectStorageSetPublicRead }}</template> </FormSwitch> - <FormSwitch v-model="objectStorageS3ForcePathStyle" class="_formBlock"> + <FormSwitch v-model="objectStorageS3ForcePathStyle"> <template #label>s3ForcePathStyle</template> </FormSwitch> </template> diff --git a/packages/frontend/src/pages/admin/proxy-account.vue b/packages/frontend/src/pages/admin/proxy-account.vue index 5d0d67980e..36a2718081 100644 --- a/packages/frontend/src/pages/admin/proxy-account.vue +++ b/packages/frontend/src/pages/admin/proxy-account.vue @@ -1,16 +1,18 @@ -<template><MkStickyContainer> +<template> +<MkStickyContainer> <template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template> - <MkSpacer :content-max="700" :margin-min="16" :margin-max="32"> - <FormSuspense :p="init"> - <MkInfo class="_formBlock">{{ i18n.ts.proxyAccountDescription }}</MkInfo> - <MkKeyValue class="_formBlock"> - <template #key>{{ i18n.ts.proxyAccount }}</template> - <template #value>{{ proxyAccount ? `@${proxyAccount.username}` : i18n.ts.none }}</template> - </MkKeyValue> + <MkSpacer :content-max="700" :margin-min="16" :margin-max="32"> + <FormSuspense :p="init"> + <MkInfo>{{ i18n.ts.proxyAccountDescription }}</MkInfo> + <MkKeyValue> + <template #key>{{ i18n.ts.proxyAccount }}</template> + <template #value>{{ proxyAccount ? `@${proxyAccount.username}` : i18n.ts.none }}</template> + </MkKeyValue> - <FormButton primary class="_formBlock" @click="chooseProxyAccount">{{ i18n.ts.selectAccount }}</FormButton> - </FormSuspense> -</MkSpacer></MkStickyContainer> + <FormButton primary @click="chooseProxyAccount">{{ i18n.ts.selectAccount }}</FormButton> + </FormSuspense> + </MkSpacer> +</MkStickyContainer> </template> <script lang="ts" setup> diff --git a/packages/frontend/src/pages/admin/security.vue b/packages/frontend/src/pages/admin/security.vue index 2682bda337..6bd26e8325 100644 --- a/packages/frontend/src/pages/admin/security.vue +++ b/packages/frontend/src/pages/admin/security.vue @@ -3,8 +3,8 @@ <template #header><XHeader :actions="headerActions" :tabs="headerTabs"/></template> <MkSpacer :content-max="700" :margin-min="16" :margin-max="32"> <FormSuspense :p="init"> - <div class="_formRoot"> - <FormFolder class="_formBlock"> + <div class="_autoGap"> + <FormFolder> <template #icon><i class="ti ti-shield"></i></template> <template #label>{{ i18n.ts.botProtection }}</template> <template v-if="enableHcaptcha" #suffix>hCaptcha</template> @@ -15,7 +15,7 @@ <XBotProtection/> </FormFolder> - <FormFolder class="_formBlock"> + <FormFolder> <template #icon><i class="ti ti-eye-off"></i></template> <template #label>{{ i18n.ts.sensitiveMediaDetection }}</template> <template v-if="sensitiveMediaDetection === 'all'" #suffix>{{ i18n.ts.all }}</template> @@ -23,76 +23,76 @@ <template v-else-if="sensitiveMediaDetection === 'remote'" #suffix>{{ i18n.ts.remoteOnly }}</template> <template v-else #suffix>{{ i18n.ts.none }}</template> - <div class="_formRoot"> - <span class="_formBlock">{{ i18n.ts._sensitiveMediaDetection.description }}</span> + <div class="_autoGap"> + <span>{{ i18n.ts._sensitiveMediaDetection.description }}</span> - <FormRadios v-model="sensitiveMediaDetection" class="_formBlock"> + <FormRadios v-model="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> </FormRadios> - <FormRange v-model="sensitiveMediaDetectionSensitivity" :min="0" :max="4" :step="1" :text-converter="(v) => `${v + 1}`" class="_formBlock"> + <FormRange v-model="sensitiveMediaDetectionSensitivity" :min="0" :max="4" :step="1" :text-converter="(v) => `${v + 1}`"> <template #label>{{ i18n.ts._sensitiveMediaDetection.sensitivity }}</template> <template #caption>{{ i18n.ts._sensitiveMediaDetection.sensitivityDescription }}</template> </FormRange> - <FormSwitch v-model="enableSensitiveMediaDetectionForVideos" class="_formBlock"> + <FormSwitch v-model="enableSensitiveMediaDetectionForVideos"> <template #label>{{ i18n.ts._sensitiveMediaDetection.analyzeVideos }}<span class="_beta">{{ i18n.ts.beta }}</span></template> <template #caption>{{ i18n.ts._sensitiveMediaDetection.analyzeVideosDescription }}</template> </FormSwitch> - <FormSwitch v-model="setSensitiveFlagAutomatically" class="_formBlock"> + <FormSwitch v-model="setSensitiveFlagAutomatically"> <template #label>{{ i18n.ts._sensitiveMediaDetection.setSensitiveFlagAutomatically }} ({{ i18n.ts.notRecommended }})</template> <template #caption>{{ i18n.ts._sensitiveMediaDetection.setSensitiveFlagAutomaticallyDescription }}</template> </FormSwitch> <!-- 現状 false positive が多すぎて実用に耐えない - <FormSwitch v-model="disallowUploadWhenPredictedAsPorn" class="_formBlock"> + <FormSwitch v-model="disallowUploadWhenPredictedAsPorn"> <template #label>{{ i18n.ts._sensitiveMediaDetection.disallowUploadWhenPredictedAsPorn }}</template> </FormSwitch> --> - <FormButton primary class="_formBlock" @click="save"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</FormButton> + <FormButton primary @click="save"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</FormButton> </div> </FormFolder> - <FormFolder class="_formBlock"> + <FormFolder> <template #label>Active Email Validation</template> <template v-if="enableActiveEmailValidation" #suffix>Enabled</template> <template v-else #suffix>Disabled</template> - <div class="_formRoot"> - <span class="_formBlock">{{ i18n.ts.activeEmailValidationDescription }}</span> - <FormSwitch v-model="enableActiveEmailValidation" class="_formBlock" @update:model-value="save"> + <div class="_autoGap"> + <span>{{ i18n.ts.activeEmailValidationDescription }}</span> + <FormSwitch v-model="enableActiveEmailValidation" @update:model-value="save"> <template #label>Enable</template> </FormSwitch> </div> </FormFolder> - <FormFolder class="_formBlock"> + <FormFolder> <template #label>Log IP address</template> <template v-if="enableIpLogging" #suffix>Enabled</template> <template v-else #suffix>Disabled</template> - <div class="_formRoot"> - <FormSwitch v-model="enableIpLogging" class="_formBlock" @update:model-value="save"> + <div class="_autoGap"> + <FormSwitch v-model="enableIpLogging" @update:model-value="save"> <template #label>Enable</template> </FormSwitch> </div> </FormFolder> - <FormFolder class="_formBlock"> + <FormFolder> <template #label>Summaly Proxy</template> - <div class="_formRoot"> - <FormInput v-model="summalyProxy" class="_formBlock"> + <div class="_autoGap"> + <FormInput v-model="summalyProxy"> <template #prefix><i class="ti ti-link"></i></template> <template #label>Summaly Proxy URL</template> </FormInput> - <FormButton primary class="_formBlock" @click="save"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</FormButton> + <FormButton primary @click="save"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</FormButton> </div> </FormFolder> </div> diff --git a/packages/frontend/src/pages/admin/settings.vue b/packages/frontend/src/pages/admin/settings.vue index 460eb92694..01c7b51e39 100644 --- a/packages/frontend/src/pages/admin/settings.vue +++ b/packages/frontend/src/pages/admin/settings.vue @@ -4,141 +4,153 @@ <template #header><XHeader :actions="headerActions" :tabs="headerTabs"/></template> <MkSpacer :content-max="700" :margin-min="16" :margin-max="32"> <FormSuspense :p="init"> - <div class="_formRoot"> - <FormInput v-model="name" class="_formBlock"> + <div class="_autoGap"> + <FormInput v-model="name"> <template #label>{{ i18n.ts.instanceName }}</template> </FormInput> - <FormTextarea v-model="description" class="_formBlock"> + <FormTextarea v-model="description"> <template #label>{{ i18n.ts.instanceDescription }}</template> </FormTextarea> - <FormInput v-model="tosUrl" class="_formBlock"> + <FormInput v-model="tosUrl"> <template #prefix><i class="ti ti-link"></i></template> <template #label>{{ i18n.ts.tosUrl }}</template> </FormInput> <FormSplit :min-width="300"> - <FormInput v-model="maintainerName" class="_formBlock"> + <FormInput v-model="maintainerName"> <template #label>{{ i18n.ts.maintainerName }}</template> </FormInput> - <FormInput v-model="maintainerEmail" type="email" class="_formBlock"> + <FormInput v-model="maintainerEmail" type="email"> <template #prefix><i class="ti ti-mail"></i></template> <template #label>{{ i18n.ts.maintainerEmail }}</template> </FormInput> </FormSplit> - <FormTextarea v-model="pinnedUsers" class="_formBlock"> + <FormTextarea v-model="pinnedUsers"> <template #label>{{ i18n.ts.pinnedUsers }}</template> <template #caption>{{ i18n.ts.pinnedUsersDescription }}</template> </FormTextarea> <FormSection> - <FormSwitch v-model="enableRegistration" class="_formBlock"> - <template #label>{{ i18n.ts.enableRegistration }}</template> - </FormSwitch> + <div class="_autoGap_half"> + <FormSwitch v-model="enableRegistration"> + <template #label>{{ i18n.ts.enableRegistration }}</template> + </FormSwitch> - <FormSwitch v-model="emailRequiredForSignup" class="_formBlock"> - <template #label>{{ i18n.ts.emailRequiredForSignup }}</template> - </FormSwitch> + <FormSwitch v-model="emailRequiredForSignup"> + <template #label>{{ i18n.ts.emailRequiredForSignup }}</template> + </FormSwitch> + </div> </FormSection> <FormSection> - <FormSwitch v-model="enableLocalTimeline" class="_formBlock">{{ i18n.ts.enableLocalTimeline }}</FormSwitch> - <FormSwitch v-model="enableGlobalTimeline" class="_formBlock">{{ i18n.ts.enableGlobalTimeline }}</FormSwitch> - <FormInfo class="_formBlock">{{ i18n.ts.disablingTimelinesInfo }}</FormInfo> + <div class="_autoGap_half"> + <FormSwitch v-model="enableLocalTimeline">{{ i18n.ts.enableLocalTimeline }}</FormSwitch> + <FormSwitch v-model="enableGlobalTimeline">{{ i18n.ts.enableGlobalTimeline }}</FormSwitch> + <FormInfo>{{ i18n.ts.disablingTimelinesInfo }}</FormInfo> + </div> </FormSection> <FormSection> <template #label>{{ i18n.ts.theme }}</template> - <FormInput v-model="iconUrl" class="_formBlock"> - <template #prefix><i class="ti ti-link"></i></template> - <template #label>{{ i18n.ts.iconUrl }}</template> - </FormInput> + <div class="_autoGap"> + <FormInput v-model="iconUrl"> + <template #prefix><i class="ti ti-link"></i></template> + <template #label>{{ i18n.ts.iconUrl }}</template> + </FormInput> - <FormInput v-model="bannerUrl" class="_formBlock"> - <template #prefix><i class="ti ti-link"></i></template> - <template #label>{{ i18n.ts.bannerUrl }}</template> - </FormInput> + <FormInput v-model="bannerUrl"> + <template #prefix><i class="ti ti-link"></i></template> + <template #label>{{ i18n.ts.bannerUrl }}</template> + </FormInput> - <FormInput v-model="backgroundImageUrl" class="_formBlock"> - <template #prefix><i class="ti ti-link"></i></template> - <template #label>{{ i18n.ts.backgroundImageUrl }}</template> - </FormInput> + <FormInput v-model="backgroundImageUrl"> + <template #prefix><i class="ti ti-link"></i></template> + <template #label>{{ i18n.ts.backgroundImageUrl }}</template> + </FormInput> - <FormInput v-model="themeColor" class="_formBlock"> - <template #prefix><i class="ti ti-palette"></i></template> - <template #label>{{ i18n.ts.themeColor }}</template> - <template #caption>#RRGGBB</template> - </FormInput> + <FormInput v-model="themeColor"> + <template #prefix><i class="ti ti-palette"></i></template> + <template #label>{{ i18n.ts.themeColor }}</template> + <template #caption>#RRGGBB</template> + </FormInput> - <FormTextarea v-model="defaultLightTheme" class="_formBlock"> - <template #label>{{ i18n.ts.instanceDefaultLightTheme }}</template> - <template #caption>{{ i18n.ts.instanceDefaultThemeDescription }}</template> - </FormTextarea> + <FormTextarea v-model="defaultLightTheme"> + <template #label>{{ i18n.ts.instanceDefaultLightTheme }}</template> + <template #caption>{{ i18n.ts.instanceDefaultThemeDescription }}</template> + </FormTextarea> - <FormTextarea v-model="defaultDarkTheme" class="_formBlock"> - <template #label>{{ i18n.ts.instanceDefaultDarkTheme }}</template> - <template #caption>{{ i18n.ts.instanceDefaultThemeDescription }}</template> - </FormTextarea> + <FormTextarea v-model="defaultDarkTheme"> + <template #label>{{ i18n.ts.instanceDefaultDarkTheme }}</template> + <template #caption>{{ i18n.ts.instanceDefaultThemeDescription }}</template> + </FormTextarea> + </div> </FormSection> <FormSection> <template #label>{{ i18n.ts.files }}</template> - <FormSwitch v-model="cacheRemoteFiles" class="_formBlock"> - <template #label>{{ i18n.ts.cacheRemoteFiles }}</template> - <template #caption>{{ i18n.ts.cacheRemoteFilesDescription }}</template> - </FormSwitch> + <div class="_autoGap"> + <FormSwitch v-model="cacheRemoteFiles"> + <template #label>{{ i18n.ts.cacheRemoteFiles }}</template> + <template #caption>{{ i18n.ts.cacheRemoteFilesDescription }}</template> + </FormSwitch> - <FormSplit :min-width="280"> - <FormInput v-model="localDriveCapacityMb" type="number" class="_formBlock"> - <template #label>{{ i18n.ts.driveCapacityPerLocalAccount }}</template> - <template #suffix>MB</template> - <template #caption>{{ i18n.ts.inMb }}</template> - </FormInput> + <FormSplit :min-width="280"> + <FormInput v-model="localDriveCapacityMb" type="number"> + <template #label>{{ i18n.ts.driveCapacityPerLocalAccount }}</template> + <template #suffix>MB</template> + <template #caption>{{ i18n.ts.inMb }}</template> + </FormInput> - <FormInput v-model="remoteDriveCapacityMb" type="number" :disabled="!cacheRemoteFiles" class="_formBlock"> - <template #label>{{ i18n.ts.driveCapacityPerRemoteAccount }}</template> - <template #suffix>MB</template> - <template #caption>{{ i18n.ts.inMb }}</template> - </FormInput> - </FormSplit> + <FormInput v-model="remoteDriveCapacityMb" type="number" :disabled="!cacheRemoteFiles"> + <template #label>{{ i18n.ts.driveCapacityPerRemoteAccount }}</template> + <template #suffix>MB</template> + <template #caption>{{ i18n.ts.inMb }}</template> + </FormInput> + </FormSplit> + </div> </FormSection> <FormSection> <template #label>ServiceWorker</template> - <FormSwitch v-model="enableServiceWorker" class="_formBlock"> - <template #label>{{ i18n.ts.enableServiceworker }}</template> - <template #caption>{{ i18n.ts.serviceworkerInfo }}</template> - </FormSwitch> + <div class="_autoGap"> + <FormSwitch v-model="enableServiceWorker"> + <template #label>{{ i18n.ts.enableServiceworker }}</template> + <template #caption>{{ i18n.ts.serviceworkerInfo }}</template> + </FormSwitch> - <template v-if="enableServiceWorker"> - <FormInput v-model="swPublicKey" class="_formBlock"> - <template #prefix><i class="ti ti-key"></i></template> - <template #label>Public key</template> - </FormInput> + <template v-if="enableServiceWorker"> + <FormInput v-model="swPublicKey"> + <template #prefix><i class="ti ti-key"></i></template> + <template #label>Public key</template> + </FormInput> - <FormInput v-model="swPrivateKey" class="_formBlock"> - <template #prefix><i class="ti ti-key"></i></template> - <template #label>Private key</template> - </FormInput> - </template> + <FormInput v-model="swPrivateKey"> + <template #prefix><i class="ti ti-key"></i></template> + <template #label>Private key</template> + </FormInput> + </template> + </div> </FormSection> <FormSection> <template #label>DeepL Translation</template> - <FormInput v-model="deeplAuthKey" class="_formBlock"> - <template #prefix><i class="ti ti-key"></i></template> - <template #label>DeepL Auth Key</template> - </FormInput> - <FormSwitch v-model="deeplIsPro" class="_formBlock"> - <template #label>Pro account</template> - </FormSwitch> + <div class="_autoGap"> + <FormInput v-model="deeplAuthKey"> + <template #prefix><i class="ti ti-key"></i></template> + <template #label>DeepL Auth Key</template> + </FormInput> + <FormSwitch v-model="deeplIsPro"> + <template #label>Pro account</template> + </FormSwitch> + </div> </FormSection> </div> </FormSuspense> diff --git a/packages/frontend/src/pages/api-console.vue b/packages/frontend/src/pages/api-console.vue index 1d5339b44c..d62e6aabd1 100644 --- a/packages/frontend/src/pages/api-console.vue +++ b/packages/frontend/src/pages/api-console.vue @@ -2,23 +2,23 @@ <MkStickyContainer> <template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template> <MkSpacer :content-max="700"> - <div class="_formRoot"> - <div class="_formBlock"> - <MkInput v-model="endpoint" :datalist="endpoints" class="_formBlock" @update:model-value="onEndpointChange()"> + <div class="_autoGap"> + <div class="_autoGap"> + <MkInput v-model="endpoint" :datalist="endpoints" @update:model-value="onEndpointChange()"> <template #label>Endpoint</template> </MkInput> - <MkTextarea v-model="body" class="_formBlock" code> + <MkTextarea v-model="body" code> <template #label>Params (JSON or JSON5)</template> </MkTextarea> - <MkSwitch v-model="withCredential" class="_formBlock"> + <MkSwitch v-model="withCredential"> With credential </MkSwitch> - <MkButton class="_formBlock" primary :disabled="sending" @click="send"> + <MkButton primary :disabled="sending" @click="send"> <template v-if="sending"><MkEllipsis/></template> <template v-else><i class="ti ti-send"></i> Send</template> </MkButton> </div> - <div v-if="res" class="_formBlock"> + <div v-if="res"> <MkTextarea v-model="res" code readonly tall> <template #label>Response</template> </MkTextarea> diff --git a/packages/frontend/src/pages/channel-editor.vue b/packages/frontend/src/pages/channel-editor.vue index 5ae7e63f99..091573b876 100644 --- a/packages/frontend/src/pages/channel-editor.vue +++ b/packages/frontend/src/pages/channel-editor.vue @@ -2,12 +2,12 @@ <MkStickyContainer> <template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template> <MkSpacer :content-max="700"> - <div class="_formRoot"> - <MkInput v-model="name" class="_formBlock"> + <div class="_autoGap"> + <MkInput v-model="name"> <template #label>{{ i18n.ts.name }}</template> </MkInput> - <MkTextarea v-model="description" class="_formBlock"> + <MkTextarea v-model="description"> <template #label>{{ i18n.ts.description }}</template> </MkTextarea> @@ -18,7 +18,7 @@ <MkButton @click="removeBannerImage()"><i class="ti ti-trash"></i> {{ i18n.ts._channel.removeBanner }}</MkButton> </div> </div> - <div class="_formBlock"> + <div> <MkButton primary @click="save()"><i class="ti ti-device-floppy"></i> {{ channelId ? i18n.ts.save : i18n.ts.create }}</MkButton> </div> </div> diff --git a/packages/frontend/src/pages/channel.vue b/packages/frontend/src/pages/channel.vue index f271bb270f..96340a36b9 100644 --- a/packages/frontend/src/pages/channel.vue +++ b/packages/frontend/src/pages/channel.vue @@ -3,7 +3,7 @@ <template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template> <MkSpacer :content-max="700"> <div v-if="channel"> - <div class="wpgynlbz _panel _gap" :class="{ hide: !showBanner }"> + <div class="wpgynlbz _panel _margin" :class="{ hide: !showBanner }"> <XChannelFollowButton :channel="channel" :full="true" class="subscribe"/> <button class="_button toggle" @click="() => showBanner = !showBanner"> <template v-if="showBanner"><i class="ti ti-chevron-up"></i></template> @@ -23,9 +23,9 @@ </div> </div> - <XPostForm v-if="$i" :channel="channel" class="post-form _panel _gap" fixed/> + <XPostForm v-if="$i" :channel="channel" class="post-form _panel _margin" fixed/> - <XTimeline :key="channelId" class="_gap" src="channel" :channel="channelId" @before="before" @after="after"/> + <XTimeline :key="channelId" class="_margin" src="channel" :channel="channelId" @before="before" @after="after"/> </div> </MkSpacer> </MkStickyContainer> diff --git a/packages/frontend/src/pages/channels.vue b/packages/frontend/src/pages/channels.vue index 34e9dac196..cc8375f636 100644 --- a/packages/frontend/src/pages/channels.vue +++ b/packages/frontend/src/pages/channels.vue @@ -4,18 +4,18 @@ <MkSpacer :content-max="700"> <div v-if="tab === 'featured'" class="_content grwlizim featured"> <MkPagination v-slot="{items}" :pagination="featuredPagination"> - <MkChannelPreview v-for="channel in items" :key="channel.id" class="_gap" :channel="channel"/> + <MkChannelPreview v-for="channel in items" :key="channel.id" class="_margin" :channel="channel"/> </MkPagination> </div> <div v-else-if="tab === 'following'" class="_content grwlizim following"> <MkPagination v-slot="{items}" :pagination="followingPagination"> - <MkChannelPreview v-for="channel in items" :key="channel.id" class="_gap" :channel="channel"/> + <MkChannelPreview v-for="channel in items" :key="channel.id" class="_margin" :channel="channel"/> </MkPagination> </div> <div v-else-if="tab === 'owned'" class="_content grwlizim owned"> <MkButton class="new" @click="create()"><i class="ti ti-plus"></i></MkButton> <MkPagination v-slot="{items}" :pagination="ownedPagination"> - <MkChannelPreview v-for="channel in items" :key="channel.id" class="_gap" :channel="channel"/> + <MkChannelPreview v-for="channel in items" :key="channel.id" class="_margin" :channel="channel"/> </MkPagination> </div> </MkSpacer> diff --git a/packages/frontend/src/pages/explore.users.vue b/packages/frontend/src/pages/explore.users.vue index 611ca0f003..f1bb0cc62e 100644 --- a/packages/frontend/src/pages/explore.users.vue +++ b/packages/frontend/src/pages/explore.users.vue @@ -6,26 +6,26 @@ </MkTab> <div v-if="origin === 'local'"> <template v-if="tag == null"> - <MkFolder class="_gap" persist-key="explore-pinned-users"> + <MkFolder class="_margin" persist-key="explore-pinned-users"> <template #header><i class="fas fa-bookmark ti-fw" style="margin-right: 0.5em;"></i>{{ i18n.ts.pinnedUsers }}</template> <XUserList :pagination="pinnedUsers"/> </MkFolder> - <MkFolder class="_gap" persist-key="explore-popular-users"> + <MkFolder class="_margin" persist-key="explore-popular-users"> <template #header><i class="fas fa-chart-line ti-fw" style="margin-right: 0.5em;"></i>{{ i18n.ts.popularUsers }}</template> <XUserList :pagination="popularUsers"/> </MkFolder> - <MkFolder class="_gap" persist-key="explore-recently-updated-users"> + <MkFolder class="_margin" persist-key="explore-recently-updated-users"> <template #header><i class="fas fa-comment-alt ti-fw" style="margin-right: 0.5em;"></i>{{ i18n.ts.recentlyUpdatedUsers }}</template> <XUserList :pagination="recentlyUpdatedUsers"/> </MkFolder> - <MkFolder class="_gap" persist-key="explore-recently-registered-users"> + <MkFolder class="_margin" persist-key="explore-recently-registered-users"> <template #header><i class="ti ti-plus ti-fw" style="margin-right: 0.5em;"></i>{{ i18n.ts.recentlyRegisteredUsers }}</template> <XUserList :pagination="recentlyRegisteredUsers"/> </MkFolder> </template> </div> <div v-else> - <MkFolder ref="tagsEl" :foldable="true" :expanded="false" class="_gap"> + <MkFolder ref="tagsEl" :foldable="true" :expanded="false" class="_margin"> <template #header><i class="ti ti-hash ti-fw" style="margin-right: 0.5em;"></i>{{ i18n.ts.popularTags }}</template> <div class="vxjfqztj"> @@ -34,21 +34,21 @@ </div> </MkFolder> - <MkFolder v-if="tag != null" :key="`${tag}`" class="_gap"> + <MkFolder v-if="tag != null" :key="`${tag}`" class="_margin"> <template #header><i class="ti ti-hash ti-fw" style="margin-right: 0.5em;"></i>{{ tag }}</template> <XUserList :pagination="tagUsers"/> </MkFolder> <template v-if="tag == null"> - <MkFolder class="_gap"> + <MkFolder class="_margin"> <template #header><i class="fas fa-chart-line ti-fw" style="margin-right: 0.5em;"></i>{{ i18n.ts.popularUsers }}</template> <XUserList :pagination="popularUsersF"/> </MkFolder> - <MkFolder class="_gap"> + <MkFolder class="_margin"> <template #header><i class="fas fa-comment-alt ti-fw" style="margin-right: 0.5em;"></i>{{ i18n.ts.recentlyUpdatedUsers }}</template> <XUserList :pagination="recentlyUpdatedUsersF"/> </MkFolder> - <MkFolder class="_gap"> + <MkFolder class="_margin"> <template #header><i class="fas fa-rocket ti-fw" style="margin-right: 0.5em;"></i>{{ i18n.ts.recentlyDiscoveredUsers }}</template> <XUserList :pagination="recentlyRegisteredUsersF"/> </MkFolder> diff --git a/packages/frontend/src/pages/explore.vue b/packages/frontend/src/pages/explore.vue index 4494f6154d..cb62af469a 100644 --- a/packages/frontend/src/pages/explore.vue +++ b/packages/frontend/src/pages/explore.vue @@ -11,18 +11,18 @@ <div v-else-if="tab === 'search'"> <MkSpacer :content-max="1200"> <div> - <MkInput v-model="searchQuery" :debounce="true" type="search" class="_formBlock"> + <MkInput v-model="searchQuery" :debounce="true" type="search"> <template #prefix><i class="ti ti-search"></i></template> <template #label>{{ i18n.ts.searchUser }}</template> </MkInput> - <MkRadios v-model="searchOrigin" class="_formBlock"> + <MkRadios v-model="searchOrigin"> <option value="combined">{{ i18n.ts.all }}</option> <option value="local">{{ i18n.ts.local }}</option> <option value="remote">{{ i18n.ts.remote }}</option> </MkRadios> </div> - <XUserList v-if="searchQuery" ref="searchEl" class="_gap" :pagination="searchPagination"/> + <XUserList v-if="searchQuery" ref="searchEl" class="_margin" :pagination="searchPagination"/> </MkSpacer> </div> </div> diff --git a/packages/frontend/src/pages/flash/flash-edit.vue b/packages/frontend/src/pages/flash/flash-edit.vue index b2eba292f2..f890564884 100644 --- a/packages/frontend/src/pages/flash/flash-edit.vue +++ b/packages/frontend/src/pages/flash/flash-edit.vue @@ -2,18 +2,20 @@ <MkStickyContainer> <template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template> <MkSpacer :content-max="700"> - <MkInput v-model="title" class="_formBlock"> - <template #label>{{ i18n.ts._play.title }}</template> - </MkInput> - <MkTextarea v-model="summary" class="_formBlock"> - <template #label>{{ i18n.ts._play.summary }}</template> - </MkTextarea> - <MkTextarea v-model="script" class="_formBlock _monospace" tall spellcheck="false"> - <template #label>{{ i18n.ts._play.script }}</template> - </MkTextarea> - <div style="display: flex; gap: var(--margin); flex-wrap: wrap;"> - <MkButton primary @click="save"><i class="ti ti-check"></i> {{ i18n.ts.save }}</MkButton> - <MkButton @click="show"><i class="ti ti-eye"></i> {{ i18n.ts.show }}</MkButton> + <div class="_autoGap"> + <MkInput v-model="title"> + <template #label>{{ i18n.ts._play.title }}</template> + </MkInput> + <MkTextarea v-model="summary"> + <template #label>{{ i18n.ts._play.summary }}</template> + </MkTextarea> + <MkTextarea v-model="script" class="_monospace" tall spellcheck="false"> + <template #label>{{ i18n.ts._play.script }}</template> + </MkTextarea> + <div style="display: flex; gap: var(--margin); flex-wrap: wrap;"> + <MkButton primary @click="save"><i class="ti ti-check"></i> {{ i18n.ts.save }}</MkButton> + <MkButton @click="show"><i class="ti ti-eye"></i> {{ i18n.ts.show }}</MkButton> + </div> </div> </MkSpacer> </MkStickyContainer> diff --git a/packages/frontend/src/pages/flash/flash.vue b/packages/frontend/src/pages/flash/flash.vue index 9495206c54..caaa192bbe 100644 --- a/packages/frontend/src/pages/flash/flash.vue +++ b/packages/frontend/src/pages/flash/flash.vue @@ -27,7 +27,7 @@ </div> </div> </Transition> - <FormFolder class="_formBlock"> + <FormFolder class="_margin"> <template #icon><i class="ti ti-code"></i></template> <template #label>{{ i18n.ts._play.viewSource }}</template> diff --git a/packages/frontend/src/pages/gallery/index.vue b/packages/frontend/src/pages/gallery/index.vue index 24a634bab5..b29a6a5310 100644 --- a/packages/frontend/src/pages/gallery/index.vue +++ b/packages/frontend/src/pages/gallery/index.vue @@ -4,7 +4,7 @@ <MkSpacer :content-max="1400"> <div class="_root"> <div v-if="tab === 'explore'"> - <MkFolder class="_gap"> + <MkFolder class="_margin"> <template #header><i class="ti ti-clock"></i>{{ i18n.ts.recentPosts }}</template> <MkPagination v-slot="{items}" :pagination="recentPostsPagination" :disable-auto-load="true"> <div class="vfpdbgtk"> @@ -12,7 +12,7 @@ </div> </MkPagination> </MkFolder> - <MkFolder class="_gap"> + <MkFolder class="_margin"> <template #header><i class="ti ti-comet"></i>{{ i18n.ts.popularPosts }}</template> <MkPagination v-slot="{items}" :pagination="popularPostsPagination" :disable-auto-load="true"> <div class="vfpdbgtk"> diff --git a/packages/frontend/src/pages/instance-info.vue b/packages/frontend/src/pages/instance-info.vue index 55771b0e30..5fc341198e 100644 --- a/packages/frontend/src/pages/instance-info.vue +++ b/packages/frontend/src/pages/instance-info.vue @@ -2,23 +2,25 @@ <MkStickyContainer> <template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template> <MkSpacer v-if="instance" :content-max="600" :margin-min="16" :margin-max="32"> - <div v-if="tab === 'overview'" class="_formRoot"> + <div v-if="tab === 'overview'" class="_autoGap"> <div class="fnfelxur"> <img :src="faviconUrl" alt="" class="icon"/> <span class="name">{{ instance.name || `(${i18n.ts.unknown})` }}</span> </div> - <MkKeyValue :copy="host" oneline style="margin: 1em 0;"> - <template #key>Host</template> - <template #value><span class="_monospace"><MkLink :url="`https://${host}`">{{ host }}</MkLink></span></template> - </MkKeyValue> - <MkKeyValue oneline style="margin: 1em 0;"> - <template #key>{{ i18n.ts.software }}</template> - <template #value><span class="_monospace">{{ instance.softwareName || `(${i18n.ts.unknown})` }} / {{ instance.softwareVersion || `(${i18n.ts.unknown})` }}</span></template> - </MkKeyValue> - <MkKeyValue oneline style="margin: 1em 0;"> - <template #key>{{ i18n.ts.administrator }}</template> - <template #value>{{ instance.maintainerName || `(${i18n.ts.unknown})` }} ({{ instance.maintainerEmail || `(${i18n.ts.unknown})` }})</template> - </MkKeyValue> + <div style="display: flex; flex-direction: column; gap: 1em;"> + <MkKeyValue :copy="host" oneline> + <template #key>Host</template> + <template #value><span class="_monospace"><MkLink :url="`https://${host}`">{{ host }}</MkLink></span></template> + </MkKeyValue> + <MkKeyValue oneline> + <template #key>{{ i18n.ts.software }}</template> + <template #value><span class="_monospace">{{ instance.softwareName || `(${i18n.ts.unknown})` }} / {{ instance.softwareVersion || `(${i18n.ts.unknown})` }}</span></template> + </MkKeyValue> + <MkKeyValue oneline> + <template #key>{{ i18n.ts.administrator }}</template> + <template #value>{{ instance.maintainerName || `(${i18n.ts.unknown})` }} ({{ instance.maintainerEmail || `(${i18n.ts.unknown})` }})</template> + </MkKeyValue> + </div> <MkKeyValue> <template #key>{{ i18n.ts.description }}</template> <template #value>{{ instance.description }}</template> @@ -26,9 +28,11 @@ <FormSection v-if="iAmModerator"> <template #label>Moderation</template> - <FormSwitch v-model="suspended" class="_formBlock" @update:model-value="toggleSuspend">{{ i18n.ts.stopActivityDelivery }}</FormSwitch> - <FormSwitch v-model="isBlocked" class="_formBlock" @update:model-value="toggleBlock">{{ i18n.ts.blockThisInstance }}</FormSwitch> - <MkButton @click="refreshMetadata"><i class="ti ti-refresh"></i> Refresh metadata</MkButton> + <div class="_autoGap_half"> + <FormSwitch v-model="suspended" @update:model-value="toggleSuspend">{{ i18n.ts.stopActivityDelivery }}</FormSwitch> + <FormSwitch v-model="isBlocked" @update:model-value="toggleBlock">{{ i18n.ts.blockThisInstance }}</FormSwitch> + <MkButton @click="refreshMetadata"><i class="ti ti-refresh"></i> Refresh metadata</MkButton> + </div> </FormSection> <FormSection> @@ -66,7 +70,7 @@ <FormLink :to="`https://${host}/manifest.json`" external style="margin-bottom: 8px;">manifest.json</FormLink> </FormSection> </div> - <div v-else-if="tab === 'chart'" class="_formRoot"> + <div v-else-if="tab === 'chart'" class="_autoGap"> <div class="cmhjzshl"> <div class="selects"> <MkSelect v-model="chartSrc" style="margin: 0 10px 0 0; flex: 1;"> @@ -91,14 +95,14 @@ </div> </div> </div> - <div v-else-if="tab === 'users'" class="_formRoot"> + <div v-else-if="tab === 'users'" class="_autoGap"> <MkPagination v-slot="{items}" :pagination="usersPagination" style="display: grid; grid-template-columns: repeat(auto-fill,minmax(270px,1fr)); grid-gap: 12px;"> <MkA v-for="user in items" :key="user.id" v-tooltip.mfm="`Last posted: ${dateString(user.updatedAt)}`" class="user" :to="`/user-info/${user.id}`"> <MkUserCardMini :user="user"/> </MkA> </MkPagination> </div> - <div v-else-if="tab === 'raw'" class="_formRoot"> + <div v-else-if="tab === 'raw'" class="_autoGap"> <MkObjectView tall :value="instance"> </MkObjectView> </div> diff --git a/packages/frontend/src/pages/my-antennas/editor.vue b/packages/frontend/src/pages/my-antennas/editor.vue index a409a734b5..ccbc6d1551 100644 --- a/packages/frontend/src/pages/my-antennas/editor.vue +++ b/packages/frontend/src/pages/my-antennas/editor.vue @@ -1,47 +1,49 @@ <template> -<div class="shaynizk"> - <div class="form"> - <MkInput v-model="name" class="_formBlock"> - <template #label>{{ i18n.ts.name }}</template> - </MkInput> - <MkSelect v-model="src" class="_formBlock"> - <template #label>{{ i18n.ts.antennaSource }}</template> - <option value="all">{{ i18n.ts._antennaSources.all }}</option> - <!--<option value="home">{{ i18n.ts._antennaSources.homeTimeline }}</option>--> - <option value="users">{{ i18n.ts._antennaSources.users }}</option> - <!--<option value="list">{{ i18n.ts._antennaSources.userList }}</option>--> - <!--<option value="group">{{ i18n.ts._antennaSources.userGroup }}</option>--> - </MkSelect> - <MkSelect v-if="src === 'list'" v-model="userListId" class="_formBlock"> - <template #label>{{ i18n.ts.userList }}</template> - <option v-for="list in userLists" :key="list.id" :value="list.id">{{ list.name }}</option> - </MkSelect> - <MkSelect v-else-if="src === 'group'" v-model="userGroupId" class="_formBlock"> - <template #label>{{ i18n.ts.userGroup }}</template> - <option v-for="group in userGroups" :key="group.id" :value="group.id">{{ group.name }}</option> - </MkSelect> - <MkTextarea v-else-if="src === 'users'" v-model="users" class="_formBlock"> - <template #label>{{ i18n.ts.users }}</template> - <template #caption>{{ i18n.ts.antennaUsersDescription }} <button class="_textButton" @click="addUser">{{ i18n.ts.addUser }}</button></template> - </MkTextarea> - <MkSwitch v-model="withReplies" class="_formBlock">{{ i18n.ts.withReplies }}</MkSwitch> - <MkTextarea v-model="keywords" class="_formBlock"> - <template #label>{{ i18n.ts.antennaKeywords }}</template> - <template #caption>{{ i18n.ts.antennaKeywordsDescription }}</template> - </MkTextarea> - <MkTextarea v-model="excludeKeywords" class="_formBlock"> - <template #label>{{ i18n.ts.antennaExcludeKeywords }}</template> - <template #caption>{{ i18n.ts.antennaKeywordsDescription }}</template> - </MkTextarea> - <MkSwitch v-model="caseSensitive" class="_formBlock">{{ i18n.ts.caseSensitive }}</MkSwitch> - <MkSwitch v-model="withFile" class="_formBlock">{{ i18n.ts.withFileAntenna }}</MkSwitch> - <MkSwitch v-model="notify" class="_formBlock">{{ i18n.ts.notifyAntenna }}</MkSwitch> +<MkSpacer :content-max="700"> + <div class="shaynizk"> + <div class="_autoGap"> + <MkInput v-model="name"> + <template #label>{{ i18n.ts.name }}</template> + </MkInput> + <MkSelect v-model="src"> + <template #label>{{ i18n.ts.antennaSource }}</template> + <option value="all">{{ i18n.ts._antennaSources.all }}</option> + <!--<option value="home">{{ i18n.ts._antennaSources.homeTimeline }}</option>--> + <option value="users">{{ i18n.ts._antennaSources.users }}</option> + <!--<option value="list">{{ i18n.ts._antennaSources.userList }}</option>--> + <!--<option value="group">{{ i18n.ts._antennaSources.userGroup }}</option>--> + </MkSelect> + <MkSelect v-if="src === 'list'" v-model="userListId"> + <template #label>{{ i18n.ts.userList }}</template> + <option v-for="list in userLists" :key="list.id" :value="list.id">{{ list.name }}</option> + </MkSelect> + <MkSelect v-else-if="src === 'group'" v-model="userGroupId"> + <template #label>{{ i18n.ts.userGroup }}</template> + <option v-for="group in userGroups" :key="group.id" :value="group.id">{{ group.name }}</option> + </MkSelect> + <MkTextarea v-else-if="src === 'users'" v-model="users"> + <template #label>{{ i18n.ts.users }}</template> + <template #caption>{{ i18n.ts.antennaUsersDescription }} <button class="_textButton" @click="addUser">{{ i18n.ts.addUser }}</button></template> + </MkTextarea> + <MkSwitch v-model="withReplies">{{ i18n.ts.withReplies }}</MkSwitch> + <MkTextarea v-model="keywords"> + <template #label>{{ i18n.ts.antennaKeywords }}</template> + <template #caption>{{ i18n.ts.antennaKeywordsDescription }}</template> + </MkTextarea> + <MkTextarea v-model="excludeKeywords"> + <template #label>{{ i18n.ts.antennaExcludeKeywords }}</template> + <template #caption>{{ i18n.ts.antennaKeywordsDescription }}</template> + </MkTextarea> + <MkSwitch v-model="caseSensitive">{{ i18n.ts.caseSensitive }}</MkSwitch> + <MkSwitch v-model="withFile">{{ i18n.ts.withFileAntenna }}</MkSwitch> + <MkSwitch v-model="notify">{{ i18n.ts.notifyAntenna }}</MkSwitch> + </div> + <div class="actions"> + <MkButton inline primary @click="saveAntenna()"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</MkButton> + <MkButton v-if="antenna.id != null" inline danger @click="deleteAntenna()"><i class="ti ti-trash"></i> {{ i18n.ts.delete }}</MkButton> + </div> </div> - <div class="actions"> - <MkButton inline primary @click="saveAntenna()"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</MkButton> - <MkButton v-if="antenna.id != null" inline danger @click="deleteAntenna()"><i class="ti ti-trash"></i> {{ i18n.ts.delete }}</MkButton> - </div> -</div> +</MkSpacer> </template> <script lang="ts" setup> @@ -143,12 +145,9 @@ function addUser() { <style lang="scss" scoped> .shaynizk { - > .form { - padding: 32px; - } - > .actions { - padding: 24px 32px; + margin-top: 16px; + padding: 24px 0; border-top: solid 0.5px var(--divider); } } diff --git a/packages/frontend/src/pages/my-clips/index.vue b/packages/frontend/src/pages/my-clips/index.vue index 6c0508134f..a79601f32f 100644 --- a/packages/frontend/src/pages/my-clips/index.vue +++ b/packages/frontend/src/pages/my-clips/index.vue @@ -6,7 +6,7 @@ <MkButton primary class="add" @click="create"><i class="ti ti-plus"></i> {{ i18n.ts.add }}</MkButton> <MkPagination v-slot="{items}" ref="pagingComponent" :pagination="pagination" class="list"> - <MkA v-for="item in items" :key="item.id" :to="`/clips/${item.id}`" class="item _panel _gap"> + <MkA v-for="item in items" :key="item.id" :to="`/clips/${item.id}`" class="item _panel _margin"> <b>{{ item.name }}</b> <div v-if="item.description" class="description">{{ item.description }}</div> </MkA> diff --git a/packages/frontend/src/pages/my-lists/list.vue b/packages/frontend/src/pages/my-lists/list.vue index 714a8d4458..48dde790e9 100644 --- a/packages/frontend/src/pages/my-lists/list.vue +++ b/packages/frontend/src/pages/my-lists/list.vue @@ -14,7 +14,7 @@ </Transition> <Transition :name="$store.state.animation ? '_transition_zoom' : ''" mode="out-in"> - <div v-if="list" class="_section members _gap"> + <div v-if="list" class="_section members _margin"> <div class="_title">{{ i18n.ts.members }}</div> <div class="_content"> <div class="users"> diff --git a/packages/frontend/src/pages/note.vue b/packages/frontend/src/pages/note.vue index 3019b6eb4f..64032ac7f9 100644 --- a/packages/frontend/src/pages/note.vue +++ b/packages/frontend/src/pages/note.vue @@ -5,19 +5,19 @@ <div class="fcuexfpr"> <Transition :name="$store.state.animation ? 'fade' : ''" mode="out-in"> <div v-if="note" class="note"> - <div v-if="showNext" class="_gap"> + <div v-if="showNext" class="_margin"> <XNotes class="_content" :pagination="nextPagination" :no-gap="true"/> </div> - <div class="main _gap"> + <div class="main _margin"> <MkButton v-if="!showNext && hasNext" class="load next" @click="showNext = true"><i class="ti ti-chevron-up"></i></MkButton> - <div class="note _gap"> + <div class="note _margin"> <MkRemoteCaution v-if="note.user.host != null" :href="note.url ?? note.uri"/> <XNoteDetailed :key="note.id" v-model:note="note" class="note"/> </div> - <div v-if="clips && clips.length > 0" class="_content clips _gap"> + <div v-if="clips && clips.length > 0" class="_content clips _margin"> <div class="title">{{ i18n.ts.clip }}</div> - <MkA v-for="item in clips" :key="item.id" :to="`/clips/${item.id}`" class="item _panel _gap"> + <MkA v-for="item in clips" :key="item.id" :to="`/clips/${item.id}`" class="item _panel _margin"> <b>{{ item.name }}</b> <div v-if="item.description" class="description">{{ item.description }}</div> <div class="user"> @@ -28,7 +28,7 @@ <MkButton v-if="!showPrev && hasPrev" class="load prev" @click="showPrev = true"><i class="ti ti-chevron-down"></i></MkButton> </div> - <div v-if="showPrev" class="_gap"> + <div v-if="showPrev" class="_margin"> <XNotes class="_content" :pagination="prevPagination" :no-gap="true"/> </div> </div> diff --git a/packages/frontend/src/pages/page-editor/page-editor.vue b/packages/frontend/src/pages/page-editor/page-editor.vue index 968aa12de2..ef48a2033d 100644 --- a/packages/frontend/src/pages/page-editor/page-editor.vue +++ b/packages/frontend/src/pages/page-editor/page-editor.vue @@ -10,29 +10,29 @@ </div> <div v-if="tab === 'settings'"> - <div class="_formRoot"> - <MkInput v-model="title" class="_formBlock"> + <div class="_autoGap"> + <MkInput v-model="title"> <template #label>{{ $ts._pages.title }}</template> </MkInput> - <MkInput v-model="summary" class="_formBlock"> + <MkInput v-model="summary"> <template #label>{{ $ts._pages.summary }}</template> </MkInput> - <MkInput v-model="name" class="_formBlock"> + <MkInput v-model="name"> <template #prefix>{{ url }}/@{{ author.username }}/pages/</template> <template #label>{{ $ts._pages.url }}</template> </MkInput> - <MkSwitch v-model="alignCenter" class="_formBlock">{{ $ts._pages.alignCenter }}</MkSwitch> + <MkSwitch v-model="alignCenter">{{ $ts._pages.alignCenter }}</MkSwitch> - <MkSelect v-model="font" class="_formBlock"> + <MkSelect v-model="font"> <template #label>{{ $ts._pages.font }}</template> <option value="serif">{{ $ts._pages.fontSerif }}</option> <option value="sans-serif">{{ $ts._pages.fontSansSerif }}</option> </MkSelect> - <MkSwitch v-model="hideTitleWhenPinned" class="_formBlock">{{ $ts._pages.hideTitleWhenPinned }}</MkSwitch> + <MkSwitch v-model="hideTitleWhenPinned">{{ $ts._pages.hideTitleWhenPinned }}</MkSwitch> <div class="eyeCatch"> <MkButton v-if="eyeCatchingImageId == null && !readonly" @click="setEyeCatchingImage"><i class="ti ti-plus"></i> {{ $ts._pages.eyeCatchingImageSet }}</MkButton> diff --git a/packages/frontend/src/pages/page.vue b/packages/frontend/src/pages/page.vue index 11ed8f9f44..07e297f5d7 100644 --- a/packages/frontend/src/pages/page.vue +++ b/packages/frontend/src/pages/page.vue @@ -51,7 +51,7 @@ <MkContainer :max-height="300" :foldable="true" class="other"> <template #header><i class="ti ti-clock"></i> {{ i18n.ts.recentPosts }}</template> <MkPagination v-slot="{items}" :pagination="otherPostsPagination"> - <MkPagePreview v-for="page in items" :key="page.id" :page="page" class="_gap"/> + <MkPagePreview v-for="page in items" :key="page.id" :page="page" class="_margin"/> </MkPagination> </MkContainer> </div> diff --git a/packages/frontend/src/pages/registry.keys.vue b/packages/frontend/src/pages/registry.keys.vue index f179fbe957..1689380650 100644 --- a/packages/frontend/src/pages/registry.keys.vue +++ b/packages/frontend/src/pages/registry.keys.vue @@ -2,25 +2,27 @@ <MkStickyContainer> <template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template> <MkSpacer :content-max="600" :margin-min="16"> - <FormSplit> - <MkKeyValue class="_formBlock"> - <template #key>{{ i18n.ts._registry.domain }}</template> - <template #value>{{ i18n.ts.system }}</template> - </MkKeyValue> - <MkKeyValue class="_formBlock"> - <template #key>{{ i18n.ts._registry.scope }}</template> - <template #value>{{ scope.join('/') }}</template> - </MkKeyValue> - </FormSplit> - - <MkButton primary @click="createKey">{{ i18n.ts._registry.createKey }}</MkButton> + <div class="_autoGap"> + <FormSplit> + <MkKeyValue> + <template #key>{{ i18n.ts._registry.domain }}</template> + <template #value>{{ i18n.ts.system }}</template> + </MkKeyValue> + <MkKeyValue> + <template #key>{{ i18n.ts._registry.scope }}</template> + <template #value>{{ scope.join('/') }}</template> + </MkKeyValue> + </FormSplit> + + <MkButton primary @click="createKey">{{ i18n.ts._registry.createKey }}</MkButton> - <FormSection v-if="keys"> - <template #label>{{ i18n.ts.keys }}</template> - <div class="_formLinks"> - <FormLink v-for="key in keys" :to="`/registry/value/system/${scope.join('/')}/${key[0]}`" class="_monospace">{{ key[0] }}<template #suffix>{{ key[1].toUpperCase() }}</template></FormLink> - </div> - </FormSection> + <FormSection v-if="keys"> + <template #label>{{ i18n.ts.keys }}</template> + <div class="_formLinks"> + <FormLink v-for="key in keys" :to="`/registry/value/system/${scope.join('/')}/${key[0]}`" class="_monospace">{{ key[0] }}<template #suffix>{{ key[1].toUpperCase() }}</template></FormLink> + </div> + </FormSection> + </div> </MkSpacer> </MkStickyContainer> </template> diff --git a/packages/frontend/src/pages/registry.value.vue b/packages/frontend/src/pages/registry.value.vue index 378420b1ba..537dd596a8 100644 --- a/packages/frontend/src/pages/registry.value.vue +++ b/packages/frontend/src/pages/registry.value.vue @@ -2,37 +2,39 @@ <MkStickyContainer> <template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template> <MkSpacer :content-max="600" :margin-min="16"> - <FormInfo warn>{{ i18n.ts.editTheseSettingsMayBreakAccount }}</FormInfo> + <div class="_autoGap"> + <FormInfo warn>{{ i18n.ts.editTheseSettingsMayBreakAccount }}</FormInfo> - <template v-if="value"> - <FormSplit> - <MkKeyValue class="_formBlock"> - <template #key>{{ i18n.ts._registry.domain }}</template> - <template #value>{{ i18n.ts.system }}</template> + <template v-if="value"> + <FormSplit> + <MkKeyValue> + <template #key>{{ i18n.ts._registry.domain }}</template> + <template #value>{{ i18n.ts.system }}</template> + </MkKeyValue> + <MkKeyValue> + <template #key>{{ i18n.ts._registry.scope }}</template> + <template #value>{{ scope.join('/') }}</template> + </MkKeyValue> + <MkKeyValue> + <template #key>{{ i18n.ts._registry.key }}</template> + <template #value>{{ key }}</template> + </MkKeyValue> + </FormSplit> + + <FormTextarea v-model="valueForEditor" tall class="_monospace"> + <template #label>{{ i18n.ts.value }} (JSON)</template> + </FormTextarea> + + <MkButton primary @click="save"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</MkButton> + + <MkKeyValue> + <template #key>{{ i18n.ts.updatedAt }}</template> + <template #value><MkTime :time="value.updatedAt" mode="detail"/></template> </MkKeyValue> - <MkKeyValue class="_formBlock"> - <template #key>{{ i18n.ts._registry.scope }}</template> - <template #value>{{ scope.join('/') }}</template> - </MkKeyValue> - <MkKeyValue class="_formBlock"> - <template #key>{{ i18n.ts._registry.key }}</template> - <template #value>{{ key }}</template> - </MkKeyValue> - </FormSplit> - - <FormTextarea v-model="valueForEditor" tall class="_formBlock _monospace"> - <template #label>{{ i18n.ts.value }} (JSON)</template> - </FormTextarea> - <MkButton class="_formBlock" primary @click="save"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</MkButton> - - <MkKeyValue class="_formBlock"> - <template #key>{{ i18n.ts.updatedAt }}</template> - <template #value><MkTime :time="value.updatedAt" mode="detail"/></template> - </MkKeyValue> - - <MkButton danger @click="del"><i class="ti ti-trash"></i> {{ i18n.ts.delete }}</MkButton> - </template> + <MkButton danger @click="del"><i class="ti ti-trash"></i> {{ i18n.ts.delete }}</MkButton> + </template> + </div> </MkSpacer> </MkStickyContainer> </template> diff --git a/packages/frontend/src/pages/reset-password.vue b/packages/frontend/src/pages/reset-password.vue index 8ec15f6425..eb79733169 100644 --- a/packages/frontend/src/pages/reset-password.vue +++ b/packages/frontend/src/pages/reset-password.vue @@ -2,13 +2,13 @@ <MkStickyContainer> <template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template> <MkSpacer v-if="token" :content-max="700" :margin-min="16" :margin-max="32"> - <div class="_formRoot"> - <FormInput v-model="password" type="password" class="_formBlock"> + <div class="_autoGap"> + <FormInput v-model="password" type="password"> <template #prefix><i class="ti ti-lock"></i></template> <template #label>{{ i18n.ts.newPassword }}</template> </FormInput> - <FormButton primary class="_formBlock" @click="save">{{ i18n.ts.save }}</FormButton> + <FormButton primary @click="save">{{ i18n.ts.save }}</FormButton> </div> </MkSpacer> </MkStickyContainer> diff --git a/packages/frontend/src/pages/settings/account-info.vue b/packages/frontend/src/pages/settings/account-info.vue index ccd99c162a..8f04d0cbe4 100644 --- a/packages/frontend/src/pages/settings/account-info.vue +++ b/packages/frontend/src/pages/settings/account-info.vue @@ -1,5 +1,5 @@ <template> -<div class="_formRoot"> +<div class="_autoGap"> <MkKeyValue> <template #key>ID</template> <template #value><span class="_monospace">{{ $i.id }}</span></template> diff --git a/packages/frontend/src/pages/settings/accounts.vue b/packages/frontend/src/pages/settings/accounts.vue index 493d3b2618..fc5972660d 100644 --- a/packages/frontend/src/pages/settings/accounts.vue +++ b/packages/frontend/src/pages/settings/accounts.vue @@ -1,5 +1,5 @@ <template> -<div class="_formRoot"> +<div class="_autoGap"> <FormSuspense :p="init"> <FormButton primary @click="addAccount"><i class="ti ti-plus"></i> {{ i18n.ts.addAccount }}</FormButton> diff --git a/packages/frontend/src/pages/settings/api.vue b/packages/frontend/src/pages/settings/api.vue index 8d7291cd10..919b45d3e6 100644 --- a/packages/frontend/src/pages/settings/api.vue +++ b/packages/frontend/src/pages/settings/api.vue @@ -1,8 +1,8 @@ <template> -<div class="_formRoot"> - <FormButton primary class="_formBlock" @click="generateToken">{{ i18n.ts.generateAccessToken }}</FormButton> - <FormLink to="/settings/apps" class="_formBlock">{{ i18n.ts.manageAccessTokens }}</FormLink> - <FormLink to="/api-console" :behavior="isDesktop ? 'window' : null" class="_formBlock">API console</FormLink> +<div class="_autoGap"> + <FormButton primary @click="generateToken">{{ i18n.ts.generateAccessToken }}</FormButton> + <FormLink to="/settings/apps">{{ i18n.ts.manageAccessTokens }}</FormLink> + <FormLink to="/api-console" :behavior="isDesktop ? 'window' : null">API console</FormLink> </div> </template> diff --git a/packages/frontend/src/pages/settings/apps.vue b/packages/frontend/src/pages/settings/apps.vue index fe237de906..43ccdb1a97 100644 --- a/packages/frontend/src/pages/settings/apps.vue +++ b/packages/frontend/src/pages/settings/apps.vue @@ -1,5 +1,5 @@ <template> -<div class="_formRoot"> +<div class="_autoGap"> <FormPagination ref="list" :pagination="pagination"> <template #empty> <div class="_fullinfo"> @@ -8,20 +8,20 @@ </div> </template> <template #default="{items}"> - <div v-for="token in items" :key="token.id" class="_panel _formBlock bfomjevm"> + <div v-for="token in items" :key="token.id" class="_panel bfomjevm"> <img v-if="token.iconUrl" class="icon" :src="token.iconUrl" alt=""/> <div class="body"> <div class="name">{{ token.name }}</div> <div class="description">{{ token.description }}</div> - <MkKeyValue class="_formBlock" oneline> + <MkKeyValue oneline> <template #key>{{ i18n.ts.installedDate }}</template> <template #value><MkTime :time="token.createdAt"/></template> </MkKeyValue> - <MkKeyValue class="_formBlock" oneline> + <MkKeyValue oneline> <template #key>{{ i18n.ts.lastUsedDate }}</template> <template #value><MkTime :time="token.lastUsedAt"/></template> </MkKeyValue> - <details class="_formBlock"> + <details> <summary>{{ i18n.ts.details }}</summary> <ul> <li v-for="p in token.permission" :key="p">{{ $t(`_permissions.${p}`) }}</li> diff --git a/packages/frontend/src/pages/settings/custom-css.vue b/packages/frontend/src/pages/settings/custom-css.vue index 2caad22b7b..4d054fd2c7 100644 --- a/packages/frontend/src/pages/settings/custom-css.vue +++ b/packages/frontend/src/pages/settings/custom-css.vue @@ -1,8 +1,8 @@ <template> -<div class="_formRoot"> - <FormInfo warn class="_formBlock">{{ i18n.ts.customCssWarn }}</FormInfo> +<div class="_autoGap"> + <FormInfo warn>{{ i18n.ts.customCssWarn }}</FormInfo> - <FormTextarea v-model="localCustomCss" manual-save tall class="_monospace _formBlock" style="tab-size: 2;"> + <FormTextarea v-model="localCustomCss" manual-save tall class="_monospace" style="tab-size: 2;"> <template #label>CSS</template> </FormTextarea> </div> diff --git a/packages/frontend/src/pages/settings/deck.vue b/packages/frontend/src/pages/settings/deck.vue index 82cefe05d5..5506f61509 100644 --- a/packages/frontend/src/pages/settings/deck.vue +++ b/packages/frontend/src/pages/settings/deck.vue @@ -1,10 +1,10 @@ <template> -<div class="_formRoot"> +<div class="_autoGap"> <FormSwitch v-model="navWindow">{{ i18n.ts.defaultNavigationBehaviour }}: {{ i18n.ts.openInWindow }}</FormSwitch> - <FormSwitch v-model="alwaysShowMainColumn" class="_formBlock">{{ i18n.ts._deck.alwaysShowMainColumn }}</FormSwitch> + <FormSwitch v-model="alwaysShowMainColumn">{{ i18n.ts._deck.alwaysShowMainColumn }}</FormSwitch> - <FormRadios v-model="columnAlign" class="_formBlock"> + <FormRadios v-model="columnAlign"> <template #label>{{ i18n.ts._deck.columnAlign }}</template> <option value="left">{{ i18n.ts.left }}</option> <option value="center">{{ i18n.ts.center }}</option> diff --git a/packages/frontend/src/pages/settings/delete-account.vue b/packages/frontend/src/pages/settings/delete-account.vue index 8a25ff39f0..c74b0f2b18 100644 --- a/packages/frontend/src/pages/settings/delete-account.vue +++ b/packages/frontend/src/pages/settings/delete-account.vue @@ -1,8 +1,8 @@ <template> -<div class="_formRoot"> - <FormInfo warn class="_formBlock">{{ i18n.ts._accountDelete.mayTakeTime }}</FormInfo> - <FormInfo class="_formBlock">{{ i18n.ts._accountDelete.sendEmail }}</FormInfo> - <FormButton v-if="!$i.isDeleted" danger class="_formBlock" @click="deleteAccount">{{ i18n.ts._accountDelete.requestAccountDelete }}</FormButton> +<div class="_autoGap"> + <FormInfo warn>{{ i18n.ts._accountDelete.mayTakeTime }}</FormInfo> + <FormInfo>{{ i18n.ts._accountDelete.sendEmail }}</FormInfo> + <FormButton v-if="!$i.isDeleted" danger @click="deleteAccount">{{ i18n.ts._accountDelete.requestAccountDelete }}</FormButton> <FormButton v-else disabled>{{ i18n.ts._accountDelete.inProgress }}</FormButton> </div> </template> diff --git a/packages/frontend/src/pages/settings/drive.vue b/packages/frontend/src/pages/settings/drive.vue index 2d45b1add8..bbf4da02e2 100644 --- a/packages/frontend/src/pages/settings/drive.vue +++ b/packages/frontend/src/pages/settings/drive.vue @@ -1,20 +1,23 @@ <template> -<div class="_formRoot"> - <FormSection v-if="!fetching"> +<div class="_autoGap"> + <FormSection v-if="!fetching" first> <template #label>{{ i18n.ts.usageAmount }}</template> - <div class="_formBlock uawsfosz"> - <div class="meter"><div :style="meterStyle"></div></div> + + <div class="_autoGap"> + <div class="uawsfosz"> + <div class="meter"><div :style="meterStyle"></div></div> + </div> + <FormSplit> + <MkKeyValue> + <template #key>{{ i18n.ts.capacity }}</template> + <template #value>{{ bytes(capacity, 1) }}</template> + </MkKeyValue> + <MkKeyValue> + <template #key>{{ i18n.ts.inUse }}</template> + <template #value>{{ bytes(usage, 1) }}</template> + </MkKeyValue> + </FormSplit> </div> - <FormSplit> - <MkKeyValue class="_formBlock"> - <template #key>{{ i18n.ts.capacity }}</template> - <template #value>{{ bytes(capacity, 1) }}</template> - </MkKeyValue> - <MkKeyValue class="_formBlock"> - <template #key>{{ i18n.ts.inUse }}</template> - <template #value>{{ bytes(usage, 1) }}</template> - </MkKeyValue> - </FormSplit> </FormSection> <FormSection> @@ -23,22 +26,24 @@ </FormSection> <FormSection> - <FormLink @click="chooseUploadFolder()"> - {{ i18n.ts.uploadFolder }} - <template #suffix>{{ uploadFolder ? uploadFolder.name : '-' }}</template> - <template #suffixIcon><i class="fas fa-folder-open"></i></template> - </FormLink> - <FormSwitch v-model="keepOriginalUploading" class="_formBlock"> - <template #label>{{ i18n.ts.keepOriginalUploading }}</template> - <template #caption>{{ i18n.ts.keepOriginalUploadingDescription }}</template> - </FormSwitch> - <FormSwitch v-model="alwaysMarkNsfw" class="_formBlock" @update:model-value="saveProfile()"> - <template #label>{{ i18n.ts.alwaysMarkSensitive }}</template> - </FormSwitch> - <FormSwitch v-model="autoSensitive" class="_formBlock" @update:model-value="saveProfile()"> - <template #label>{{ i18n.ts.enableAutoSensitive }}<span class="_beta">{{ i18n.ts.beta }}</span></template> - <template #caption>{{ i18n.ts.enableAutoSensitiveDescription }}</template> - </FormSwitch> + <div class="_autoGap"> + <FormLink @click="chooseUploadFolder()"> + {{ i18n.ts.uploadFolder }} + <template #suffix>{{ uploadFolder ? uploadFolder.name : '-' }}</template> + <template #suffixIcon><i class="fas fa-folder-open"></i></template> + </FormLink> + <FormSwitch v-model="keepOriginalUploading"> + <template #label>{{ i18n.ts.keepOriginalUploading }}</template> + <template #caption>{{ i18n.ts.keepOriginalUploadingDescription }}</template> + </FormSwitch> + <FormSwitch v-model="alwaysMarkNsfw" @update:model-value="saveProfile()"> + <template #label>{{ i18n.ts.alwaysMarkSensitive }}</template> + </FormSwitch> + <FormSwitch v-model="autoSensitive" @update:model-value="saveProfile()"> + <template #label>{{ i18n.ts.enableAutoSensitive }}<span class="_beta">{{ i18n.ts.beta }}</span></template> + <template #caption>{{ i18n.ts.enableAutoSensitiveDescription }}</template> + </FormSwitch> + </div> </FormSection> </div> </template> diff --git a/packages/frontend/src/pages/settings/email.vue b/packages/frontend/src/pages/settings/email.vue index 3fff8c6b1d..6b4b3a70c4 100644 --- a/packages/frontend/src/pages/settings/email.vue +++ b/packages/frontend/src/pages/settings/email.vue @@ -1,5 +1,5 @@ <template> -<div class="_formRoot"> +<div class="_autoGap"> <FormSection> <template #label>{{ i18n.ts.emailAddress }}</template> <FormInput v-model="emailAddress" type="email" manual-save> @@ -17,24 +17,27 @@ <FormSection> <template #label>{{ i18n.ts.emailNotification }}</template> - <FormSwitch v-model="emailNotification_mention" class="_formBlock"> - {{ i18n.ts._notification._types.mention }} - </FormSwitch> - <FormSwitch v-model="emailNotification_reply" class="_formBlock"> - {{ i18n.ts._notification._types.reply }} - </FormSwitch> - <FormSwitch v-model="emailNotification_quote" class="_formBlock"> - {{ i18n.ts._notification._types.quote }} - </FormSwitch> - <FormSwitch v-model="emailNotification_follow" class="_formBlock"> - {{ i18n.ts._notification._types.follow }} - </FormSwitch> - <FormSwitch v-model="emailNotification_receiveFollowRequest" class="_formBlock"> - {{ i18n.ts._notification._types.receiveFollowRequest }} - </FormSwitch> - <FormSwitch v-model="emailNotification_groupInvited" class="_formBlock"> - {{ i18n.ts._notification._types.groupInvited }} - </FormSwitch> + + <div class="_autoGap_half"> + <FormSwitch v-model="emailNotification_mention"> + {{ i18n.ts._notification._types.mention }} + </FormSwitch> + <FormSwitch v-model="emailNotification_reply"> + {{ i18n.ts._notification._types.reply }} + </FormSwitch> + <FormSwitch v-model="emailNotification_quote"> + {{ i18n.ts._notification._types.quote }} + </FormSwitch> + <FormSwitch v-model="emailNotification_follow"> + {{ i18n.ts._notification._types.follow }} + </FormSwitch> + <FormSwitch v-model="emailNotification_receiveFollowRequest"> + {{ i18n.ts._notification._types.receiveFollowRequest }} + </FormSwitch> + <FormSwitch v-model="emailNotification_groupInvited"> + {{ i18n.ts._notification._types.groupInvited }} + </FormSwitch> + </div> </FormSection> </div> </template> diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue index b426ccfa0a..f2d6bbe1f7 100644 --- a/packages/frontend/src/pages/settings/general.vue +++ b/packages/frontend/src/pages/settings/general.vue @@ -1,6 +1,6 @@ <template> -<div class="_formRoot"> - <FormSelect v-model="lang" class="_formBlock"> +<div class="_autoGap"> + <FormSelect v-model="lang"> <template #label>{{ i18n.ts.uiLanguage }}</template> <option v-for="x in langs" :key="x[0]" :value="x[0]">{{ x[1] }}</option> <template #caption> @@ -12,7 +12,7 @@ </template> </FormSelect> - <FormRadios v-model="overridedDeviceKind" class="_formBlock"> + <FormRadios v-model="overridedDeviceKind"> <template #label>{{ i18n.ts.overridedDeviceKind }}</template> <option :value="null">{{ i18n.ts.auto }}</option> <option value="smartphone"><i class="ti ti-device-mobile"/> {{ i18n.ts.smartphone }}</option> @@ -20,80 +20,88 @@ <option value="desktop"><i class="ti ti-device-desktop"/> {{ i18n.ts.desktop }}</option> </FormRadios> - <FormSwitch v-model="showFixedPostForm" class="_formBlock">{{ i18n.ts.showFixedPostForm }}</FormSwitch> + <FormSwitch v-model="showFixedPostForm">{{ i18n.ts.showFixedPostForm }}</FormSwitch> <FormSection> <template #label>{{ i18n.ts.behavior }}</template> - <FormSwitch v-model="imageNewTab" class="_formBlock">{{ i18n.ts.openImageInNewTab }}</FormSwitch> - <FormSwitch v-model="enableInfiniteScroll" class="_formBlock">{{ i18n.ts.enableInfiniteScroll }}</FormSwitch> - <FormSwitch v-model="useReactionPickerForContextMenu" class="_formBlock">{{ i18n.ts.useReactionPickerForContextMenu }}</FormSwitch> - <FormSelect v-model="serverDisconnectedBehavior" class="_formBlock"> - <template #label>{{ i18n.ts.whenServerDisconnected }}</template> - <option value="reload">{{ i18n.ts._serverDisconnectedBehavior.reload }}</option> - <option value="dialog">{{ i18n.ts._serverDisconnectedBehavior.dialog }}</option> - <option value="quiet">{{ i18n.ts._serverDisconnectedBehavior.quiet }}</option> - </FormSelect> + <div class="_autoGap"> + <div class="_autoGap_half"> + <FormSwitch v-model="imageNewTab">{{ i18n.ts.openImageInNewTab }}</FormSwitch> + <FormSwitch v-model="enableInfiniteScroll">{{ i18n.ts.enableInfiniteScroll }}</FormSwitch> + <FormSwitch v-model="useReactionPickerForContextMenu">{{ i18n.ts.useReactionPickerForContextMenu }}</FormSwitch> + </div> + <FormSelect v-model="serverDisconnectedBehavior"> + <template #label>{{ i18n.ts.whenServerDisconnected }}</template> + <option value="reload">{{ i18n.ts._serverDisconnectedBehavior.reload }}</option> + <option value="dialog">{{ i18n.ts._serverDisconnectedBehavior.dialog }}</option> + <option value="quiet">{{ i18n.ts._serverDisconnectedBehavior.quiet }}</option> + </FormSelect> + </div> </FormSection> <FormSection> <template #label>{{ i18n.ts.appearance }}</template> - <FormSwitch v-model="disableAnimatedMfm" class="_formBlock">{{ i18n.ts.disableAnimatedMfm }}</FormSwitch> - <FormSwitch v-model="reduceAnimation" class="_formBlock">{{ i18n.ts.reduceUiAnimation }}</FormSwitch> - <FormSwitch v-model="useBlurEffect" class="_formBlock">{{ i18n.ts.useBlurEffect }}</FormSwitch> - <FormSwitch v-model="useBlurEffectForModal" class="_formBlock">{{ i18n.ts.useBlurEffectForModal }}</FormSwitch> - <FormSwitch v-model="showGapBetweenNotesInTimeline" class="_formBlock">{{ i18n.ts.showGapBetweenNotesInTimeline }}</FormSwitch> - <FormSwitch v-model="loadRawImages" class="_formBlock">{{ i18n.ts.loadRawImages }}</FormSwitch> - <FormSwitch v-model="disableShowingAnimatedImages" class="_formBlock">{{ i18n.ts.disableShowingAnimatedImages }}</FormSwitch> - <FormSwitch v-model="squareAvatars" class="_formBlock">{{ i18n.ts.squareAvatars }}</FormSwitch> - <FormSwitch v-model="useSystemFont" class="_formBlock">{{ i18n.ts.useSystemFont }}</FormSwitch> - <div class="_formBlock"> - <FormRadios v-model="emojiStyle"> - <template #label>{{ i18n.ts.emojiStyle }}</template> - <option value="native">{{ i18n.ts.native }}</option> - <option value="fluentEmoji">Fluent Emoji</option> - <option value="twemoji">Twemoji</option> + + <div class="_autoGap"> + <div class="_autoGap_half"> + <FormSwitch v-model="disableAnimatedMfm">{{ i18n.ts.disableAnimatedMfm }}</FormSwitch> + <FormSwitch v-model="reduceAnimation">{{ i18n.ts.reduceUiAnimation }}</FormSwitch> + <FormSwitch v-model="useBlurEffect">{{ i18n.ts.useBlurEffect }}</FormSwitch> + <FormSwitch v-model="useBlurEffectForModal">{{ i18n.ts.useBlurEffectForModal }}</FormSwitch> + <FormSwitch v-model="showGapBetweenNotesInTimeline">{{ i18n.ts.showGapBetweenNotesInTimeline }}</FormSwitch> + <FormSwitch v-model="loadRawImages">{{ i18n.ts.loadRawImages }}</FormSwitch> + <FormSwitch v-model="disableShowingAnimatedImages">{{ i18n.ts.disableShowingAnimatedImages }}</FormSwitch> + <FormSwitch v-model="squareAvatars">{{ i18n.ts.squareAvatars }}</FormSwitch> + <FormSwitch v-model="useSystemFont">{{ i18n.ts.useSystemFont }}</FormSwitch> + <FormSwitch v-model="disableDrawer">{{ i18n.ts.disableDrawer }}</FormSwitch> + </div> + <div> + <FormRadios v-model="emojiStyle"> + <template #label>{{ i18n.ts.emojiStyle }}</template> + <option value="native">{{ i18n.ts.native }}</option> + <option value="fluentEmoji">Fluent Emoji</option> + <option value="twemoji">Twemoji</option> + </FormRadios> + <div style="margin: 8px 0 0 0; font-size: 1.5em;"><Mfm :key="emojiStyle" text="🍮🍦🍭🍩🍰🍫🍬🥞🍪"/></div> + </div> + + <FormRadios v-model="fontSize"> + <template #label>{{ i18n.ts.fontSize }}</template> + <option :value="null"><span style="font-size: 14px;">Aa</span></option> + <option value="1"><span style="font-size: 15px;">Aa</span></option> + <option value="2"><span style="font-size: 16px;">Aa</span></option> + <option value="3"><span style="font-size: 17px;">Aa</span></option> </FormRadios> - <div style="margin: 8px 0 0 0; font-size: 1.5em;"><Mfm :key="emojiStyle" text="🍮🍦🍭🍩🍰🍫🍬🥞🍪"/></div> </div> - - <FormSwitch v-model="disableDrawer" class="_formBlock">{{ i18n.ts.disableDrawer }}</FormSwitch> - - <FormRadios v-model="fontSize" class="_formBlock"> - <template #label>{{ i18n.ts.fontSize }}</template> - <option :value="null"><span style="font-size: 14px;">Aa</span></option> - <option value="1"><span style="font-size: 15px;">Aa</span></option> - <option value="2"><span style="font-size: 16px;">Aa</span></option> - <option value="3"><span style="font-size: 17px;">Aa</span></option> - </FormRadios> </FormSection> <FormSection> <FormSwitch v-model="aiChanMode">{{ i18n.ts.aiChanMode }}</FormSwitch> </FormSection> - <FormSelect v-model="instanceTicker" class="_formBlock"> + <FormSelect v-model="instanceTicker"> <template #label>{{ i18n.ts.instanceTicker }}</template> <option value="none">{{ i18n.ts._instanceTicker.none }}</option> <option value="remote">{{ i18n.ts._instanceTicker.remote }}</option> <option value="always">{{ i18n.ts._instanceTicker.always }}</option> </FormSelect> - <FormSelect v-model="nsfw" class="_formBlock"> + <FormSelect v-model="nsfw"> <template #label>{{ i18n.ts.nsfw }}</template> <option value="respect">{{ i18n.ts._nsfw.respect }}</option> <option value="ignore">{{ i18n.ts._nsfw.ignore }}</option> <option value="force">{{ i18n.ts._nsfw.force }}</option> </FormSelect> - <FormRange v-model="numberOfPageCache" :min="1" :max="10" :step="1" easing class="_formBlock"> + <FormRange v-model="numberOfPageCache" :min="1" :max="10" :step="1" easing> <template #label>{{ i18n.ts.numberOfPageCache }}</template> <template #caption>{{ i18n.ts.numberOfPageCacheDescription }}</template> </FormRange> - <FormLink to="/settings/deck" class="_formBlock">{{ i18n.ts.deck }}</FormLink> + <FormLink to="/settings/deck">{{ i18n.ts.deck }}</FormLink> - <FormLink to="/settings/custom-css" class="_formBlock"><template #icon><i class="ti ti-code"></i></template>{{ i18n.ts.customCss }}</FormLink> + <FormLink to="/settings/custom-css"><template #icon><i class="ti ti-code"></i></template>{{ i18n.ts.customCss }}</FormLink> </div> </template> diff --git a/packages/frontend/src/pages/settings/import-export.vue b/packages/frontend/src/pages/settings/import-export.vue index 3012c3f283..13dcc627a2 100644 --- a/packages/frontend/src/pages/settings/import-export.vue +++ b/packages/frontend/src/pages/settings/import-export.vue @@ -1,5 +1,5 @@ <template> -<div class="_formRoot"> +<div class="_autoGap"> <FormSection> <template #label><i class="ti ti-pencil"></i> {{ i18n.ts._exportOrImport.allNotes }}</template> <FormFolder> @@ -18,18 +18,18 @@ </FormSection> <FormSection> <template #label><i class="ti ti-users"></i> {{ i18n.ts._exportOrImport.followingList }}</template> - <FormFolder class="_formBlock"> + <FormFolder> <template #label>{{ i18n.ts.export }}</template> <template #icon><i class="ti ti-download"></i></template> - <FormSwitch v-model="excludeMutingUsers" class="_formBlock"> + <FormSwitch v-model="excludeMutingUsers"> {{ i18n.ts._exportOrImport.excludeMutingUsers }} </FormSwitch> - <FormSwitch v-model="excludeInactiveUsers" class="_formBlock"> + <FormSwitch v-model="excludeInactiveUsers"> {{ i18n.ts._exportOrImport.excludeInactiveUsers }} </FormSwitch> <MkButton primary :class="$style.button" inline @click="exportFollowing()"><i class="ti ti-download"></i> {{ i18n.ts.export }}</MkButton> </FormFolder> - <FormFolder class="_formBlock"> + <FormFolder> <template #label>{{ i18n.ts.import }}</template> <template #icon><i class="ti ti-upload"></i></template> <MkButton primary :class="$style.button" inline @click="importFollowing($event)"><i class="ti ti-upload"></i> {{ i18n.ts.import }}</MkButton> @@ -37,12 +37,12 @@ </FormSection> <FormSection> <template #label><i class="ti ti-users"></i> {{ i18n.ts._exportOrImport.userLists }}</template> - <FormFolder class="_formBlock"> + <FormFolder> <template #label>{{ i18n.ts.export }}</template> <template #icon><i class="ti ti-download"></i></template> <MkButton primary :class="$style.button" inline @click="exportUserLists()"><i class="ti ti-download"></i> {{ i18n.ts.export }}</MkButton> </FormFolder> - <FormFolder class="_formBlock"> + <FormFolder> <template #label>{{ i18n.ts.import }}</template> <template #icon><i class="ti ti-upload"></i></template> <MkButton primary :class="$style.button" inline @click="importUserLists($event)"><i class="ti ti-upload"></i> {{ i18n.ts.import }}</MkButton> @@ -50,12 +50,12 @@ </FormSection> <FormSection> <template #label><i class="ti ti-user-off"></i> {{ i18n.ts._exportOrImport.muteList }}</template> - <FormFolder class="_formBlock"> + <FormFolder> <template #label>{{ i18n.ts.export }}</template> <template #icon><i class="ti ti-download"></i></template> <MkButton primary :class="$style.button" inline @click="exportMuting()"><i class="ti ti-download"></i> {{ i18n.ts.export }}</MkButton> </FormFolder> - <FormFolder class="_formBlock"> + <FormFolder> <template #label>{{ i18n.ts.import }}</template> <template #icon><i class="ti ti-upload"></i></template> <MkButton primary :class="$style.button" inline @click="importMuting($event)"><i class="ti ti-upload"></i> {{ i18n.ts.import }}</MkButton> @@ -63,12 +63,12 @@ </FormSection> <FormSection> <template #label><i class="ti ti-user-off"></i> {{ i18n.ts._exportOrImport.blockingList }}</template> - <FormFolder class="_formBlock"> + <FormFolder> <template #label>{{ i18n.ts.export }}</template> <template #icon><i class="ti ti-download"></i></template> <MkButton primary :class="$style.button" inline @click="exportBlocking()"><i class="ti ti-download"></i> {{ i18n.ts.export }}</MkButton> </FormFolder> - <FormFolder class="_formBlock"> + <FormFolder> <template #label>{{ i18n.ts.import }}</template> <template #icon><i class="ti ti-upload"></i></template> <MkButton primary :class="$style.button" inline @click="importBlocking($event)"><i class="ti ti-upload"></i> {{ i18n.ts.import }}</MkButton> diff --git a/packages/frontend/src/pages/settings/instance-mute.vue b/packages/frontend/src/pages/settings/instance-mute.vue index 54504de188..6436be5b08 100644 --- a/packages/frontend/src/pages/settings/instance-mute.vue +++ b/packages/frontend/src/pages/settings/instance-mute.vue @@ -1,11 +1,11 @@ <template> -<div class="_formRoot"> +<div class="_autoGap"> <MkInfo>{{ i18n.ts._instanceMute.title }}</MkInfo> - <FormTextarea v-model="instanceMutes" class="_formBlock"> + <FormTextarea v-model="instanceMutes"> <template #label>{{ i18n.ts._instanceMute.heading }}</template> <template #caption>{{ i18n.ts._instanceMute.instanceMuteDescription }}<br>{{ i18n.ts._instanceMute.instanceMuteDescription2 }}</template> </FormTextarea> - <MkButton primary :disabled="!changed" class="_formBlock" @click="save()"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</MkButton> + <MkButton primary :disabled="!changed" @click="save()"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</MkButton> </div> </template> diff --git a/packages/frontend/src/pages/settings/integration.vue b/packages/frontend/src/pages/settings/integration.vue index 557fe778e6..fe8087eb62 100644 --- a/packages/frontend/src/pages/settings/integration.vue +++ b/packages/frontend/src/pages/settings/integration.vue @@ -1,5 +1,5 @@ <template> -<div class="_formRoot"> +<div class="_autoGap"> <FormSection v-if="instance.enableTwitterIntegration"> <template #label><i class="ti ti-brand-twitter"></i> Twitter</template> <p v-if="integrations.twitter">{{ i18n.ts.connectedTo }}: <a :href="`https://twitter.com/${integrations.twitter.screenName}`" rel="nofollow noopener" target="_blank">@{{ integrations.twitter.screenName }}</a></p> diff --git a/packages/frontend/src/pages/settings/mute-block.vue b/packages/frontend/src/pages/settings/mute-block.vue index 1cf33d34db..7a72d4a643 100644 --- a/packages/frontend/src/pages/settings/mute-block.vue +++ b/packages/frontend/src/pages/settings/mute-block.vue @@ -1,5 +1,5 @@ <template> -<div class="_formRoot"> +<div class="_autoGap"> <MkTab v-model="tab" style="margin-bottom: var(--margin);"> <option value="mute">{{ i18n.ts.mutedUsers }}</option> <option value="block">{{ i18n.ts.blockedUsers }}</option> diff --git a/packages/frontend/src/pages/settings/navbar.vue b/packages/frontend/src/pages/settings/navbar.vue index 9ab8700b01..33b970528a 100644 --- a/packages/frontend/src/pages/settings/navbar.vue +++ b/packages/frontend/src/pages/settings/navbar.vue @@ -1,11 +1,11 @@ <template> -<div class="_formRoot"> - <FormTextarea v-model="items" tall manual-save class="_formBlock"> +<div class="_autoGap"> + <FormTextarea v-model="items" tall manual-save> <template #label>{{ i18n.ts.navbar }}</template> <template #caption><button class="_textButton" @click="addItem">{{ i18n.ts.addItem }}</button></template> </FormTextarea> - <FormRadios v-model="menuDisplay" class="_formBlock"> + <FormRadios v-model="menuDisplay"> <template #label>{{ i18n.ts.display }}</template> <option value="sideFull">{{ i18n.ts._menuDisplay.sideFull }}</option> <option value="sideIcon">{{ i18n.ts._menuDisplay.sideIcon }}</option> @@ -13,7 +13,7 @@ <!-- <MkRadio v-model="menuDisplay" value="hide" disabled>{{ i18n.ts._menuDisplay.hide }}</MkRadio>--> <!-- TODO: サイドバーを完全に隠せるようにすると、別途ハンバーガーボタンのようなものをUIに表示する必要があり面倒 --> </FormRadios> - <FormButton danger class="_formBlock" @click="reset()"><i class="ti ti-reload"></i> {{ i18n.ts.default }}</FormButton> + <FormButton danger @click="reset()"><i class="ti ti-reload"></i> {{ i18n.ts.default }}</FormButton> </div> </template> diff --git a/packages/frontend/src/pages/settings/notifications.vue b/packages/frontend/src/pages/settings/notifications.vue index c1b7130245..872d26c3fb 100644 --- a/packages/frontend/src/pages/settings/notifications.vue +++ b/packages/frontend/src/pages/settings/notifications.vue @@ -1,22 +1,27 @@ <template> -<div class="_formRoot"> - <FormLink class="_formBlock" @click="configure"><template #icon><i class="ti ti-settings"></i></template>{{ i18n.ts.notificationSetting }}</FormLink> +<div class="_autoGap"> + <FormLink @click="configure"><template #icon><i class="ti ti-settings"></i></template>{{ i18n.ts.notificationSetting }}</FormLink> <FormSection> - <FormLink class="_formBlock" @click="readAllNotifications">{{ i18n.ts.markAsReadAllNotifications }}</FormLink> - <FormLink class="_formBlock" @click="readAllUnreadNotes">{{ i18n.ts.markAsReadAllUnreadNotes }}</FormLink> - <FormLink class="_formBlock" @click="readAllMessagingMessages">{{ i18n.ts.markAsReadAllTalkMessages }}</FormLink> + <div class="_autoGap"> + <FormLink @click="readAllNotifications">{{ i18n.ts.markAsReadAllNotifications }}</FormLink> + <FormLink @click="readAllUnreadNotes">{{ i18n.ts.markAsReadAllUnreadNotes }}</FormLink> + <FormLink @click="readAllMessagingMessages">{{ i18n.ts.markAsReadAllTalkMessages }}</FormLink> + </div> </FormSection> <FormSection> <template #label>{{ i18n.ts.pushNotification }}</template> - <MkPushNotificationAllowButton ref="allowButton" /> - <FormSwitch class="_formBlock" :disabled="!pushRegistrationInServer" :model-value="sendReadMessage" @update:model-value="onChangeSendReadMessage"> - <template #label>{{ i18n.ts.sendPushNotificationReadMessage }}</template> - <template #caption> - <I18n :src="i18n.ts.sendPushNotificationReadMessageCaption"> - <template #emptyPushNotificationMessage>{{ i18n.ts._notification.emptyPushNotificationMessage }}</template> - </I18n> - </template> - </FormSwitch> + + <div class="_autoGap"> + <MkPushNotificationAllowButton ref="allowButton"/> + <FormSwitch :disabled="!pushRegistrationInServer" :model-value="sendReadMessage" @update:model-value="onChangeSendReadMessage"> + <template #label>{{ i18n.ts.sendPushNotificationReadMessage }}</template> + <template #caption> + <I18n :src="i18n.ts.sendPushNotificationReadMessageCaption"> + <template #emptyPushNotificationMessage>{{ i18n.ts._notification.emptyPushNotificationMessage }}</template> + </I18n> + </template> + </FormSwitch> + </div> </FormSection> </div> </template> diff --git a/packages/frontend/src/pages/settings/other.vue b/packages/frontend/src/pages/settings/other.vue index 40bb202789..82fca7fd14 100644 --- a/packages/frontend/src/pages/settings/other.vue +++ b/packages/frontend/src/pages/settings/other.vue @@ -1,18 +1,18 @@ <template> -<div class="_formRoot"> - <FormSwitch v-model="$i.injectFeaturedNote" class="_formBlock" @update:model-value="onChangeInjectFeaturedNote"> +<div class="_autoGap"> + <FormSwitch v-model="$i.injectFeaturedNote" @update:model-value="onChangeInjectFeaturedNote"> {{ i18n.ts.showFeaturedNotesInTimeline }} </FormSwitch> <!-- - <FormSwitch v-model="reportError" class="_formBlock">{{ i18n.ts.sendErrorReports }}<template #caption>{{ i18n.ts.sendErrorReportsDescription }}</template></FormSwitch> + <FormSwitch v-model="reportError">{{ i18n.ts.sendErrorReports }}<template #caption>{{ i18n.ts.sendErrorReportsDescription }}</template></FormSwitch> --> - <FormLink to="/settings/account-info" class="_formBlock">{{ i18n.ts.accountInfo }}</FormLink> + <FormLink to="/settings/account-info">{{ i18n.ts.accountInfo }}</FormLink> - <FormLink to="/registry" class="_formBlock"><template #icon><i class="ti ti-adjustments"></i></template>{{ i18n.ts.registry }}</FormLink> + <FormLink to="/registry"><template #icon><i class="ti ti-adjustments"></i></template>{{ i18n.ts.registry }}</FormLink> - <FormLink to="/settings/delete-account" class="_formBlock"><template #icon><i class="ti ti-alert-triangle"></i></template>{{ i18n.ts.closeAccount }}</FormLink> + <FormLink to="/settings/delete-account"><template #icon><i class="ti ti-alert-triangle"></i></template>{{ i18n.ts.closeAccount }}</FormLink> </div> </template> diff --git a/packages/frontend/src/pages/settings/plugin.install.vue b/packages/frontend/src/pages/settings/plugin.install.vue index 3d77cadaaa..1950e7f089 100644 --- a/packages/frontend/src/pages/settings/plugin.install.vue +++ b/packages/frontend/src/pages/settings/plugin.install.vue @@ -1,12 +1,12 @@ <template> -<div class="_formRoot"> - <FormInfo warn class="_formBlock">{{ i18n.ts._plugin.installWarn }}</FormInfo> +<div class="_autoGap"> + <FormInfo warn>{{ i18n.ts._plugin.installWarn }}</FormInfo> - <FormTextarea v-model="code" tall class="_formBlock"> + <FormTextarea v-model="code" tall> <template #label>{{ i18n.ts.code }}</template> </FormTextarea> - <div class="_formBlock"> + <div> <FormButton :disabled="code == null" primary inline @click="install"><i class="ti ti-check"></i> {{ i18n.ts.install }}</FormButton> </div> </div> diff --git a/packages/frontend/src/pages/settings/plugin.vue b/packages/frontend/src/pages/settings/plugin.vue index 905efd833d..3ff1d64b8e 100644 --- a/packages/frontend/src/pages/settings/plugin.vue +++ b/packages/frontend/src/pages/settings/plugin.vue @@ -1,23 +1,23 @@ <template> -<div class="_formRoot"> +<div class="_autoGap"> <FormLink 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> - <div v-for="plugin in plugins" :key="plugin.id" class="_formBlock _panel" style="padding: 20px;"> + <div v-for="plugin in plugins" :key="plugin.id" class="_panel _autoGap_half" style="padding: 20px;"> <span style="display: flex;"><b>{{ plugin.name }}</b><span style="margin-left: auto;">v{{ plugin.version }}</span></span> - <FormSwitch class="_formBlock" :model-value="plugin.active" @update:model-value="changeActive(plugin, $event)">{{ i18n.ts.makeActive }}</FormSwitch> + <FormSwitch :model-value="plugin.active" @update:model-value="changeActive(plugin, $event)">{{ i18n.ts.makeActive }}</FormSwitch> - <MkKeyValue class="_formBlock"> + <MkKeyValue> <template #key>{{ i18n.ts.author }}</template> <template #value>{{ plugin.author }}</template> </MkKeyValue> - <MkKeyValue class="_formBlock"> + <MkKeyValue> <template #key>{{ i18n.ts.description }}</template> <template #value>{{ plugin.description }}</template> </MkKeyValue> - <MkKeyValue class="_formBlock"> + <MkKeyValue> <template #key>{{ i18n.ts.permission }}</template> <template #value>{{ plugin.permission }}</template> </MkKeyValue> diff --git a/packages/frontend/src/pages/settings/preferences-backups.vue b/packages/frontend/src/pages/settings/preferences-backups.vue index a713c1262d..bc19234c43 100644 --- a/packages/frontend/src/pages/settings/preferences-backups.vue +++ b/packages/frontend/src/pages/settings/preferences-backups.vue @@ -1,5 +1,5 @@ <template> -<div class="_formRoot"> +<div class="_autoGap"> <div :class="$style.buttons"> <MkButton inline primary @click="saveNew">{{ ts._preferencesBackups.saveNew }}</MkButton> <MkButton inline @click="loadFile">{{ ts._preferencesBackups.loadFile }}</MkButton> @@ -8,17 +8,19 @@ <FormSection> <template #label>{{ ts._preferencesBackups.list }}</template> <template v-if="profiles && Object.keys(profiles).length > 0"> - <div - v-for="(profile, id) in profiles" - :key="id" - class="_formBlock _panel" - :class="$style.profile" - @click="$event => menu($event, id)" - @contextmenu.prevent.stop="$event => menu($event, id)" - > - <div :class="$style.profileName">{{ profile.name }}</div> - <div :class="$style.profileTime">{{ t('_preferencesBackups.createdAt', { date: (new Date(profile.createdAt)).toLocaleDateString(), time: (new Date(profile.createdAt)).toLocaleTimeString() }) }}</div> - <div v-if="profile.updatedAt" :class="$style.profileTime">{{ t('_preferencesBackups.updatedAt', { date: (new Date(profile.updatedAt)).toLocaleDateString(), time: (new Date(profile.updatedAt)).toLocaleTimeString() }) }}</div> + <div class="_autoGap_half"> + <div + v-for="(profile, id) in profiles" + :key="id" + class="_panel" + :class="$style.profile" + @click="$event => menu($event, id)" + @contextmenu.prevent.stop="$event => menu($event, id)" + > + <div :class="$style.profileName">{{ profile.name }}</div> + <div :class="$style.profileTime">{{ t('_preferencesBackups.createdAt', { date: (new Date(profile.createdAt)).toLocaleDateString(), time: (new Date(profile.createdAt)).toLocaleTimeString() }) }}</div> + <div v-if="profile.updatedAt" :class="$style.profileTime">{{ t('_preferencesBackups.updatedAt', { date: (new Date(profile.updatedAt)).toLocaleDateString(), time: (new Date(profile.updatedAt)).toLocaleTimeString() }) }}</div> + </div> </div> </template> <div v-else-if="profiles"> diff --git a/packages/frontend/src/pages/settings/privacy.vue b/packages/frontend/src/pages/settings/privacy.vue index 915ca05767..94ad35df4e 100644 --- a/packages/frontend/src/pages/settings/privacy.vue +++ b/packages/frontend/src/pages/settings/privacy.vue @@ -1,14 +1,14 @@ <template> -<div class="_formRoot"> - <FormSwitch v-model="isLocked" class="_formBlock" @update:model-value="save()">{{ i18n.ts.makeFollowManuallyApprove }}<template #caption>{{ i18n.ts.lockedAccountInfo }}</template></FormSwitch> - <FormSwitch v-if="isLocked" v-model="autoAcceptFollowed" class="_formBlock" @update:model-value="save()">{{ i18n.ts.autoAcceptFollowed }}</FormSwitch> +<div class="_autoGap"> + <FormSwitch v-model="isLocked" @update:model-value="save()">{{ i18n.ts.makeFollowManuallyApprove }}<template #caption>{{ i18n.ts.lockedAccountInfo }}</template></FormSwitch> + <FormSwitch v-if="isLocked" v-model="autoAcceptFollowed" @update:model-value="save()">{{ i18n.ts.autoAcceptFollowed }}</FormSwitch> - <FormSwitch v-model="publicReactions" class="_formBlock" @update:model-value="save()"> + <FormSwitch v-model="publicReactions" @update:model-value="save()"> {{ i18n.ts.makeReactionsPublic }} <template #caption>{{ i18n.ts.makeReactionsPublicDescription }}</template> </FormSwitch> - <FormSelect v-model="ffVisibility" class="_formBlock" @update:model-value="save()"> + <FormSelect v-model="ffVisibility" @update:model-value="save()"> <template #label>{{ i18n.ts.ffVisibility }}</template> <option value="public">{{ i18n.ts._ffVisibility.public }}</option> <option value="followers">{{ i18n.ts._ffVisibility.followers }}</option> @@ -16,39 +16,43 @@ <template #caption>{{ i18n.ts.ffVisibilityDescription }}</template> </FormSelect> - <FormSwitch v-model="hideOnlineStatus" class="_formBlock" @update:model-value="save()"> + <FormSwitch v-model="hideOnlineStatus" @update:model-value="save()"> {{ i18n.ts.hideOnlineStatus }} <template #caption>{{ i18n.ts.hideOnlineStatusDescription }}</template> </FormSwitch> - <FormSwitch v-model="noCrawle" class="_formBlock" @update:model-value="save()"> + <FormSwitch v-model="noCrawle" @update:model-value="save()"> {{ i18n.ts.noCrawle }} <template #caption>{{ i18n.ts.noCrawleDescription }}</template> </FormSwitch> - <FormSwitch v-model="isExplorable" class="_formBlock" @update:model-value="save()"> + <FormSwitch v-model="isExplorable" @update:model-value="save()"> {{ i18n.ts.makeExplorable }} <template #caption>{{ i18n.ts.makeExplorableDescription }}</template> </FormSwitch> <FormSection> - <FormSwitch v-model="rememberNoteVisibility" class="_formBlock" @update:model-value="save()">{{ i18n.ts.rememberNoteVisibility }}</FormSwitch> - <FormFolder v-if="!rememberNoteVisibility" class="_formBlock"> - <template #label>{{ i18n.ts.defaultNoteVisibility }}</template> - <template v-if="defaultNoteVisibility === 'public'" #suffix>{{ i18n.ts._visibility.public }}</template> - <template v-else-if="defaultNoteVisibility === 'home'" #suffix>{{ i18n.ts._visibility.home }}</template> - <template v-else-if="defaultNoteVisibility === 'followers'" #suffix>{{ i18n.ts._visibility.followers }}</template> - <template v-else-if="defaultNoteVisibility === 'specified'" #suffix>{{ i18n.ts._visibility.specified }}</template> + <div class="_autoGap"> + <FormSwitch v-model="rememberNoteVisibility" @update:model-value="save()">{{ i18n.ts.rememberNoteVisibility }}</FormSwitch> + <FormFolder v-if="!rememberNoteVisibility"> + <template #label>{{ i18n.ts.defaultNoteVisibility }}</template> + <template v-if="defaultNoteVisibility === 'public'" #suffix>{{ i18n.ts._visibility.public }}</template> + <template v-else-if="defaultNoteVisibility === 'home'" #suffix>{{ i18n.ts._visibility.home }}</template> + <template v-else-if="defaultNoteVisibility === 'followers'" #suffix>{{ i18n.ts._visibility.followers }}</template> + <template v-else-if="defaultNoteVisibility === 'specified'" #suffix>{{ i18n.ts._visibility.specified }}</template> - <FormSelect v-model="defaultNoteVisibility" class="_formBlock"> - <option value="public">{{ i18n.ts._visibility.public }}</option> - <option value="home">{{ i18n.ts._visibility.home }}</option> - <option value="followers">{{ i18n.ts._visibility.followers }}</option> - <option value="specified">{{ i18n.ts._visibility.specified }}</option> - </FormSelect> - <FormSwitch v-model="defaultNoteLocalOnly" class="_formBlock">{{ i18n.ts._visibility.localOnly }}</FormSwitch> - </FormFolder> + <div class="_autoGap"> + <FormSelect v-model="defaultNoteVisibility"> + <option value="public">{{ i18n.ts._visibility.public }}</option> + <option value="home">{{ i18n.ts._visibility.home }}</option> + <option value="followers">{{ i18n.ts._visibility.followers }}</option> + <option value="specified">{{ i18n.ts._visibility.specified }}</option> + </FormSelect> + <FormSwitch v-model="defaultNoteLocalOnly">{{ i18n.ts._visibility.localOnly }}</FormSwitch> + </div> + </FormFolder> + </div> </FormSection> - <FormSwitch v-model="keepCw" class="_formBlock" @update:model-value="save()">{{ i18n.ts.keepCw }}</FormSwitch> + <FormSwitch v-model="keepCw" @update:model-value="save()">{{ i18n.ts.keepCw }}</FormSwitch> </div> </template> diff --git a/packages/frontend/src/pages/settings/profile.vue b/packages/frontend/src/pages/settings/profile.vue index 14eeeaaa11..6ee2efe467 100644 --- a/packages/frontend/src/pages/settings/profile.vue +++ b/packages/frontend/src/pages/settings/profile.vue @@ -1,5 +1,5 @@ <template> -<div class="_formRoot"> +<div class="_autoGap"> <div class="llvierxe" :style="{ backgroundImage: $i.bannerUrl ? `url(${ $i.bannerUrl })` : null }"> <div class="avatar"> <MkAvatar class="avatar" :user="$i" :disable-link="true" @click="changeAvatar"/> @@ -8,37 +8,37 @@ <MkButton primary rounded class="bannerEdit" @click="changeBanner">{{ i18n.ts._profile.changeBanner }}</MkButton> </div> - <FormInput v-model="profile.name" :max="30" manual-save class="_formBlock"> + <FormInput v-model="profile.name" :max="30" manual-save> <template #label>{{ i18n.ts._profile.name }}</template> </FormInput> - <FormTextarea v-model="profile.description" :max="500" tall manual-save class="_formBlock"> + <FormTextarea v-model="profile.description" :max="500" tall manual-save> <template #label>{{ i18n.ts._profile.description }}</template> <template #caption>{{ i18n.ts._profile.youCanIncludeHashtags }}</template> </FormTextarea> - <FormInput v-model="profile.location" manual-save class="_formBlock"> + <FormInput v-model="profile.location" manual-save> <template #label>{{ i18n.ts.location }}</template> <template #prefix><i class="ti ti-map-pin"></i></template> </FormInput> - <FormInput v-model="profile.birthday" type="date" manual-save class="_formBlock"> + <FormInput v-model="profile.birthday" type="date" manual-save> <template #label>{{ i18n.ts.birthday }}</template> <template #prefix><i class="ti ti-cake"></i></template> </FormInput> - <FormSelect v-model="profile.lang" class="_formBlock"> + <FormSelect v-model="profile.lang"> <template #label>{{ i18n.ts.language }}</template> <option v-for="x in Object.keys(langmap)" :key="x" :value="x">{{ langmap[x].nativeName }}</option> </FormSelect> - <FormSlot class="_formBlock"> + <FormSlot> <FormFolder> <template #icon><i class="ti ti-list"></i></template> <template #label>{{ i18n.ts._profile.metadataEdit }}</template> - <div class="_formRoot"> - <FormSplit v-for="(record, i) in fields" :min-width="250" class="_formBlock"> + <div class="_autoGap"> + <FormSplit v-for="(record, i) in fields" :min-width="250"> <FormInput v-model="record.name" small> <template #label>{{ i18n.ts._profile.metadataLabel }} #{{ i + 1 }}</template> </FormInput> @@ -46,8 +46,10 @@ <template #label>{{ i18n.ts._profile.metadataContent }} #{{ i + 1 }}</template> </FormInput> </FormSplit> - <MkButton :disabled="fields.length >= 16" inline style="margin-right: 8px;" @click="addField"><i class="ti ti-plus"></i> {{ i18n.ts.add }}</MkButton> - <MkButton inline primary @click="saveFields"><i class="ti ti-check"></i> {{ i18n.ts.save }}</MkButton> + <div> + <MkButton :disabled="fields.length >= 16" inline style="margin-right: 8px;" @click="addField"><i class="ti ti-plus"></i> {{ i18n.ts.add }}</MkButton> + <MkButton inline primary @click="saveFields"><i class="ti ti-check"></i> {{ i18n.ts.save }}</MkButton> + </div> </div> </FormFolder> <template #caption>{{ i18n.ts._profile.metadataDescription }}</template> @@ -56,13 +58,13 @@ <FormFolder> <template #label>{{ i18n.ts.advancedSettings }}</template> - <div class="_formRoot"> - <FormSwitch v-model="profile.isCat" class="_formBlock">{{ i18n.ts.flagAsCat }}<template #caption>{{ i18n.ts.flagAsCatDescription }}</template></FormSwitch> - <FormSwitch v-model="profile.isBot" class="_formBlock">{{ i18n.ts.flagAsBot }}<template #caption>{{ i18n.ts.flagAsBotDescription }}</template></FormSwitch> + <div class="_autoGap"> + <FormSwitch v-model="profile.isCat">{{ i18n.ts.flagAsCat }}<template #caption>{{ i18n.ts.flagAsCatDescription }}</template></FormSwitch> + <FormSwitch v-model="profile.isBot">{{ i18n.ts.flagAsBot }}<template #caption>{{ i18n.ts.flagAsBotDescription }}</template></FormSwitch> </div> </FormFolder> - <FormSwitch v-model="profile.showTimelineReplies" class="_formBlock">{{ i18n.ts.flagShowTimelineReplies }}<template #caption>{{ i18n.ts.flagShowTimelineRepliesDescription }} {{ i18n.ts.reflectMayTakeTime }}</template></FormSwitch> + <FormSwitch v-model="profile.showTimelineReplies">{{ i18n.ts.flagShowTimelineReplies }}<template #caption>{{ i18n.ts.flagShowTimelineRepliesDescription }} {{ i18n.ts.reflectMayTakeTime }}</template></FormSwitch> </div> </template> diff --git a/packages/frontend/src/pages/settings/reaction.vue b/packages/frontend/src/pages/settings/reaction.vue index 2748cd7d4e..7f2e930785 100644 --- a/packages/frontend/src/pages/settings/reaction.vue +++ b/packages/frontend/src/pages/settings/reaction.vue @@ -1,6 +1,6 @@ <template> -<div class="_formRoot"> - <FromSlot class="_formBlock"> +<div class="_autoGap"> + <FromSlot> <template #label>{{ i18n.ts.reactionSettingDescription }}</template> <div v-panel style="border-radius: 6px;"> <Sortable v-model="reactions" class="zoaiodol" :item-key="item => item" :animation="150" :delay="100" :delay-on-touch-only="true"> @@ -17,13 +17,13 @@ <template #caption>{{ i18n.ts.reactionSettingDescription2 }} <button class="_textButton" @click="preview">{{ i18n.ts.preview }}</button></template> </FromSlot> - <FormRadios v-model="reactionPickerSize" class="_formBlock"> + <FormRadios v-model="reactionPickerSize"> <template #label>{{ i18n.ts.size }}</template> <option :value="1">{{ i18n.ts.small }}</option> <option :value="2">{{ i18n.ts.medium }}</option> <option :value="3">{{ i18n.ts.large }}</option> </FormRadios> - <FormRadios v-model="reactionPickerWidth" class="_formBlock"> + <FormRadios v-model="reactionPickerWidth"> <template #label>{{ i18n.ts.numberOfColumn }}</template> <option :value="1">5</option> <option :value="2">6</option> @@ -31,7 +31,7 @@ <option :value="4">8</option> <option :value="5">9</option> </FormRadios> - <FormRadios v-model="reactionPickerHeight" class="_formBlock"> + <FormRadios v-model="reactionPickerHeight"> <template #label>{{ i18n.ts.height }}</template> <option :value="1">{{ i18n.ts.small }}</option> <option :value="2">{{ i18n.ts.medium }}</option> @@ -39,7 +39,7 @@ <option :value="4">{{ i18n.ts.large }}+</option> </FormRadios> - <FormSwitch v-model="reactionPickerUseDrawerForMobile" class="_formBlock"> + <FormSwitch v-model="reactionPickerUseDrawerForMobile"> {{ i18n.ts.useDrawerReactionPickerForMobile }} <template #caption>{{ i18n.ts.needReloadToApply }}</template> </FormSwitch> diff --git a/packages/frontend/src/pages/settings/security.vue b/packages/frontend/src/pages/settings/security.vue index 33f49eb3ef..e3b63ea1a9 100644 --- a/packages/frontend/src/pages/settings/security.vue +++ b/packages/frontend/src/pages/settings/security.vue @@ -1,5 +1,5 @@ <template> -<div class="_formRoot"> +<div class="_autoGap"> <FormSection> <template #label>{{ i18n.ts.password }}</template> <FormButton primary @click="change()">{{ i18n.ts.changePassword }}</FormButton> diff --git a/packages/frontend/src/pages/settings/sounds.sound.vue b/packages/frontend/src/pages/settings/sounds.sound.vue index 62627c6333..122b0d499f 100644 --- a/packages/frontend/src/pages/settings/sounds.sound.vue +++ b/packages/frontend/src/pages/settings/sounds.sound.vue @@ -1,10 +1,10 @@ <template> -<div class="_formRoot"> +<div class="_autoGap"> <FormSelect v-model="type"> <template #label>{{ i18n.ts.sound }}</template> <option v-for="x in soundsTypes" :key="x" :value="x">{{ x == null ? i18n.ts.none : x }}</option> </FormSelect> - <FormRange v-model="volume" :min="0" :max="1" :step="0.05" :text-converter="(v) => `${Math.floor(v * 100)}%`" class="_formBlock"> + <FormRange v-model="volume" :min="0" :max="1" :step="0.05" :text-converter="(v) => `${Math.floor(v * 100)}%`"> <template #label>{{ i18n.ts.volume }}</template> </FormRange> diff --git a/packages/frontend/src/pages/settings/sounds.vue b/packages/frontend/src/pages/settings/sounds.vue index ef60b2c3c9..4d7ea95be4 100644 --- a/packages/frontend/src/pages/settings/sounds.vue +++ b/packages/frontend/src/pages/settings/sounds.vue @@ -1,20 +1,22 @@ <template> -<div class="_formRoot"> - <FormRange v-model="masterVolume" :min="0" :max="1" :step="0.05" :text-converter="(v) => `${Math.floor(v * 100)}%`" class="_formBlock"> +<div class="_autoGap"> + <FormRange v-model="masterVolume" :min="0" :max="1" :step="0.05" :text-converter="(v) => `${Math.floor(v * 100)}%`"> <template #label>{{ i18n.ts.masterVolume }}</template> </FormRange> <FormSection> <template #label>{{ i18n.ts.sounds }}</template> - <FormFolder v-for="type in Object.keys(sounds)" :key="type" style="margin-bottom: 8px;"> - <template #label>{{ $t('_sfx.' + type) }}</template> - <template #suffix>{{ sounds[type].type ?? i18n.ts.none }}</template> + <div class="_autoGap_half"> + <FormFolder v-for="type in Object.keys(sounds)" :key="type"> + <template #label>{{ $t('_sfx.' + type) }}</template> + <template #suffix>{{ sounds[type].type ?? i18n.ts.none }}</template> - <XSound :type="sounds[type].type" :volume="sounds[type].volume" @update="(res) => updated(type, res)"/> - </FormFolder> + <XSound :type="sounds[type].type" :volume="sounds[type].volume" @update="(res) => updated(type, res)"/> + </FormFolder> + </div> </FormSection> - <FormButton danger class="_formBlock" @click="reset()"><i class="ti ti-reload"></i> {{ i18n.ts.default }}</FormButton> + <FormButton danger @click="reset()"><i class="ti ti-reload"></i> {{ i18n.ts.default }}</FormButton> </div> </template> diff --git a/packages/frontend/src/pages/settings/statusbar.statusbar.vue b/packages/frontend/src/pages/settings/statusbar.statusbar.vue index 608222386e..328a7f8e10 100644 --- a/packages/frontend/src/pages/settings/statusbar.statusbar.vue +++ b/packages/frontend/src/pages/settings/statusbar.statusbar.vue @@ -1,21 +1,21 @@ <template> -<div class="_formRoot"> - <FormSelect v-model="statusbar.type" placeholder="Please select" class="_formBlock"> +<div class="_autoGap"> + <FormSelect v-model="statusbar.type" placeholder="Please select"> <template #label>{{ i18n.ts.type }}</template> <option value="rss">RSS</option> <option value="federation">Federation</option> <option value="userList">User list timeline</option> </FormSelect> - <MkInput v-model="statusbar.name" manual-save class="_formBlock"> + <MkInput v-model="statusbar.name" manual-save> <template #label>{{ i18n.ts.label }}</template> </MkInput> - <MkSwitch v-model="statusbar.black" class="_formBlock"> + <MkSwitch v-model="statusbar.black"> <template #label>Black</template> </MkSwitch> - <FormRadios v-model="statusbar.size" class="_formBlock"> + <FormRadios v-model="statusbar.size"> <template #label>{{ i18n.ts.size }}</template> <option value="verySmall">{{ i18n.ts.small }}+</option> <option value="small">{{ i18n.ts.small }}</option> @@ -25,51 +25,51 @@ </FormRadios> <template v-if="statusbar.type === 'rss'"> - <MkInput v-model="statusbar.props.url" manual-save class="_formBlock" type="url"> + <MkInput v-model="statusbar.props.url" manual-save type="url"> <template #label>URL</template> </MkInput> - <MkSwitch v-model="statusbar.props.shuffle" class="_formBlock"> + <MkSwitch v-model="statusbar.props.shuffle"> <template #label>{{ i18n.ts.shuffle }}</template> </MkSwitch> - <MkInput v-model="statusbar.props.refreshIntervalSec" manual-save class="_formBlock" type="number"> + <MkInput v-model="statusbar.props.refreshIntervalSec" manual-save type="number"> <template #label>{{ i18n.ts.refreshInterval }}</template> </MkInput> - <FormRange v-model="statusbar.props.marqueeDuration" :min="5" :max="150" :step="1" class="_formBlock"> + <FormRange v-model="statusbar.props.marqueeDuration" :min="5" :max="150" :step="1"> <template #label>{{ i18n.ts.speed }}</template> <template #caption>{{ i18n.ts.fast }} <-> {{ i18n.ts.slow }}</template> </FormRange> - <MkSwitch v-model="statusbar.props.marqueeReverse" class="_formBlock"> + <MkSwitch v-model="statusbar.props.marqueeReverse"> <template #label>{{ i18n.ts.reverse }}</template> </MkSwitch> </template> <template v-else-if="statusbar.type === 'federation'"> - <MkInput v-model="statusbar.props.refreshIntervalSec" manual-save class="_formBlock" type="number"> + <MkInput v-model="statusbar.props.refreshIntervalSec" manual-save type="number"> <template #label>{{ i18n.ts.refreshInterval }}</template> </MkInput> - <FormRange v-model="statusbar.props.marqueeDuration" :min="5" :max="150" :step="1" class="_formBlock"> + <FormRange v-model="statusbar.props.marqueeDuration" :min="5" :max="150" :step="1"> <template #label>{{ i18n.ts.speed }}</template> <template #caption>{{ i18n.ts.fast }} <-> {{ i18n.ts.slow }}</template> </FormRange> - <MkSwitch v-model="statusbar.props.marqueeReverse" class="_formBlock"> + <MkSwitch v-model="statusbar.props.marqueeReverse"> <template #label>{{ i18n.ts.reverse }}</template> </MkSwitch> - <MkSwitch v-model="statusbar.props.colored" class="_formBlock"> + <MkSwitch v-model="statusbar.props.colored"> <template #label>{{ i18n.ts.colored }}</template> </MkSwitch> </template> <template v-else-if="statusbar.type === 'userList' && userLists != null"> - <FormSelect v-model="statusbar.props.userListId" class="_formBlock"> + <FormSelect v-model="statusbar.props.userListId"> <template #label>{{ i18n.ts.userList }}</template> <option v-for="list in userLists" :value="list.id">{{ list.name }}</option> </FormSelect> - <MkInput v-model="statusbar.props.refreshIntervalSec" manual-save class="_formBlock" type="number"> + <MkInput v-model="statusbar.props.refreshIntervalSec" manual-save type="number"> <template #label>{{ i18n.ts.refreshInterval }}</template> </MkInput> - <FormRange v-model="statusbar.props.marqueeDuration" :min="5" :max="150" :step="1" class="_formBlock"> + <FormRange v-model="statusbar.props.marqueeDuration" :min="5" :max="150" :step="1"> <template #label>{{ i18n.ts.speed }}</template> <template #caption>{{ i18n.ts.fast }} <-> {{ i18n.ts.slow }}</template> </FormRange> - <MkSwitch v-model="statusbar.props.marqueeReverse" class="_formBlock"> + <MkSwitch v-model="statusbar.props.marqueeReverse"> <template #label>{{ i18n.ts.reverse }}</template> </MkSwitch> </template> diff --git a/packages/frontend/src/pages/settings/statusbar.vue b/packages/frontend/src/pages/settings/statusbar.vue index 86c69fa2c3..1363c79748 100644 --- a/packages/frontend/src/pages/settings/statusbar.vue +++ b/packages/frontend/src/pages/settings/statusbar.vue @@ -1,6 +1,6 @@ <template> -<div class="_formRoot"> - <FormFolder v-for="x in statusbars" :key="x.id" class="_formBlock"> +<div class="_autoGap"> + <FormFolder v-for="x in statusbars" :key="x.id"> <template #label>{{ x.type ?? i18n.ts.notSet }}</template> <template #suffix>{{ x.name }}</template> <XStatusbar :_id="x.id" :user-lists="userLists"/> diff --git a/packages/frontend/src/pages/settings/theme.install.vue b/packages/frontend/src/pages/settings/theme.install.vue index 52a436e18d..e08a03d0c9 100644 --- a/packages/frontend/src/pages/settings/theme.install.vue +++ b/packages/frontend/src/pages/settings/theme.install.vue @@ -1,10 +1,10 @@ <template> -<div class="_formRoot"> - <FormTextarea v-model="installThemeCode" class="_formBlock"> +<div class="_autoGap"> + <FormTextarea v-model="installThemeCode"> <template #label>{{ i18n.ts._theme.code }}</template> </FormTextarea> - <div class="_formBlock" style="display: flex; gap: var(--margin); flex-wrap: wrap;"> + <div style="display: flex; gap: var(--margin); flex-wrap: wrap;"> <FormButton :disabled="installThemeCode == null" inline @click="() => preview(installThemeCode)"><i class="ti ti-eye"></i> {{ i18n.ts.preview }}</FormButton> <FormButton :disabled="installThemeCode == null" primary inline @click="() => install(installThemeCode)"><i class="ti ti-check"></i> {{ i18n.ts.install }}</FormButton> </div> diff --git a/packages/frontend/src/pages/settings/theme.manage.vue b/packages/frontend/src/pages/settings/theme.manage.vue index 409f0af650..afbc40a18d 100644 --- a/packages/frontend/src/pages/settings/theme.manage.vue +++ b/packages/frontend/src/pages/settings/theme.manage.vue @@ -1,6 +1,6 @@ <template> -<div class="_formRoot"> - <FormSelect v-model="selectedThemeId" class="_formBlock"> +<div class="_autoGap"> + <FormSelect v-model="selectedThemeId"> <template #label>{{ i18n.ts.theme }}</template> <optgroup :label="i18n.ts._theme.installedThemes"> <option v-for="x in installedThemes" :key="x.id" :value="x.id">{{ x.name }}</option> @@ -10,17 +10,17 @@ </optgroup> </FormSelect> <template v-if="selectedTheme"> - <FormInput readonly :model-value="selectedTheme.author" class="_formBlock"> + <FormInput readonly :model-value="selectedTheme.author"> <template #label>{{ i18n.ts.author }}</template> </FormInput> - <FormTextarea v-if="selectedTheme.desc" readonly :model-value="selectedTheme.desc" class="_formBlock"> + <FormTextarea v-if="selectedTheme.desc" readonly :model-value="selectedTheme.desc"> <template #label>{{ i18n.ts._theme.description }}</template> </FormTextarea> - <FormTextarea readonly tall :model-value="selectedThemeCode" class="_formBlock"> + <FormTextarea readonly tall :model-value="selectedThemeCode"> <template #label>{{ i18n.ts._theme.code }}</template> <template #caption><button class="_textButton" @click="copyThemeCode()">{{ i18n.ts.copy }}</button></template> </FormTextarea> - <FormButton v-if="!builtinThemes.some(t => t.id == selectedTheme.id)" class="_formBlock" danger @click="uninstall()"><i class="ti ti-trash"></i> {{ i18n.ts.uninstall }}</FormButton> + <FormButton v-if="!builtinThemes.some(t => t.id == selectedTheme.id)" danger @click="uninstall()"><i class="ti ti-trash"></i> {{ i18n.ts.uninstall }}</FormButton> </template> </div> </template> diff --git a/packages/frontend/src/pages/settings/theme.vue b/packages/frontend/src/pages/settings/theme.vue index f37c213b06..d2fc0980d4 100644 --- a/packages/frontend/src/pages/settings/theme.vue +++ b/packages/frontend/src/pages/settings/theme.vue @@ -1,6 +1,6 @@ <template> -<div class="_formRoot rsljpzjq"> - <div v-adaptive-border class="rfqxtzch _panel _formBlock"> +<div class="_autoGap rsljpzjq"> + <div v-adaptive-border class="rfqxtzch _panel"> <div class="toggle"> <div class="toggleWrapper"> <input id="dn" v-model="darkMode" type="checkbox" class="dn"/> @@ -26,7 +26,7 @@ </div> </div> - <div class="selects _formBlock"> + <div class="selects"> <FormSelect v-model="lightThemeId" large class="select"> <template #label>{{ i18n.ts.themeForLightMode }}</template> <template #prefix><i class="ti ti-sun"></i></template> @@ -60,8 +60,8 @@ </div> </FormSection> - <FormButton v-if="wallpaper == null" class="_formBlock" @click="setWallpaper">{{ i18n.ts.setWallpaper }}</FormButton> - <FormButton v-else class="_formBlock" @click="wallpaper = null">{{ i18n.ts.removeWallpaper }}</FormButton> + <FormButton v-if="wallpaper == null" @click="setWallpaper">{{ i18n.ts.setWallpaper }}</FormButton> + <FormButton v-else @click="wallpaper = null">{{ i18n.ts.removeWallpaper }}</FormButton> </div> </template> diff --git a/packages/frontend/src/pages/settings/webhook.edit.vue b/packages/frontend/src/pages/settings/webhook.edit.vue index c8ec1ea586..439a27ed39 100644 --- a/packages/frontend/src/pages/settings/webhook.edit.vue +++ b/packages/frontend/src/pages/settings/webhook.edit.vue @@ -1,14 +1,14 @@ <template> -<div class="_formRoot"> - <FormInput v-model="name" class="_formBlock"> +<div class="_autoGap"> + <FormInput v-model="name"> <template #label>Name</template> </FormInput> - <FormInput v-model="url" type="url" class="_formBlock"> + <FormInput v-model="url" type="url"> <template #label>URL</template> </FormInput> - <FormInput v-model="secret" class="_formBlock"> + <FormInput v-model="secret"> <template #prefix><i class="ti ti-lock"></i></template> <template #label>Secret</template> </FormInput> @@ -16,18 +16,20 @@ <FormSection> <template #label>Events</template> - <FormSwitch v-model="event_follow" class="_formBlock">Follow</FormSwitch> - <FormSwitch v-model="event_followed" class="_formBlock">Followed</FormSwitch> - <FormSwitch v-model="event_note" class="_formBlock">Note</FormSwitch> - <FormSwitch v-model="event_reply" class="_formBlock">Reply</FormSwitch> - <FormSwitch v-model="event_renote" class="_formBlock">Renote</FormSwitch> - <FormSwitch v-model="event_reaction" class="_formBlock">Reaction</FormSwitch> - <FormSwitch v-model="event_mention" class="_formBlock">Mention</FormSwitch> + <div class="_autoGap_half"> + <FormSwitch v-model="event_follow">Follow</FormSwitch> + <FormSwitch v-model="event_followed">Followed</FormSwitch> + <FormSwitch v-model="event_note">Note</FormSwitch> + <FormSwitch v-model="event_reply">Reply</FormSwitch> + <FormSwitch v-model="event_renote">Renote</FormSwitch> + <FormSwitch v-model="event_reaction">Reaction</FormSwitch> + <FormSwitch v-model="event_mention">Mention</FormSwitch> + </div> </FormSection> - <FormSwitch v-model="active" class="_formBlock">Active</FormSwitch> + <FormSwitch v-model="active">Active</FormSwitch> - <div class="_formBlock" style="display: flex; gap: var(--margin); flex-wrap: wrap;"> + <div style="display: flex; gap: var(--margin); flex-wrap: wrap;"> <FormButton primary inline @click="save"><i class="ti ti-check"></i> {{ i18n.ts.save }}</FormButton> </div> </div> diff --git a/packages/frontend/src/pages/settings/webhook.new.vue b/packages/frontend/src/pages/settings/webhook.new.vue index 00a547da69..3ef19e3f54 100644 --- a/packages/frontend/src/pages/settings/webhook.new.vue +++ b/packages/frontend/src/pages/settings/webhook.new.vue @@ -1,14 +1,14 @@ <template> -<div class="_formRoot"> - <FormInput v-model="name" class="_formBlock"> +<div class="_autoGap"> + <FormInput v-model="name"> <template #label>Name</template> </FormInput> - <FormInput v-model="url" type="url" class="_formBlock"> + <FormInput v-model="url" type="url"> <template #label>URL</template> </FormInput> - <FormInput v-model="secret" class="_formBlock"> + <FormInput v-model="secret"> <template #prefix><i class="ti ti-lock"></i></template> <template #label>Secret</template> </FormInput> @@ -16,16 +16,18 @@ <FormSection> <template #label>Events</template> - <FormSwitch v-model="event_follow" class="_formBlock">Follow</FormSwitch> - <FormSwitch v-model="event_followed" class="_formBlock">Followed</FormSwitch> - <FormSwitch v-model="event_note" class="_formBlock">Note</FormSwitch> - <FormSwitch v-model="event_reply" class="_formBlock">Reply</FormSwitch> - <FormSwitch v-model="event_renote" class="_formBlock">Renote</FormSwitch> - <FormSwitch v-model="event_reaction" class="_formBlock">Reaction</FormSwitch> - <FormSwitch v-model="event_mention" class="_formBlock">Mention</FormSwitch> + <div class="_autoGap_half"> + <FormSwitch v-model="event_follow">Follow</FormSwitch> + <FormSwitch v-model="event_followed">Followed</FormSwitch> + <FormSwitch v-model="event_note">Note</FormSwitch> + <FormSwitch v-model="event_reply">Reply</FormSwitch> + <FormSwitch v-model="event_renote">Renote</FormSwitch> + <FormSwitch v-model="event_reaction">Reaction</FormSwitch> + <FormSwitch v-model="event_mention">Mention</FormSwitch> + </div> </FormSection> - <div class="_formBlock" style="display: flex; gap: var(--margin); flex-wrap: wrap;"> + <div style="display: flex; gap: var(--margin); flex-wrap: wrap;"> <FormButton primary inline @click="create"><i class="ti ti-check"></i> {{ i18n.ts.create }}</FormButton> </div> </div> diff --git a/packages/frontend/src/pages/settings/webhook.vue b/packages/frontend/src/pages/settings/webhook.vue index 9be23ee4f0..e193f783f9 100644 --- a/packages/frontend/src/pages/settings/webhook.vue +++ b/packages/frontend/src/pages/settings/webhook.vue @@ -1,15 +1,13 @@ <template> -<div class="_formRoot"> - <FormSection> - <FormLink :to="`/settings/webhook/new`"> - Create webhook - </FormLink> - </FormSection> - +<div class="_autoGap"> + <FormLink :to="`/settings/webhook/new`"> + Create webhook + </FormLink> + <FormSection> <MkPagination :pagination="pagination"> <template #default="{items}"> - <FormLink v-for="webhook in items" :key="webhook.id" :to="`/settings/webhook/edit/${webhook.id}`" class="_formBlock"> + <FormLink v-for="webhook in items" :key="webhook.id" :to="`/settings/webhook/edit/${webhook.id}`" class="_margin"> <template #icon> <i v-if="webhook.active === false" class="ti ti-player-pause"></i> <i v-else-if="webhook.latestStatus === null" class="ti ti-circle"></i> diff --git a/packages/frontend/src/pages/settings/word-mute.vue b/packages/frontend/src/pages/settings/word-mute.vue index 6961d8151d..3cd28f6015 100644 --- a/packages/frontend/src/pages/settings/word-mute.vue +++ b/packages/frontend/src/pages/settings/word-mute.vue @@ -1,24 +1,24 @@ <template> -<div class="_formRoot"> - <MkTab v-model="tab" class="_formBlock"> +<div class="_autoGap"> + <MkTab v-model="tab"> <option value="soft">{{ i18n.ts._wordMute.soft }}</option> <option value="hard">{{ i18n.ts._wordMute.hard }}</option> </MkTab> - <div class="_formBlock"> - <div v-show="tab === 'soft'"> - <MkInfo class="_formBlock">{{ i18n.ts._wordMute.softDescription }}</MkInfo> - <FormTextarea v-model="softMutedWords" class="_formBlock"> + <div> + <div v-show="tab === 'soft'" class="_autoGap"> + <MkInfo>{{ i18n.ts._wordMute.softDescription }}</MkInfo> + <FormTextarea v-model="softMutedWords"> <span>{{ i18n.ts._wordMute.muteWords }}</span> <template #caption>{{ i18n.ts._wordMute.muteWordsDescription }}<br>{{ i18n.ts._wordMute.muteWordsDescription2 }}</template> </FormTextarea> </div> - <div v-show="tab === 'hard'"> - <MkInfo class="_formBlock">{{ i18n.ts._wordMute.hardDescription }} {{ i18n.ts.reflectMayTakeTime }}</MkInfo> - <FormTextarea v-model="hardMutedWords" class="_formBlock"> + <div v-show="tab === 'hard'" class="_autoGap"> + <MkInfo>{{ i18n.ts._wordMute.hardDescription }} {{ i18n.ts.reflectMayTakeTime }}</MkInfo> + <FormTextarea v-model="hardMutedWords"> <span>{{ i18n.ts._wordMute.muteWords }}</span> <template #caption>{{ i18n.ts._wordMute.muteWordsDescription }}<br>{{ i18n.ts._wordMute.muteWordsDescription2 }}</template> </FormTextarea> - <MkKeyValue v-if="hardWordMutedNotesCount != null" class="_formBlock"> + <MkKeyValue v-if="hardWordMutedNotesCount != null"> <template #key>{{ i18n.ts._wordMute.mutedNotes }}</template> <template #value>{{ number(hardWordMutedNotesCount) }}</template> </MkKeyValue> diff --git a/packages/frontend/src/pages/theme-editor.vue b/packages/frontend/src/pages/theme-editor.vue index d8ff170ca2..4b058a5366 100644 --- a/packages/frontend/src/pages/theme-editor.vue +++ b/packages/frontend/src/pages/theme-editor.vue @@ -2,8 +2,8 @@ <MkStickyContainer> <template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template> <MkSpacer :content-max="800" :margin-min="16" :margin-max="32"> - <div class="cwepdizn _formRoot"> - <FormFolder :default-open="true" class="_formBlock"> + <div class="cwepdizn _autoGap"> + <FormFolder :default-open="true"> <template #label>{{ i18n.ts.backgroundColor }}</template> <div class="cwepdizn-colors"> <div class="row"> @@ -19,7 +19,7 @@ </div> </FormFolder> - <FormFolder :default-open="true" class="_formBlock"> + <FormFolder :default-open="true"> <template #label>{{ i18n.ts.accentColor }}</template> <div class="cwepdizn-colors"> <div class="row"> @@ -30,7 +30,7 @@ </div> </FormFolder> - <FormFolder :default-open="true" class="_formBlock"> + <FormFolder :default-open="true"> <template #label>{{ i18n.ts.textColor }}</template> <div class="cwepdizn-colors"> <div class="row"> @@ -41,22 +41,22 @@ </div> </FormFolder> - <FormFolder :default-open="false" class="_formBlock"> + <FormFolder :default-open="false"> <template #icon><i class="ti ti-code"></i></template> <template #label>{{ i18n.ts.editCode }}</template> - <div class="_formRoot"> - <FormTextarea v-model="themeCode" tall class="_formBlock"> + <div class="_autoGap"> + <FormTextarea v-model="themeCode" tall> <template #label>{{ i18n.ts._theme.code }}</template> </FormTextarea> - <FormButton primary class="_formBlock" @click="applyThemeCode">{{ i18n.ts.apply }}</FormButton> + <FormButton primary @click="applyThemeCode">{{ i18n.ts.apply }}</FormButton> </div> </FormFolder> - <FormFolder :default-open="false" class="_formBlock"> + <FormFolder :default-open="false"> <template #label>{{ i18n.ts.addDescription }}</template> - <div class="_formRoot"> + <div class="_autoGap"> <FormTextarea v-model="description"> <template #label>{{ i18n.ts._theme.description }}</template> </FormTextarea> diff --git a/packages/frontend/src/pages/user-info.vue b/packages/frontend/src/pages/user-info.vue index addc8db9e6..d879c95dd7 100644 --- a/packages/frontend/src/pages/user-info.vue +++ b/packages/frontend/src/pages/user-info.vue @@ -3,8 +3,8 @@ <template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template> <MkSpacer :content-max="600" :margin-min="16" :margin-max="32"> <FormSuspense :p="init"> - <div v-if="tab === 'overview'" class="_formRoot"> - <div class="_formBlock aeakzknw"> + <div v-if="tab === 'overview'" class="_autoGap"> + <div class="aeakzknw"> <MkAvatar class="avatar" :user="user" :show-indicator="true"/> <div class="body"> <span class="name"><MkUserName class="name" :user="user"/></span> @@ -17,36 +17,36 @@ </div> </div> - <MkInfo v-if="user.username.includes('.')" class="_formBlock">{{ i18n.ts.isSystemAccount }}</MkInfo> + <MkInfo v-if="user.username.includes('.')">{{ i18n.ts.isSystemAccount }}</MkInfo> - <div v-if="user.url" class="_formLinksGrid _formBlock"> + <div v-if="user.url" class="_formLinksGrid"> <FormLink :to="userPage(user)">Profile</FormLink> <FormLink :to="user.url" :external="true">Profile (remote)</FormLink> </div> - <FormLink v-else class="_formBlock" :to="userPage(user)">Profile</FormLink> + <FormLink v-else :to="userPage(user)">Profile</FormLink> - <FormLink v-if="user.host" class="_formBlock" :to="`/instance-info/${user.host}`">{{ i18n.ts.instanceInfo }}</FormLink> + <FormLink v-if="user.host" :to="`/instance-info/${user.host}`">{{ i18n.ts.instanceInfo }}</FormLink> - <div class="_formBlock"> - <MkKeyValue :copy="user.id" oneline style="margin: 1em 0;"> + <div style="display: flex; flex-direction: column; gap: 1em;"> + <MkKeyValue :copy="user.id" oneline> <template #key>ID</template> <template #value><span class="_monospace">{{ user.id }}</span></template> </MkKeyValue> <!-- 要る? - <MkKeyValue v-if="ips.length > 0" :copy="user.id" oneline style="margin: 1em 0;"> + <MkKeyValue v-if="ips.length > 0" :copy="user.id" oneline> <template #key>IP (recent)</template> <template #value><span class="_monospace">{{ ips[0].ip }}</span></template> </MkKeyValue> --> - <MkKeyValue oneline style="margin: 1em 0;"> + <MkKeyValue oneline> <template #key>{{ i18n.ts.createdAt }}</template> <template #value><span class="_monospace"><MkTime :time="user.createdAt" :mode="'detail'"/></span></template> </MkKeyValue> - <MkKeyValue v-if="info" oneline style="margin: 1em 0;"> + <MkKeyValue v-if="info" oneline> <template #key>{{ i18n.ts.lastActiveDate }}</template> <template #value><span class="_monospace"><MkTime :time="info.lastActiveDate" :mode="'detail'"/></span></template> </MkKeyValue> - <MkKeyValue v-if="info" oneline style="margin: 1em 0;"> + <MkKeyValue v-if="info" oneline> <template #key>{{ i18n.ts.email }}</template> <template #value><span class="_monospace">{{ info.email }}</span></template> </MkKeyValue> @@ -55,48 +55,50 @@ <FormSection> <template #label>ActivityPub</template> - <div class="_formBlock"> - <MkKeyValue v-if="user.host" oneline style="margin: 1em 0;"> - <template #key>{{ i18n.ts.instanceInfo }}</template> - <template #value><MkA :to="`/instance-info/${user.host}`" class="_link">{{ user.host }} <i class="ti ti-chevron-right"></i></MkA></template> - </MkKeyValue> - <MkKeyValue v-else oneline style="margin: 1em 0;"> - <template #key>{{ i18n.ts.instanceInfo }}</template> - <template #value>(Local user)</template> - </MkKeyValue> - <MkKeyValue oneline style="margin: 1em 0;"> - <template #key>{{ i18n.ts.updatedAt }}</template> - <template #value><MkTime v-if="user.lastFetchedAt" mode="detail" :time="user.lastFetchedAt"/><span v-else>N/A</span></template> - </MkKeyValue> - <MkKeyValue v-if="ap" oneline style="margin: 1em 0;"> - <template #key>Type</template> - <template #value><span class="_monospace">{{ ap.type }}</span></template> - </MkKeyValue> + <div class="_autoGap"> + <div style="display: flex; flex-direction: column; gap: 1em;"> + <MkKeyValue v-if="user.host" oneline> + <template #key>{{ i18n.ts.instanceInfo }}</template> + <template #value><MkA :to="`/instance-info/${user.host}`" class="_link">{{ user.host }} <i class="ti ti-chevron-right"></i></MkA></template> + </MkKeyValue> + <MkKeyValue v-else oneline> + <template #key>{{ i18n.ts.instanceInfo }}</template> + <template #value>(Local user)</template> + </MkKeyValue> + <MkKeyValue oneline> + <template #key>{{ i18n.ts.updatedAt }}</template> + <template #value><MkTime v-if="user.lastFetchedAt" mode="detail" :time="user.lastFetchedAt"/><span v-else>N/A</span></template> + </MkKeyValue> + <MkKeyValue v-if="ap" oneline> + <template #key>Type</template> + <template #value><span class="_monospace">{{ ap.type }}</span></template> + </MkKeyValue> + </div> + + <FormButton v-if="user.host != null" @click="updateRemoteUser"><i class="ti ti-refresh"></i> {{ i18n.ts.updateRemoteUser }}</FormButton> + + <FormFolder> + <template #label>Raw</template> + + <MkObjectView v-if="ap" tall :value="ap"> + </MkObjectView> + </FormFolder> </div> - - <FormButton v-if="user.host != null" class="_formBlock" @click="updateRemoteUser"><i class="ti ti-refresh"></i> {{ i18n.ts.updateRemoteUser }}</FormButton> - - <FormFolder class="_formBlock"> - <template #label>Raw</template> - - <MkObjectView v-if="ap" tall :value="ap"> - </MkObjectView> - </FormFolder> </FormSection> </div> - <div v-else-if="tab === 'moderation'" class="_formRoot"> - <FormSwitch v-if="user.host == null && $i.isAdmin && (moderator || !user.isAdmin)" v-model="moderator" class="_formBlock" @update:model-value="toggleModerator">{{ i18n.ts.moderator }}</FormSwitch> - <FormSwitch v-model="silenced" class="_formBlock" @update:model-value="toggleSilence">{{ i18n.ts.silence }}</FormSwitch> - <FormSwitch v-model="suspended" class="_formBlock" @update:model-value="toggleSuspend">{{ i18n.ts.suspend }}</FormSwitch> + <div v-else-if="tab === 'moderation'" class="_autoGap"> + <FormSwitch v-if="user.host == null && $i.isAdmin && (moderator || !user.isAdmin)" v-model="moderator" @update:model-value="toggleModerator">{{ i18n.ts.moderator }}</FormSwitch> + <FormSwitch v-model="silenced" @update:model-value="toggleSilence">{{ i18n.ts.silence }}</FormSwitch> + <FormSwitch v-model="suspended" @update:model-value="toggleSuspend">{{ i18n.ts.suspend }}</FormSwitch> {{ i18n.ts.reflectMayTakeTime }} - <div class="_formBlock"> + <div> <FormButton v-if="user.host == null && iAmModerator" inline style="margin-right: 8px;" @click="resetPassword"><i class="ti ti-key"></i> {{ i18n.ts.resetPassword }}</FormButton> <FormButton v-if="$i.isAdmin" inline danger @click="deleteAccount">{{ i18n.ts.deleteAccount }}</FormButton> </div> - <FormTextarea v-model="moderationNote" manual-save class="_formBlock"> + <FormTextarea v-model="moderationNote" manual-save> <template #label>Moderation note</template> </FormTextarea> - <FormFolder class="_formBlock"> + <FormFolder> <template #label>IP</template> <MkInfo v-if="!iAmAdmin" warn>{{ i18n.ts.requireAdminForView }}</MkInfo> <MkInfo v-else>The date is the IP address was first acknowledged.</MkInfo> @@ -107,7 +109,7 @@ </div> </template> </FormFolder> - <FormFolder class="_formBlock"> + <FormFolder> <template #label>{{ i18n.ts.files }}</template> <MkFileListForAdmin :pagination="filesPagination" view-mode="grid"/> @@ -124,7 +126,7 @@ </FormInput> </FormSection> </div> - <div v-else-if="tab === 'chart'" class="_formRoot"> + <div v-else-if="tab === 'chart'" class="_autoGap"> <div class="cmhjzshm"> <div class="selects"> <MkSelect v-model="chartSrc" style="margin: 0 10px 0 0; flex: 1;"> @@ -139,7 +141,7 @@ </div> </div> </div> - <div v-else-if="tab === 'raw'" class="_formRoot"> + <div v-else-if="tab === 'raw'" class="_autoGap"> <MkObjectView v-if="info && $i.isAdmin" tall :value="info"> </MkObjectView> diff --git a/packages/frontend/src/pages/user/clips.vue b/packages/frontend/src/pages/user/clips.vue index 8c71aacb0c..95f8cbc296 100644 --- a/packages/frontend/src/pages/user/clips.vue +++ b/packages/frontend/src/pages/user/clips.vue @@ -2,7 +2,7 @@ <MkSpacer :content-max="700"> <div class="pages-user-clips"> <MkPagination v-slot="{items}" ref="list" :pagination="pagination" class="list"> - <MkA v-for="item in items" :key="item.id" :to="`/clips/${item.id}`" class="item _panel _gap"> + <MkA v-for="item in items" :key="item.id" :to="`/clips/${item.id}`" class="item _panel _margin"> <b>{{ item.name }}</b> <div v-if="item.description" class="description">{{ item.description }}</div> </MkA> diff --git a/packages/frontend/src/pages/user/home.vue b/packages/frontend/src/pages/user/home.vue index 4a92074d93..40da7f9d57 100644 --- a/packages/frontend/src/pages/user/home.vue +++ b/packages/frontend/src/pages/user/home.vue @@ -86,7 +86,7 @@ </div> <div class="contents"> - <div v-if="user.pinnedNotes.length > 0" class="_gap"> + <div v-if="user.pinnedNotes.length > 0" class="_margin"> <XNote v-for="note in user.pinnedNotes" :key="note.id" class="note _block" :note="note" :pinned="true"/> </div> <MkInfo v-else-if="$i && $i.id === user.id">{{ i18n.ts.userPagePinTip }}</MkInfo> diff --git a/packages/frontend/src/pages/user/pages.vue b/packages/frontend/src/pages/user/pages.vue index 7833d6c42c..7ea1d75f43 100644 --- a/packages/frontend/src/pages/user/pages.vue +++ b/packages/frontend/src/pages/user/pages.vue @@ -1,7 +1,7 @@ <template> <MkSpacer :content-max="700"> <MkPagination v-slot="{items}" ref="list" :pagination="pagination"> - <MkPagePreview v-for="page in items" :key="page.id" :page="page" class="_gap"/> + <MkPagePreview v-for="page in items" :key="page.id" :page="page" class="_margin"/> </MkPagination> </MkSpacer> </template> diff --git a/packages/frontend/src/pages/user/reactions.vue b/packages/frontend/src/pages/user/reactions.vue index bc4f39a74f..24129ec024 100644 --- a/packages/frontend/src/pages/user/reactions.vue +++ b/packages/frontend/src/pages/user/reactions.vue @@ -1,7 +1,7 @@ <template> <MkSpacer :content-max="700"> <MkPagination v-slot="{items}" ref="list" :pagination="pagination"> - <div v-for="item in items" :key="item.id" :to="`/clips/${item.id}`" class="item _panel _gap afdcfbfb"> + <div v-for="item in items" :key="item.id" :to="`/clips/${item.id}`" class="item _panel _margin afdcfbfb"> <div class="header"> <MkAvatar class="avatar" :user="user"/> <MkReactionIcon class="reaction" :reaction="item.type" :no-style="true"/> diff --git a/packages/frontend/src/pages/welcome.setup.vue b/packages/frontend/src/pages/welcome.setup.vue index 2729d30d4b..0c627be8e4 100644 --- a/packages/frontend/src/pages/welcome.setup.vue +++ b/packages/frontend/src/pages/welcome.setup.vue @@ -1,18 +1,18 @@ <template> <form class="mk-setup" @submit.prevent="submit()"> <h1>Welcome to Misskey!</h1> - <div class="_formRoot"> + <div class="_autoGap"> <p>{{ $ts.intro }}</p> - <MkInput v-model="username" pattern="^[a-zA-Z0-9_]{1,20}$" :spellcheck="false" required data-cy-admin-username class="_formBlock"> + <MkInput v-model="username" pattern="^[a-zA-Z0-9_]{1,20}$" :spellcheck="false" required data-cy-admin-username> <template #label>{{ $ts.username }}</template> <template #prefix>@</template> <template #suffix>@{{ host }}</template> </MkInput> - <MkInput v-model="password" type="password" data-cy-admin-password class="_formBlock"> + <MkInput v-model="password" type="password" data-cy-admin-password> <template #label>{{ $ts.password }}</template> <template #prefix><i class="ti ti-lock"></i></template> </MkInput> - <div class="bottom _formBlock"> + <div class="bottom"> <MkButton gradate type="submit" :disabled="submitting" data-cy-admin-ok> {{ submitting ? $ts.processing : $ts.done }}<MkEllipsis v-if="submitting"/> </MkButton> diff --git a/packages/frontend/src/style.scss b/packages/frontend/src/style.scss index f4369884f7..e0ff1c23e0 100644 --- a/packages/frontend/src/style.scss +++ b/packages/frontend/src/style.scss @@ -252,6 +252,7 @@ hr { overflow: clip; } +// TODO: 廃止 ._block { @extend ._panel; @@ -260,10 +261,22 @@ hr { } } -._gap { +._margin { margin: var(--margin) 0; } +._autoGap { + display: flex; + flex-direction: column; + gap: 1.5em; +} + +._autoGap_half { + display: flex; + flex-direction: column; + gap: 0.75em; +} + // TODO: 廃止 ._card { @extend ._panel; @@ -370,20 +383,6 @@ hr { backdrop-filter: var(--blur, blur(15px)); } -._formBlock { - margin: 1.5em 0; -} - -._formRoot { - > ._formBlock:first-child { - margin-top: 0; - } - - > ._formBlock:last-child { - margin-bottom: 0; - } -} - ._formLinksGrid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); @@ -534,7 +533,7 @@ hr { transition: transform 0.1s ease; } -@keyframes bounce{ +@keyframes bounce { 0% { transform: scaleX(0.90) scaleY(0.90) ; } From bcc3380cfca3ea1faf2a72b30d18f465bdc27634 Mon Sep 17 00:00:00 2001 From: CyberRex <hspwinx86@gmail.com> Date: Fri, 6 Jan 2023 08:05:02 +0900 Subject: [PATCH 25/60] fix(backend): Set correct access control of admin/drive/files (#9472) --- packages/backend/src/server/api/endpoints/admin/drive/files.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/src/server/api/endpoints/admin/drive/files.ts b/packages/backend/src/server/api/endpoints/admin/drive/files.ts index 53a37cb691..8a4498d5fa 100644 --- a/packages/backend/src/server/api/endpoints/admin/drive/files.ts +++ b/packages/backend/src/server/api/endpoints/admin/drive/files.ts @@ -8,7 +8,7 @@ import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.j export const meta = { tags: ['admin'], - requireCredential: false, + requireCredential: true, requireModerator: true, res: { From 372a17d7f0fd4e95086b5e9685d4c21a3550422b Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Fri, 6 Jan 2023 09:41:14 +0900 Subject: [PATCH 26/60] refactor --- .../src/components/MkAbuseReportWindow.vue | 6 +- packages/frontend/src/components/MkAsUi.vue | 2 +- .../src/components/MkCropperDialog.vue | 8 +- .../src/components/MkDriveSelectDialog.vue | 8 +- .../components/MkFileCaptionEditWindow.vue | 8 +- .../src/components/MkFlashPreview.vue | 2 +- .../src/components/MkForgotPassword.vue | 8 +- .../frontend/src/components/MkFormDialog.vue | 8 +- packages/frontend/src/components/MkModal.vue | 1 - .../src/components/MkModalPageWindow.vue | 2 +- .../frontend/src/components/MkModalWindow.vue | 3 +- .../src/components/MkNoteDetailed.vue | 2 +- .../MkNotificationSettingWindow.vue | 10 +-- .../frontend/src/components/MkPagePreview.vue | 2 +- .../src/components/MkRemoteCaution.vue | 2 +- packages/frontend/src/components/MkSample.vue | 8 +- packages/frontend/src/components/MkSignin.vue | 6 +- .../src/components/MkSigninDialog.vue | 12 +-- .../src/components/MkSignupDialog.vue | 16 ++-- .../src/components/MkTokenGenerateWindow.vue | 10 +-- .../src/components/MkUserSelectDialog.vue | 6 +- packages/frontend/src/components/MkWindow.vue | 2 +- packages/frontend/src/pages/admin/abuses.vue | 4 +- .../src/pages/admin/announcements.vue | 10 +-- .../src/pages/admin/bot-protection.vue | 4 +- .../src/pages/admin/emoji-edit-dialog.vue | 6 +- packages/frontend/src/pages/admin/emojis.vue | 2 +- .../src/pages/admin/instance-block.vue | 4 +- .../src/pages/admin/integrations.discord.vue | 4 +- .../src/pages/admin/integrations.github.vue | 4 +- .../src/pages/admin/integrations.twitter.vue | 4 +- .../src/pages/admin/proxy-account.vue | 4 +- packages/frontend/src/pages/admin/relays.vue | 2 +- .../frontend/src/pages/admin/security.vue | 6 +- packages/frontend/src/pages/announcements.vue | 23 +++-- .../frontend/src/pages/antenna-timeline.vue | 2 +- packages/frontend/src/pages/auth.form.vue | 10 +-- packages/frontend/src/pages/channels.vue | 6 +- .../frontend/src/pages/flash/flash-edit.vue | 2 +- packages/frontend/src/pages/gallery/edit.vue | 10 +-- packages/frontend/src/pages/gallery/post.vue | 2 +- .../frontend/src/pages/messaging/index.vue | 2 +- .../pages/messaging/messaging-room.form.vue | 2 +- .../src/pages/messaging/messaging-room.vue | 4 +- .../frontend/src/pages/mfm-cheat-sheet.vue | 60 ++++++------- packages/frontend/src/pages/miauth.vue | 22 ++--- .../frontend/src/pages/my-lists/index.vue | 2 +- packages/frontend/src/pages/my-lists/list.vue | 10 +-- packages/frontend/src/pages/note.vue | 6 +- packages/frontend/src/pages/page.vue | 2 +- .../frontend/src/pages/reset-password.vue | 4 +- .../frontend/src/pages/settings/accounts.vue | 4 +- packages/frontend/src/pages/settings/api.vue | 4 +- .../src/pages/settings/delete-account.vue | 6 +- .../frontend/src/pages/settings/navbar.vue | 4 +- .../src/pages/settings/notifications.vue | 2 +- .../src/pages/settings/plugin.install.vue | 4 +- .../frontend/src/pages/settings/plugin.vue | 2 +- .../frontend/src/pages/settings/reaction.vue | 8 +- .../frontend/src/pages/settings/security.vue | 6 +- .../src/pages/settings/sounds.sound.vue | 8 +- .../frontend/src/pages/settings/sounds.vue | 4 +- .../pages/settings/statusbar.statusbar.vue | 6 +- .../frontend/src/pages/settings/statusbar.vue | 4 +- .../src/pages/settings/theme.install.vue | 8 +- .../src/pages/settings/theme.manage.vue | 4 +- .../frontend/src/pages/settings/theme.vue | 6 +- .../src/pages/settings/webhook.edit.vue | 6 +- .../src/pages/settings/webhook.new.vue | 6 +- packages/frontend/src/pages/tag.vue | 2 +- packages/frontend/src/pages/theme-editor.vue | 4 +- packages/frontend/src/pages/timeline.vue | 6 +- packages/frontend/src/pages/user-info.vue | 8 +- .../frontend/src/pages/user-list-timeline.vue | 2 +- packages/frontend/src/pages/user/home.vue | 4 +- packages/frontend/src/style.scss | 90 +------------------ packages/frontend/src/ui/deck/column.vue | 2 +- 77 files changed, 245 insertions(+), 320 deletions(-) diff --git a/packages/frontend/src/components/MkAbuseReportWindow.vue b/packages/frontend/src/components/MkAbuseReportWindow.vue index ab90ed357a..e20cf1b735 100644 --- a/packages/frontend/src/components/MkAbuseReportWindow.vue +++ b/packages/frontend/src/components/MkAbuseReportWindow.vue @@ -8,14 +8,14 @@ </template> </I18n> </template> - <div class="dpvffvvy _monolithic_"> - <div class="_section"> + <div class="dpvffvvy _autoGap"> + <div class=""> <MkTextarea v-model="comment"> <template #label>{{ i18n.ts.details }}</template> <template #caption>{{ i18n.ts.fillAbuseReportDescription }}</template> </MkTextarea> </div> - <div class="_section"> + <div class=""> <MkButton primary full :disabled="comment.length === 0" @click="send">{{ i18n.ts.send }}</MkButton> </div> </div> diff --git a/packages/frontend/src/components/MkAsUi.vue b/packages/frontend/src/components/MkAsUi.vue index ec214f0396..e2d8c010a2 100644 --- a/packages/frontend/src/components/MkAsUi.vue +++ b/packages/frontend/src/components/MkAsUi.vue @@ -8,7 +8,7 @@ <span v-else-if="c.type === 'text'" :class="{ [$style.fontSerif]: c.font === 'serif', [$style.fontMonospace]: c.font === 'monospace' }" :style="{ fontSize: c.size ? `${c.size * 100}%` : null, fontWeight: c.bold ? 'bold' : null, color: c.color ?? null }">{{ c.text }}</span> <Mfm v-else-if="c.type === 'mfm'" :class="{ [$style.fontSerif]: c.font === 'serif', [$style.fontMonospace]: c.font === 'monospace' }" :style="{ fontSize: c.size ? `${c.size * 100}%` : null, color: c.color ?? null }" :text="c.text"/> <MkButton v-else-if="c.type === 'button'" :primary="c.primary" :rounded="c.rounded" :small="size === 'small'" @click="c.onClick">{{ c.text }}</MkButton> - <div v-else-if="c.type === 'buttons'" style="display: flex; gap: 8px; flex-wrap: wrap;"> + <div v-else-if="c.type === 'buttons'" class="_buttons"> <MkButton v-for="button in c.buttons" :primary="button.primary" :rounded="button.rounded" :small="size === 'small'" @click="button.onClick">{{ button.text }}</MkButton> </div> <MkSwitch v-else-if="c.type === 'switch'" :model-value="valueForSwitch" @update:model-value="onSwitchUpdate"> diff --git a/packages/frontend/src/components/MkCropperDialog.vue b/packages/frontend/src/components/MkCropperDialog.vue index f00fef12f1..84adb790f9 100644 --- a/packages/frontend/src/components/MkCropperDialog.vue +++ b/packages/frontend/src/components/MkCropperDialog.vue @@ -1,5 +1,5 @@ <template> -<XModalWindow +<MkModalWindow ref="dialogEl" :width="800" :height="500" @@ -22,7 +22,7 @@ </div> </div> </template> -</XModalWindow> +</MkModalWindow> </template> <script lang="ts" setup> @@ -30,7 +30,7 @@ import { nextTick, onMounted } from 'vue'; import * as misskey from 'misskey-js'; import Cropper from 'cropperjs'; import tinycolor from 'tinycolor2'; -import XModalWindow from '@/components/MkModalWindow.vue'; +import MkModalWindow from '@/components/MkModalWindow.vue'; import * as os from '@/os'; import { $i } from '@/account'; import { defaultStore } from '@/store'; @@ -50,7 +50,7 @@ const props = defineProps<{ }>(); const imgUrl = getProxiedImageUrl(props.file.url); -let dialogEl = $shallowRef<InstanceType<typeof XModalWindow>>(); +let dialogEl = $shallowRef<InstanceType<typeof MkModalWindow>>(); let imgEl = $shallowRef<HTMLImageElement>(); let cropper: Cropper | null = null; let loading = $ref(true); diff --git a/packages/frontend/src/components/MkDriveSelectDialog.vue b/packages/frontend/src/components/MkDriveSelectDialog.vue index 6a96e758fa..8d2b19c013 100644 --- a/packages/frontend/src/components/MkDriveSelectDialog.vue +++ b/packages/frontend/src/components/MkDriveSelectDialog.vue @@ -1,5 +1,5 @@ <template> -<XModalWindow +<MkModalWindow ref="dialog" :width="800" :height="500" @@ -15,14 +15,14 @@ <span v-if="selected.length > 0" style="margin-left: 8px; opacity: 0.5;">({{ number(selected.length) }})</span> </template> <XDrive :multiple="multiple" :select="type" @change-selection="onChangeSelection" @selected="ok()"/> -</XModalWindow> +</MkModalWindow> </template> <script lang="ts" setup> import { ref, shallowRef } from 'vue'; import * as Misskey from 'misskey-js'; import XDrive from '@/components/MkDrive.vue'; -import XModalWindow from '@/components/MkModalWindow.vue'; +import MkModalWindow from '@/components/MkModalWindow.vue'; import number from '@/filters/number'; import { i18n } from '@/i18n'; @@ -38,7 +38,7 @@ const emit = defineEmits<{ (ev: 'closed'): void; }>(); -const dialog = shallowRef<InstanceType<typeof XModalWindow>>(); +const dialog = shallowRef<InstanceType<typeof MkModalWindow>>(); const selected = ref<Misskey.entities.DriveFile[]>([]); diff --git a/packages/frontend/src/components/MkFileCaptionEditWindow.vue b/packages/frontend/src/components/MkFileCaptionEditWindow.vue index b3bd194dc3..f7b7430bff 100644 --- a/packages/frontend/src/components/MkFileCaptionEditWindow.vue +++ b/packages/frontend/src/components/MkFileCaptionEditWindow.vue @@ -1,5 +1,5 @@ <template> -<XModalWindow +<MkModalWindow ref="dialog" :width="400" :height="450" @@ -16,13 +16,13 @@ <template #label>{{ i18n.ts.caption }}</template> </MkTextarea> </MkSpacer> -</XModalWindow> +</MkModalWindow> </template> <script lang="ts" setup> import { } from 'vue'; import * as Misskey from 'misskey-js'; -import XModalWindow from '@/components/MkModalWindow.vue'; +import MkModalWindow from '@/components/MkModalWindow.vue'; import MkTextarea from '@/components/form/textarea.vue'; import MkDriveFileThumbnail from '@/components/MkDriveFileThumbnail.vue'; import { i18n } from '@/i18n'; @@ -37,7 +37,7 @@ const emit = defineEmits<{ (ev: 'closed'): void; }>(); -const dialog = $shallowRef<InstanceType<typeof XModalWindow>>(); +const dialog = $shallowRef<InstanceType<typeof MkModalWindow>>(); let caption = $ref(props.default); diff --git a/packages/frontend/src/components/MkFlashPreview.vue b/packages/frontend/src/components/MkFlashPreview.vue index 1a82ffe5ab..a1ce80c98c 100644 --- a/packages/frontend/src/components/MkFlashPreview.vue +++ b/packages/frontend/src/components/MkFlashPreview.vue @@ -1,5 +1,5 @@ <template> -<MkA :to="`/play/${flash.id}`" class="vhpxefrk _block" tabindex="-1"> +<MkA :to="`/play/${flash.id}`" class="vhpxefrk" tabindex="-1"> <article> <header> <h1 :title="flash.title">{{ flash.title }}</h1> diff --git a/packages/frontend/src/components/MkForgotPassword.vue b/packages/frontend/src/components/MkForgotPassword.vue index 29bf355cc1..f74bb24b8d 100644 --- a/packages/frontend/src/components/MkForgotPassword.vue +++ b/packages/frontend/src/components/MkForgotPassword.vue @@ -1,5 +1,5 @@ <template> -<XModalWindow +<MkModalWindow ref="dialog" :width="370" :height="400" @@ -29,12 +29,12 @@ <div v-else class="bafecedb"> {{ i18n.ts._forgotPassword.contactAdmin }} </div> -</XModalWindow> +</MkModalWindow> </template> <script lang="ts" setup> import { } from 'vue'; -import XModalWindow from '@/components/MkModalWindow.vue'; +import MkModalWindow from '@/components/MkModalWindow.vue'; import MkButton from '@/components/MkButton.vue'; import MkInput from '@/components/form/input.vue'; import * as os from '@/os'; @@ -46,7 +46,7 @@ const emit = defineEmits<{ (ev: 'closed'): void; }>(); -let dialog: InstanceType<typeof XModalWindow> = $ref(); +let dialog: InstanceType<typeof MkModalWindow> = $ref(); let username = $ref(''); let email = $ref(''); diff --git a/packages/frontend/src/components/MkFormDialog.vue b/packages/frontend/src/components/MkFormDialog.vue index 29fc040092..2f440e4b9f 100644 --- a/packages/frontend/src/components/MkFormDialog.vue +++ b/packages/frontend/src/components/MkFormDialog.vue @@ -1,5 +1,5 @@ <template> -<XModalWindow +<MkModalWindow ref="dialog" :width="450" :can-close="false" @@ -51,7 +51,7 @@ </template> </div> </MkSpacer> -</XModalWindow> +</MkModalWindow> </template> <script lang="ts"> @@ -63,11 +63,11 @@ import FormSelect from './form/select.vue'; import FormRange from './form/range.vue'; import MkButton from './MkButton.vue'; import FormRadios from './form/radios.vue'; -import XModalWindow from '@/components/MkModalWindow.vue'; +import MkModalWindow from '@/components/MkModalWindow.vue'; export default defineComponent({ components: { - XModalWindow, + MkModalWindow, FormInput, FormTextarea, FormSwitch, diff --git a/packages/frontend/src/components/MkModal.vue b/packages/frontend/src/components/MkModal.vue index 505b5e64bc..38b1ce7c1d 100644 --- a/packages/frontend/src/components/MkModal.vue +++ b/packages/frontend/src/components/MkModal.vue @@ -383,7 +383,6 @@ defineExpose({ mask-image: linear-gradient(0deg, rgba(0,0,0,0) 0%, rgba(0,0,0,1) 32px, rgba(0,0,0,1) calc(100% - 32px), rgba(0,0,0,0) 100%); overflow: auto; display: flex; - container-type: inline-size; @media (max-width: 500px) { padding: 16px; diff --git a/packages/frontend/src/components/MkModalPageWindow.vue b/packages/frontend/src/components/MkModalPageWindow.vue index 2791d5ceb9..b06fcb9ffa 100644 --- a/packages/frontend/src/components/MkModalPageWindow.vue +++ b/packages/frontend/src/components/MkModalPageWindow.vue @@ -1,6 +1,6 @@ <template> <MkModal ref="modal" @click="$emit('click')" @closed="$emit('closed')"> - <div ref="rootEl" class="hrmcaedk _narrow_" :style="{ width: `${width}px`, height: (height ? `min(${height}px, 100%)` : '100%') }"> + <div ref="rootEl" class="hrmcaedk" :style="{ width: `${width}px`, height: (height ? `min(${height}px, 100%)` : '100%') }"> <div class="header" @contextmenu="onContextmenu"> <button v-if="history.length > 0" v-tooltip="$ts.goBack" class="_button" @click="back()"><i class="ti ti-arrow-left"></i></button> <span v-else style="display: inline-block; width: 20px"></span> diff --git a/packages/frontend/src/components/MkModalWindow.vue b/packages/frontend/src/components/MkModalWindow.vue index 1e93f01c8d..ac428fa7b1 100644 --- a/packages/frontend/src/components/MkModalWindow.vue +++ b/packages/frontend/src/components/MkModalWindow.vue @@ -1,6 +1,6 @@ <template> <MkModal ref="modal" :prefer-type="'dialog'" @click="onBgClick" @closed="$emit('closed')"> - <div ref="rootEl" class="ebkgoccj _narrow_" :style="{ width: `${width}px`, height: scroll ? (height ? `${height}px` : null) : (height ? `min(${height}px, 100%)` : '100%') }" @keydown="onKeydown"> + <div ref="rootEl" class="ebkgoccj" :style="{ width: `${width}px`, height: scroll ? (height ? `${height}px` : null) : (height ? `min(${height}px, 100%)` : '100%') }" @keydown="onKeydown"> <div ref="headerEl" class="header"> <button v-if="withOkButton" class="_button" @click="$emit('close')"><i class="ti ti-x"></i></button> <span class="title"> @@ -89,6 +89,7 @@ defineExpose({ display: flex; flex-direction: column; contain: content; + container-type: inline-size; border-radius: var(--radius); --root-margin: 24px; diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue index 7d01a7bf75..c0e1ca7215 100644 --- a/packages/frontend/src/components/MkNoteDetailed.vue +++ b/packages/frontend/src/components/MkNoteDetailed.vue @@ -4,7 +4,7 @@ v-show="!isDeleted" ref="el" v-hotkey="keymap" - class="lxwezrsl _block" + class="lxwezrsl" :tabindex="!isDeleted ? '-1' : null" :class="{ renote: isRenote }" > diff --git a/packages/frontend/src/components/MkNotificationSettingWindow.vue b/packages/frontend/src/components/MkNotificationSettingWindow.vue index e58a11bf5c..671865f629 100644 --- a/packages/frontend/src/components/MkNotificationSettingWindow.vue +++ b/packages/frontend/src/components/MkNotificationSettingWindow.vue @@ -1,5 +1,5 @@ <template> -<XModalWindow +<MkModalWindow ref="dialog" :width="400" :height="450" @@ -21,7 +21,7 @@ </template> <template v-if="!useGlobalSetting"> <MkInfo>{{ i18n.ts.notificationSettingDesc }}</MkInfo> - <div style="display: flex; gap: var(--margin); flex-wrap: wrap;"> + <div class="_buttons"> <MkButton inline @click="disableAll">{{ i18n.ts.disableAll }}</MkButton> <MkButton inline @click="enableAll">{{ i18n.ts.enableAll }}</MkButton> </div> @@ -29,7 +29,7 @@ </template> </div> </MkSpacer> -</XModalWindow> +</MkModalWindow> </template> <script lang="ts" setup> @@ -38,7 +38,7 @@ import { notificationTypes } from 'misskey-js'; import MkSwitch from './form/switch.vue'; import MkInfo from './MkInfo.vue'; import MkButton from './MkButton.vue'; -import XModalWindow from '@/components/MkModalWindow.vue'; +import MkModalWindow from '@/components/MkModalWindow.vue'; import { i18n } from '@/i18n'; const emit = defineEmits<{ @@ -56,7 +56,7 @@ const props = withDefaults(defineProps<{ let includingTypes = $computed(() => props.includingTypes || []); -const dialog = $shallowRef<InstanceType<typeof XModalWindow>>(); +const dialog = $shallowRef<InstanceType<typeof MkModalWindow>>(); let typesMap = $ref<Record<typeof notificationTypes[number], boolean>>({}); let useGlobalSetting = $ref((includingTypes === null || includingTypes.length === 0) && props.showGlobalToggle); diff --git a/packages/frontend/src/components/MkPagePreview.vue b/packages/frontend/src/components/MkPagePreview.vue index 1eb61d4344..a78431e2a7 100644 --- a/packages/frontend/src/components/MkPagePreview.vue +++ b/packages/frontend/src/components/MkPagePreview.vue @@ -1,5 +1,5 @@ <template> -<MkA :to="`/@${page.user.username}/pages/${page.name}`" class="vhpxefrj _block" tabindex="-1"> +<MkA :to="`/@${page.user.username}/pages/${page.name}`" class="vhpxefrj" tabindex="-1"> <div v-if="page.eyeCatchingImage" class="thumbnail" :style="`background-image: url('${page.eyeCatchingImage.thumbnailUrl}')`"></div> <article> <header> diff --git a/packages/frontend/src/components/MkRemoteCaution.vue b/packages/frontend/src/components/MkRemoteCaution.vue index d5dc01c1f8..6d398e770d 100644 --- a/packages/frontend/src/components/MkRemoteCaution.vue +++ b/packages/frontend/src/components/MkRemoteCaution.vue @@ -1,5 +1,5 @@ <template> -<div class="jmgmzlwq _block"><i class="ti ti-alert-triangle" style="margin-right: 8px;"></i>{{ i18n.ts.remoteUserCaution }}<a class="link" :href="href" rel="nofollow noopener" target="_blank">{{ i18n.ts.showOnRemote }}</a></div> +<div class="jmgmzlwq"><i class="ti ti-alert-triangle" style="margin-right: 8px;"></i>{{ i18n.ts.remoteUserCaution }}<a class="link" :href="href" rel="nofollow noopener" target="_blank">{{ i18n.ts.showOnRemote }}</a></div> </template> <script lang="ts" setup> diff --git a/packages/frontend/src/components/MkSample.vue b/packages/frontend/src/components/MkSample.vue index 1d25ab54b5..ded5ac3f7a 100644 --- a/packages/frontend/src/components/MkSample.vue +++ b/packages/frontend/src/components/MkSample.vue @@ -1,6 +1,6 @@ <template> -<div class="_card"> - <div class="_content"> +<div class=""> + <div class=""> <MkInput v-model="text"> <template #label>Text</template> </MkInput> @@ -15,10 +15,10 @@ <MkButton inline>This is</MkButton> <MkButton inline primary>the button</MkButton> </div> - <div class="_content" style="pointer-events: none;"> + <div class="" style="pointer-events: none;"> <Mfm :text="mfm"/> </div> - <div class="_content"> + <div class=""> <MkButton inline primary @click="openMenu">Open menu</MkButton> <MkButton inline primary @click="openDialog">Open dialog</MkButton> <MkButton inline primary @click="openForm">Open form</MkButton> diff --git a/packages/frontend/src/components/MkSignin.vue b/packages/frontend/src/components/MkSignin.vue index 281c935eb6..eb015b08f2 100644 --- a/packages/frontend/src/components/MkSignin.vue +++ b/packages/frontend/src/components/MkSignin.vue @@ -1,6 +1,6 @@ <template> -<form class="eppvobhk _monolithic_" :class="{ signing, totpLogin }" @submit.prevent="onSubmit"> - <div class="auth _section _autoGap"> +<form class="eppvobhk" :class="{ signing, totpLogin }" @submit.prevent="onSubmit"> + <div class="auth _autoGap"> <div v-show="withAvatar" class="avatar" :style="{ backgroundImage: user ? `url('${ user.avatarUrl }')` : null, marginBottom: message ? '1.5em' : null }"></div> <MkInfo v-if="message"> {{ message }} @@ -40,7 +40,7 @@ </div> </div> </div> - <div class="social _section"> + <div class="social"> <a v-if="meta && meta.enableTwitterIntegration" class="_borderButton _margin" :href="`${apiUrl}/signin/twitter`"><i class="ti ti-brand-twitter" style="margin-right: 4px;"></i>{{ $t('signinWith', { x: 'Twitter' }) }}</a> <a v-if="meta && meta.enableGithubIntegration" class="_borderButton _margin" :href="`${apiUrl}/signin/github`"><i class="ti ti-brand-github" style="margin-right: 4px;"></i>{{ $t('signinWith', { x: 'GitHub' }) }}</a> <a v-if="meta && meta.enableDiscordIntegration" class="_borderButton _margin" :href="`${apiUrl}/signin/discord`"><i class="ti ti-brand-discord" style="margin-right: 4px;"></i>{{ $t('signinWith', { x: 'Discord' }) }}</a> diff --git a/packages/frontend/src/components/MkSigninDialog.vue b/packages/frontend/src/components/MkSigninDialog.vue index 5015d09e64..83506b8f66 100644 --- a/packages/frontend/src/components/MkSigninDialog.vue +++ b/packages/frontend/src/components/MkSigninDialog.vue @@ -1,5 +1,5 @@ <template> -<XModalWindow +<MkModalWindow ref="dialog" :width="370" :height="400" @@ -8,14 +8,16 @@ > <template #header>{{ i18n.ts.login }}</template> - <MkSignin :auto-set="autoSet" :message="message" @login="onLogin"/> -</XModalWindow> + <MkSpacer :margin-min="20" :margin-max="28"> + <MkSignin :auto-set="autoSet" :message="message" @login="onLogin"/> + </MkSpacer> +</MkModalWindow> </template> <script lang="ts" setup> import { } from 'vue'; import MkSignin from '@/components/MkSignin.vue'; -import XModalWindow from '@/components/MkModalWindow.vue'; +import MkModalWindow from '@/components/MkModalWindow.vue'; import { i18n } from '@/i18n'; const props = withDefaults(defineProps<{ @@ -32,7 +34,7 @@ const emit = defineEmits<{ (ev: 'cancelled'): void; }>(); -const dialog = $shallowRef<InstanceType<typeof XModalWindow>>(); +const dialog = $shallowRef<InstanceType<typeof MkModalWindow>>(); function onClose() { emit('cancelled'); diff --git a/packages/frontend/src/components/MkSignupDialog.vue b/packages/frontend/src/components/MkSignupDialog.vue index e1b76474a0..790c1e94df 100644 --- a/packages/frontend/src/components/MkSignupDialog.vue +++ b/packages/frontend/src/components/MkSignupDialog.vue @@ -1,5 +1,5 @@ <template> -<XModalWindow +<MkModalWindow ref="dialog" :width="366" :height="500" @@ -8,18 +8,16 @@ > <template #header>{{ i18n.ts.signup }}</template> - <div class="_monolithic_"> - <div class="_section"> - <XSignup :auto-set="autoSet" @signup="onSignup" @signup-email-pending="onSignupEmailPending"/> - </div> - </div> -</XModalWindow> + <MkSpacer :margin-min="20" :margin-max="28"> + <XSignup :auto-set="autoSet" @signup="onSignup" @signup-email-pending="onSignupEmailPending"/> + </MkSpacer> +</MkModalWindow> </template> <script lang="ts" setup> import { } from 'vue'; import XSignup from '@/components/MkSignup.vue'; -import XModalWindow from '@/components/MkModalWindow.vue'; +import MkModalWindow from '@/components/MkModalWindow.vue'; import { i18n } from '@/i18n'; const props = withDefaults(defineProps<{ @@ -33,7 +31,7 @@ const emit = defineEmits<{ (ev: 'closed'): void; }>(); -const dialog = $shallowRef<InstanceType<typeof XModalWindow>>(); +const dialog = $shallowRef<InstanceType<typeof MkModalWindow>>(); function onSignup(res) { emit('done', res); diff --git a/packages/frontend/src/components/MkTokenGenerateWindow.vue b/packages/frontend/src/components/MkTokenGenerateWindow.vue index 1762a0d33e..1a5a92caff 100644 --- a/packages/frontend/src/components/MkTokenGenerateWindow.vue +++ b/packages/frontend/src/components/MkTokenGenerateWindow.vue @@ -1,5 +1,5 @@ <template> -<XModalWindow +<MkModalWindow ref="dialog" :width="400" :height="450" @@ -23,14 +23,14 @@ </MkInput> </div> <div><b>{{ $ts.permission }}</b></div> - <div style="display: flex; gap: var(--margin); flex-wrap: wrap;"> + <div class="_buttons"> <MkButton inline @click="disableAll">{{ i18n.ts.disableAll }}</MkButton> <MkButton inline @click="enableAll">{{ i18n.ts.enableAll }}</MkButton> </div> <MkSwitch v-for="kind in (initialPermissions || kinds)" :key="kind" v-model="permissions[kind]">{{ $t(`_permissions.${kind}`) }}</MkSwitch> </div> </MkSpacer> -</XModalWindow> +</MkModalWindow> </template> <script lang="ts" setup> @@ -40,7 +40,7 @@ import MkInput from './form/input.vue'; import MkSwitch from './form/switch.vue'; import MkButton from './MkButton.vue'; import MkInfo from './MkInfo.vue'; -import XModalWindow from '@/components/MkModalWindow.vue'; +import MkModalWindow from '@/components/MkModalWindow.vue'; import { i18n } from '@/i18n'; const props = withDefaults(defineProps<{ @@ -60,7 +60,7 @@ const emit = defineEmits<{ (ev: 'done', result: { name: string | null, permissions: string[] }): void; }>(); -const dialog = $shallowRef<InstanceType<typeof XModalWindow>>(); +const dialog = $shallowRef<InstanceType<typeof MkModalWindow>>(); let name = $ref(props.initialName); let permissions = $ref({}); diff --git a/packages/frontend/src/components/MkUserSelectDialog.vue b/packages/frontend/src/components/MkUserSelectDialog.vue index 1d31769c30..c8a2fd8cc1 100644 --- a/packages/frontend/src/components/MkUserSelectDialog.vue +++ b/packages/frontend/src/components/MkUserSelectDialog.vue @@ -1,5 +1,5 @@ <template> -<XModalWindow +<MkModalWindow ref="dialogEl" :with-ok-button="true" :ok-button-disabled="selected == null" @@ -48,7 +48,7 @@ </div> </div> </div> -</XModalWindow> +</MkModalWindow> </template> <script lang="ts" setup> @@ -56,7 +56,7 @@ import { nextTick, onMounted } from 'vue'; import * as misskey from 'misskey-js'; import MkInput from '@/components/form/input.vue'; import FormSplit from '@/components/form/split.vue'; -import XModalWindow from '@/components/MkModalWindow.vue'; +import MkModalWindow from '@/components/MkModalWindow.vue'; import * as os from '@/os'; import { defaultStore } from '@/store'; import { i18n } from '@/i18n'; diff --git a/packages/frontend/src/components/MkWindow.vue b/packages/frontend/src/components/MkWindow.vue index 0f7e0e4f2e..b28c5e4b85 100644 --- a/packages/frontend/src/components/MkWindow.vue +++ b/packages/frontend/src/components/MkWindow.vue @@ -1,7 +1,7 @@ <template> <Transition :name="$store.state.animation ? 'window' : ''" appear @after-leave="$emit('closed')"> <div v-if="showing" ref="rootEl" class="ebkgocck" :class="{ maximized }"> - <div class="body _shadow _narrow_" @mousedown="onBodyMousedown" @keydown="onKeydown"> + <div class="body _shadow" @mousedown="onBodyMousedown" @keydown="onKeydown"> <div class="header" :class="{ mini }" @contextmenu.prevent.stop="onContextmenu"> <span class="left"> <button v-for="button in buttonsLeft" v-tooltip="button.title" class="button _button" :class="{ highlighted: button.highlighted }" @click="button.onClick"><i :class="button.icon"></i></button> diff --git a/packages/frontend/src/pages/admin/abuses.vue b/packages/frontend/src/pages/admin/abuses.vue index 3bff312b8b..88535cc67c 100644 --- a/packages/frontend/src/pages/admin/abuses.vue +++ b/packages/frontend/src/pages/admin/abuses.vue @@ -3,8 +3,8 @@ <template #header><XHeader :actions="headerActions" :tabs="headerTabs"/></template> <MkSpacer :content-max="900"> <div class="lcixvhis"> - <div class="_section reports"> - <div class="_content"> + <div class="reports"> + <div class=""> <div class="inputs" style="display: flex;"> <MkSelect v-model="state" style="margin: 0; flex: 1;"> <template #label>{{ i18n.ts.state }}</template> diff --git a/packages/frontend/src/pages/admin/announcements.vue b/packages/frontend/src/pages/admin/announcements.vue index f5c2f11957..d9300da2e5 100644 --- a/packages/frontend/src/pages/admin/announcements.vue +++ b/packages/frontend/src/pages/admin/announcements.vue @@ -2,9 +2,9 @@ <MkStickyContainer> <template #header><XHeader :actions="headerActions" :tabs="headerTabs"/></template> <MkSpacer :content-max="900"> - <div class="ztgjmzrw"> - <section v-for="announcement in announcements" class="_card _margin announcements"> - <div class="_content announcement"> + <div class="ztgjmzrw _autoGap"> + <section v-for="announcement in announcements" class=""> + <div class="_panel _autoGap" style="padding: 24px;"> <MkInput v-model="announcement.title"> <template #label>{{ i18n.ts.title }}</template> </MkInput> @@ -15,9 +15,9 @@ <template #label>{{ i18n.ts.imageUrl }}</template> </MkInput> <p v-if="announcement.reads">{{ i18n.t('nUsersRead', { n: announcement.reads }) }}</p> - <div class="buttons"> + <div class="buttons _buttons"> <MkButton class="button" inline primary @click="save(announcement)"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</MkButton> - <MkButton class="button" inline @click="remove(announcement)"><i class="ti ti-trash"></i> {{ i18n.ts.remove }}</MkButton> + <MkButton class="button" inline danger @click="remove(announcement)"><i class="ti ti-trash"></i> {{ i18n.ts.remove }}</MkButton> </div> </div> </section> diff --git a/packages/frontend/src/pages/admin/bot-protection.vue b/packages/frontend/src/pages/admin/bot-protection.vue index c213e9805b..22fac54913 100644 --- a/packages/frontend/src/pages/admin/bot-protection.vue +++ b/packages/frontend/src/pages/admin/bot-protection.vue @@ -52,7 +52,7 @@ </FormSlot> </template> - <FormButton primary @click="save"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</FormButton> + <MkButton primary @click="save"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</MkButton> </div> </FormSuspense> </div> @@ -62,7 +62,7 @@ import { defineAsyncComponent } from 'vue'; import FormRadios from '@/components/form/radios.vue'; import FormInput from '@/components/form/input.vue'; -import FormButton from '@/components/MkButton.vue'; +import MkButton from '@/components/MkButton.vue'; import FormSuspense from '@/components/form/suspense.vue'; import FormSlot from '@/components/form/slot.vue'; import * as os from '@/os'; diff --git a/packages/frontend/src/pages/admin/emoji-edit-dialog.vue b/packages/frontend/src/pages/admin/emoji-edit-dialog.vue index 115125bd4c..08b73d2ac2 100644 --- a/packages/frontend/src/pages/admin/emoji-edit-dialog.vue +++ b/packages/frontend/src/pages/admin/emoji-edit-dialog.vue @@ -1,5 +1,5 @@ <template> -<XModalWindow +<MkModalWindow ref="dialog" :width="370" :with-ok-button="true" @@ -25,12 +25,12 @@ <MkButton danger @click="del()"><i class="ti ti-trash"></i> {{ i18n.ts.delete }}</MkButton> </div> </MkSpacer> -</XModalWindow> +</MkModalWindow> </template> <script lang="ts" setup> import { } from 'vue'; -import XModalWindow from '@/components/MkModalWindow.vue'; +import MkModalWindow from '@/components/MkModalWindow.vue'; import MkButton from '@/components/MkButton.vue'; import MkInput from '@/components/form/input.vue'; import * as os from '@/os'; diff --git a/packages/frontend/src/pages/admin/emojis.vue b/packages/frontend/src/pages/admin/emojis.vue index 3d56ab1962..68ad51bce3 100644 --- a/packages/frontend/src/pages/admin/emojis.vue +++ b/packages/frontend/src/pages/admin/emojis.vue @@ -12,7 +12,7 @@ <MkSwitch v-model="selectMode" style="margin: 8px 0;"> <template #label>Select mode</template> </MkSwitch> - <div v-if="selectMode" style="display: flex; gap: var(--margin); flex-wrap: wrap;"> + <div v-if="selectMode" class="_buttons"> <MkButton inline @click="selectAll">Select all</MkButton> <MkButton inline @click="setCategoryBulk">Set category</MkButton> <MkButton inline @click="addTagBulk">Add tag</MkButton> diff --git a/packages/frontend/src/pages/admin/instance-block.vue b/packages/frontend/src/pages/admin/instance-block.vue index 6c6941daec..a45588e005 100644 --- a/packages/frontend/src/pages/admin/instance-block.vue +++ b/packages/frontend/src/pages/admin/instance-block.vue @@ -8,7 +8,7 @@ <template #caption>{{ i18n.ts.blockedInstancesDescription }}</template> </FormTextarea> - <FormButton primary @click="save"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</FormButton> + <MkButton primary @click="save"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</MkButton> </FormSuspense> </MkSpacer> </MkStickyContainer> @@ -17,7 +17,7 @@ <script lang="ts" setup> import { } from 'vue'; import XHeader from './_header_.vue'; -import FormButton from '@/components/MkButton.vue'; +import MkButton from '@/components/MkButton.vue'; import FormTextarea from '@/components/form/textarea.vue'; import FormSuspense from '@/components/form/suspense.vue'; import * as os from '@/os'; diff --git a/packages/frontend/src/pages/admin/integrations.discord.vue b/packages/frontend/src/pages/admin/integrations.discord.vue index e5069b9f85..09270896e7 100644 --- a/packages/frontend/src/pages/admin/integrations.discord.vue +++ b/packages/frontend/src/pages/admin/integrations.discord.vue @@ -19,7 +19,7 @@ </FormInput> </template> - <FormButton primary @click="save"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</FormButton> + <MkButton primary @click="save"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</MkButton> </div> </FormSuspense> </template> @@ -28,7 +28,7 @@ import { } from 'vue'; import FormSwitch from '@/components/form/switch.vue'; import FormInput from '@/components/form/input.vue'; -import FormButton from '@/components/MkButton.vue'; +import MkButton from '@/components/MkButton.vue'; import FormInfo from '@/components/MkInfo.vue'; import FormSuspense from '@/components/form/suspense.vue'; import * as os from '@/os'; diff --git a/packages/frontend/src/pages/admin/integrations.github.vue b/packages/frontend/src/pages/admin/integrations.github.vue index 8418e202ab..530fa6e037 100644 --- a/packages/frontend/src/pages/admin/integrations.github.vue +++ b/packages/frontend/src/pages/admin/integrations.github.vue @@ -19,7 +19,7 @@ </FormInput> </template> - <FormButton primary @click="save"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</FormButton> + <MkButton primary @click="save"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</MkButton> </div> </FormSuspense> </template> @@ -28,7 +28,7 @@ import { } from 'vue'; import FormSwitch from '@/components/form/switch.vue'; import FormInput from '@/components/form/input.vue'; -import FormButton from '@/components/MkButton.vue'; +import MkButton from '@/components/MkButton.vue'; import FormInfo from '@/components/MkInfo.vue'; import FormSuspense from '@/components/form/suspense.vue'; import * as os from '@/os'; diff --git a/packages/frontend/src/pages/admin/integrations.twitter.vue b/packages/frontend/src/pages/admin/integrations.twitter.vue index 8bd3c7c16d..b807c12074 100644 --- a/packages/frontend/src/pages/admin/integrations.twitter.vue +++ b/packages/frontend/src/pages/admin/integrations.twitter.vue @@ -19,7 +19,7 @@ </FormInput> </template> - <FormButton primary @click="save"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</FormButton> + <MkButton primary @click="save"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</MkButton> </div> </FormSuspense> </template> @@ -28,7 +28,7 @@ import { defineComponent } from 'vue'; import FormSwitch from '@/components/form/switch.vue'; import FormInput from '@/components/form/input.vue'; -import FormButton from '@/components/MkButton.vue'; +import MkButton from '@/components/MkButton.vue'; import FormInfo from '@/components/MkInfo.vue'; import FormSuspense from '@/components/form/suspense.vue'; import * as os from '@/os'; diff --git a/packages/frontend/src/pages/admin/proxy-account.vue b/packages/frontend/src/pages/admin/proxy-account.vue index 36a2718081..6ad566187a 100644 --- a/packages/frontend/src/pages/admin/proxy-account.vue +++ b/packages/frontend/src/pages/admin/proxy-account.vue @@ -9,7 +9,7 @@ <template #value>{{ proxyAccount ? `@${proxyAccount.username}` : i18n.ts.none }}</template> </MkKeyValue> - <FormButton primary @click="chooseProxyAccount">{{ i18n.ts.selectAccount }}</FormButton> + <MkButton primary @click="chooseProxyAccount">{{ i18n.ts.selectAccount }}</MkButton> </FormSuspense> </MkSpacer> </MkStickyContainer> @@ -18,7 +18,7 @@ <script lang="ts" setup> import { } from 'vue'; import MkKeyValue from '@/components/MkKeyValue.vue'; -import FormButton from '@/components/MkButton.vue'; +import MkButton from '@/components/MkButton.vue'; import MkInfo from '@/components/MkInfo.vue'; import FormSuspense from '@/components/form/suspense.vue'; import * as os from '@/os'; diff --git a/packages/frontend/src/pages/admin/relays.vue b/packages/frontend/src/pages/admin/relays.vue index 4768ae67b1..eb2788fdeb 100644 --- a/packages/frontend/src/pages/admin/relays.vue +++ b/packages/frontend/src/pages/admin/relays.vue @@ -2,7 +2,7 @@ <MkStickyContainer> <template #header><XHeader :actions="headerActions" :tabs="headerTabs"/></template> <MkSpacer :content-max="800"> - <div v-for="relay in relays" :key="relay.inbox" class="relaycxt _panel _block" style="padding: 16px;"> + <div v-for="relay in relays" :key="relay.inbox" class="relaycxt _panel" style="padding: 16px;"> <div>{{ relay.inbox }}</div> <div class="status"> <i v-if="relay.status === 'accepted'" class="ti ti-check icon accepted"></i> diff --git a/packages/frontend/src/pages/admin/security.vue b/packages/frontend/src/pages/admin/security.vue index 6bd26e8325..92f3c259b9 100644 --- a/packages/frontend/src/pages/admin/security.vue +++ b/packages/frontend/src/pages/admin/security.vue @@ -54,7 +54,7 @@ </FormSwitch> --> - <FormButton primary @click="save"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</FormButton> + <MkButton primary @click="save"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</MkButton> </div> </FormFolder> @@ -92,7 +92,7 @@ <template #label>Summaly Proxy URL</template> </FormInput> - <FormButton primary @click="save"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</FormButton> + <MkButton primary @click="save"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</MkButton> </div> </FormFolder> </div> @@ -112,7 +112,7 @@ import FormInfo from '@/components/MkInfo.vue'; import FormSuspense from '@/components/form/suspense.vue'; import FormRange from '@/components/form/range.vue'; import FormInput from '@/components/form/input.vue'; -import FormButton from '@/components/MkButton.vue'; +import MkButton from '@/components/MkButton.vue'; import * as os from '@/os'; import { fetchInstance } from '@/instance'; import { i18n } from '@/i18n'; diff --git a/packages/frontend/src/pages/announcements.vue b/packages/frontend/src/pages/announcements.vue index 6a93b3b9fa..702cf05e68 100644 --- a/packages/frontend/src/pages/announcements.vue +++ b/packages/frontend/src/pages/announcements.vue @@ -2,14 +2,14 @@ <MkStickyContainer> <template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template> <MkSpacer :content-max="800"> - <MkPagination v-slot="{items}" :pagination="pagination" class="ruryvtyk _content"> - <section v-for="(announcement, i) in items" :key="announcement.id" class="_card announcement"> - <div class="_title"><span v-if="$i && !announcement.isRead">🆕 </span>{{ announcement.title }}</div> - <div class="_content"> + <MkPagination v-slot="{items}" :pagination="pagination" class="ruryvtyk _autoGap"> + <section v-for="(announcement, i) in items" :key="announcement.id" class="announcement _panel"> + <div class="header"><span v-if="$i && !announcement.isRead">🆕 </span>{{ announcement.title }}</div> + <div class="content"> <Mfm :text="announcement.text"/> <img v-if="announcement.imageUrl" :src="announcement.imageUrl"/> </div> - <div v-if="$i && !announcement.isRead" class="_footer"> + <div v-if="$i && !announcement.isRead" class="footer"> <MkButton primary @click="read(items, announcement, i)"><i class="ti ti-check"></i> {{ $ts.gotIt }}</MkButton> </div> </section> @@ -53,17 +53,24 @@ definePageMetadata({ <style lang="scss" scoped> .ruryvtyk { > .announcement { - &:not(:last-child) { - margin-bottom: var(--margin); + > .header { + padding: 16px; + font-weight: bold; } - > ._content { + > .content { + padding: 0 16px; + > img { display: block; max-height: 300px; max-width: 100%; } } + + > .footer { + padding: 16px; + } } } </style> diff --git a/packages/frontend/src/pages/antenna-timeline.vue b/packages/frontend/src/pages/antenna-timeline.vue index af7e95d543..bf3004f280 100644 --- a/packages/frontend/src/pages/antenna-timeline.vue +++ b/packages/frontend/src/pages/antenna-timeline.vue @@ -3,7 +3,7 @@ <template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template> <div ref="rootEl" v-hotkey.global="keymap" class="tqmomfks"> <div v-if="queue > 0" class="new"><button class="_buttonPrimary" @click="top()">{{ $ts.newNoteRecived }}</button></div> - <div class="tl _block"> + <div class="tl"> <XTimeline ref="tlEl" :key="antennaId" class="tl" diff --git a/packages/frontend/src/pages/auth.form.vue b/packages/frontend/src/pages/auth.form.vue index 1546735266..801295fce9 100644 --- a/packages/frontend/src/pages/auth.form.vue +++ b/packages/frontend/src/pages/auth.form.vue @@ -1,18 +1,18 @@ <template> -<section class="_section"> - <div class="_title">{{ $t('_auth.shareAccess', { name: app.name }) }}</div> - <div class="_content"> +<section class=""> + <div class="">{{ $t('_auth.shareAccess', { name: app.name }) }}</div> + <div class=""> <h2>{{ app.name }}</h2> <p class="id">{{ app.id }}</p> <p class="description">{{ app.description }}</p> </div> - <div class="_content"> + <div class=""> <h2>{{ $ts._auth.permissionAsk }}</h2> <ul> <li v-for="p in app.permission" :key="p">{{ $t(`_permissions.${p}`) }}</li> </ul> </div> - <div class="_footer"> + <div class=""> <MkButton inline @click="cancel">{{ $ts.cancel }}</MkButton> <MkButton inline primary @click="accept">{{ $ts.accept }}</MkButton> </div> diff --git a/packages/frontend/src/pages/channels.vue b/packages/frontend/src/pages/channels.vue index cc8375f636..9043d06c52 100644 --- a/packages/frontend/src/pages/channels.vue +++ b/packages/frontend/src/pages/channels.vue @@ -2,17 +2,17 @@ <MkStickyContainer> <template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template> <MkSpacer :content-max="700"> - <div v-if="tab === 'featured'" class="_content grwlizim featured"> + <div v-if="tab === 'featured'" class="grwlizim featured"> <MkPagination v-slot="{items}" :pagination="featuredPagination"> <MkChannelPreview v-for="channel in items" :key="channel.id" class="_margin" :channel="channel"/> </MkPagination> </div> - <div v-else-if="tab === 'following'" class="_content grwlizim following"> + <div v-else-if="tab === 'following'" class="grwlizim following"> <MkPagination v-slot="{items}" :pagination="followingPagination"> <MkChannelPreview v-for="channel in items" :key="channel.id" class="_margin" :channel="channel"/> </MkPagination> </div> - <div v-else-if="tab === 'owned'" class="_content grwlizim owned"> + <div v-else-if="tab === 'owned'" class="grwlizim owned"> <MkButton class="new" @click="create()"><i class="ti ti-plus"></i></MkButton> <MkPagination v-slot="{items}" :pagination="ownedPagination"> <MkChannelPreview v-for="channel in items" :key="channel.id" class="_margin" :channel="channel"/> diff --git a/packages/frontend/src/pages/flash/flash-edit.vue b/packages/frontend/src/pages/flash/flash-edit.vue index f890564884..b9b588a121 100644 --- a/packages/frontend/src/pages/flash/flash-edit.vue +++ b/packages/frontend/src/pages/flash/flash-edit.vue @@ -12,7 +12,7 @@ <MkTextarea v-model="script" class="_monospace" tall spellcheck="false"> <template #label>{{ i18n.ts._play.script }}</template> </MkTextarea> - <div style="display: flex; gap: var(--margin); flex-wrap: wrap;"> + <div class="_buttons"> <MkButton primary @click="save"><i class="ti ti-check"></i> {{ i18n.ts.save }}</MkButton> <MkButton @click="show"><i class="ti ti-eye"></i> {{ i18n.ts.show }}</MkButton> </div> diff --git a/packages/frontend/src/pages/gallery/edit.vue b/packages/frontend/src/pages/gallery/edit.vue index c8111d7890..0e3a1ce061 100644 --- a/packages/frontend/src/pages/gallery/edit.vue +++ b/packages/frontend/src/pages/gallery/edit.vue @@ -16,15 +16,15 @@ <div class="name">{{ file.name }}</div> <button v-tooltip="i18n.ts.remove" class="remove _button" @click="remove(file)"><i class="ti ti-x"></i></button> </div> - <FormButton primary @click="selectFile"><i class="ti ti-plus"></i> {{ i18n.ts.attachFile }}</FormButton> + <MkButton primary @click="selectFile"><i class="ti ti-plus"></i> {{ i18n.ts.attachFile }}</MkButton> </div> <FormSwitch v-model="isSensitive">{{ i18n.ts.markAsSensitive }}</FormSwitch> - <FormButton v-if="postId" primary @click="save"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</FormButton> - <FormButton v-else primary @click="save"><i class="ti ti-device-floppy"></i> {{ i18n.ts.publish }}</FormButton> + <MkButton v-if="postId" primary @click="save"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</MkButton> + <MkButton v-else primary @click="save"><i class="ti ti-device-floppy"></i> {{ i18n.ts.publish }}</MkButton> - <FormButton v-if="postId" danger @click="del"><i class="ti ti-trash"></i> {{ i18n.ts.delete }}</FormButton> + <MkButton v-if="postId" danger @click="del"><i class="ti ti-trash"></i> {{ i18n.ts.delete }}</MkButton> </FormSuspense> </MkSpacer> </MkStickyContainer> @@ -32,7 +32,7 @@ <script lang="ts" setup> import { computed, inject, watch } from 'vue'; -import FormButton from '@/components/MkButton.vue'; +import MkButton from '@/components/MkButton.vue'; import FormInput from '@/components/form/input.vue'; import FormTextarea from '@/components/form/textarea.vue'; import FormSwitch from '@/components/form/switch.vue'; diff --git a/packages/frontend/src/pages/gallery/post.vue b/packages/frontend/src/pages/gallery/post.vue index b1dc872ff9..30f63cf3aa 100644 --- a/packages/frontend/src/pages/gallery/post.vue +++ b/packages/frontend/src/pages/gallery/post.vue @@ -10,7 +10,7 @@ <img :src="file.url"/> </div> </div> - <div class="body _block"> + <div class="body"> <div class="title">{{ post.title }}</div> <div class="description"><Mfm :text="post.description"/></div> <div class="info"> diff --git a/packages/frontend/src/pages/messaging/index.vue b/packages/frontend/src/pages/messaging/index.vue index 3fb5047a04..e751754503 100644 --- a/packages/frontend/src/pages/messaging/index.vue +++ b/packages/frontend/src/pages/messaging/index.vue @@ -10,7 +10,7 @@ v-for="(message, i) in messages" :key="message.id" v-anim="i" - class="message _block" + class="message" :class="{ isMe: isMe(message), isRead: message.groupId ? message.reads.includes($i.id) : message.isRead }" :to="message.groupId ? `/my/messaging/group/${message.groupId}` : `/my/messaging/${getAcct(isMe(message) ? message.recipient : message.user)}`" :data-index="i" diff --git a/packages/frontend/src/pages/messaging/messaging-room.form.vue b/packages/frontend/src/pages/messaging/messaging-room.form.vue index 2c54c6f71f..a0fa2677d0 100644 --- a/packages/frontend/src/pages/messaging/messaging-room.form.vue +++ b/packages/frontend/src/pages/messaging/messaging-room.form.vue @@ -1,6 +1,6 @@ <template> <div - class="pemppnzi _block" + class="pemppnzi" @dragover.stop="onDragover" @drop.stop="onDrop" > diff --git a/packages/frontend/src/pages/messaging/messaging-room.vue b/packages/frontend/src/pages/messaging/messaging-room.vue index f0a36fb8b1..fa08b1cb72 100644 --- a/packages/frontend/src/pages/messaging/messaging-room.vue +++ b/packages/frontend/src/pages/messaging/messaging-room.vue @@ -1,11 +1,11 @@ <template> <div ref="rootEl" - class="_section" + class="" @dragover.prevent.stop="onDragover" @drop.prevent.stop="onDrop" > - <div class="_content mk-messaging-room"> + <div class="mk-messaging-room"> <div class="body"> <MkPagination v-if="pagination" ref="pagingComponent" :key="userAcct || groupId" :pagination="pagination"> <template #empty> diff --git a/packages/frontend/src/pages/mfm-cheat-sheet.vue b/packages/frontend/src/pages/mfm-cheat-sheet.vue index 7c85dfb7ad..2683affc42 100644 --- a/packages/frontend/src/pages/mfm-cheat-sheet.vue +++ b/packages/frontend/src/pages/mfm-cheat-sheet.vue @@ -4,7 +4,7 @@ <MkSpacer :content-max="800"> <div class="mwysmxbg"> <div>{{ i18n.ts._mfm.intro }}</div> - <div class="section _block"> + <div class="section"> <div class="title">{{ i18n.ts._mfm.mention }}</div> <div class="content"> <p>{{ i18n.ts._mfm.mentionDescription }}</p> @@ -14,7 +14,7 @@ </div> </div> </div> - <div class="section _block"> + <div class="section"> <div class="title">{{ i18n.ts._mfm.hashtag }}</div> <div class="content"> <p>{{ i18n.ts._mfm.hashtagDescription }}</p> @@ -24,7 +24,7 @@ </div> </div> </div> - <div class="section _block"> + <div class="section"> <div class="title">{{ i18n.ts._mfm.url }}</div> <div class="content"> <p>{{ i18n.ts._mfm.urlDescription }}</p> @@ -34,7 +34,7 @@ </div> </div> </div> - <div class="section _block"> + <div class="section"> <div class="title">{{ i18n.ts._mfm.link }}</div> <div class="content"> <p>{{ i18n.ts._mfm.linkDescription }}</p> @@ -44,7 +44,7 @@ </div> </div> </div> - <div class="section _block"> + <div class="section"> <div class="title">{{ i18n.ts._mfm.emoji }}</div> <div class="content"> <p>{{ i18n.ts._mfm.emojiDescription }}</p> @@ -54,7 +54,7 @@ </div> </div> </div> - <div class="section _block"> + <div class="section"> <div class="title">{{ i18n.ts._mfm.bold }}</div> <div class="content"> <p>{{ i18n.ts._mfm.boldDescription }}</p> @@ -64,7 +64,7 @@ </div> </div> </div> - <div class="section _block"> + <div class="section"> <div class="title">{{ i18n.ts._mfm.small }}</div> <div class="content"> <p>{{ i18n.ts._mfm.smallDescription }}</p> @@ -74,7 +74,7 @@ </div> </div> </div> - <div class="section _block"> + <div class="section"> <div class="title">{{ i18n.ts._mfm.quote }}</div> <div class="content"> <p>{{ i18n.ts._mfm.quoteDescription }}</p> @@ -84,7 +84,7 @@ </div> </div> </div> - <div class="section _block"> + <div class="section"> <div class="title">{{ i18n.ts._mfm.center }}</div> <div class="content"> <p>{{ i18n.ts._mfm.centerDescription }}</p> @@ -94,7 +94,7 @@ </div> </div> </div> - <div class="section _block"> + <div class="section"> <div class="title">{{ i18n.ts._mfm.inlineCode }}</div> <div class="content"> <p>{{ i18n.ts._mfm.inlineCodeDescription }}</p> @@ -104,7 +104,7 @@ </div> </div> </div> - <div class="section _block"> + <div class="section"> <div class="title">{{ i18n.ts._mfm.blockCode }}</div> <div class="content"> <p>{{ i18n.ts._mfm.blockCodeDescription }}</p> @@ -114,7 +114,7 @@ </div> </div> </div> - <div class="section _block"> + <div class="section"> <div class="title">{{ i18n.ts._mfm.inlineMath }}</div> <div class="content"> <p>{{ i18n.ts._mfm.inlineMathDescription }}</p> @@ -125,7 +125,7 @@ </div> </div> <!-- deprecated - <div class="section _block"> + <div class="section"> <div class="title">{{ i18n.ts._mfm.search }}</div> <div class="content"> <p>{{ i18n.ts._mfm.searchDescription }}</p> @@ -136,7 +136,7 @@ </div> </div> --> - <div class="section _block"> + <div class="section"> <div class="title">{{ i18n.ts._mfm.flip }}</div> <div class="content"> <p>{{ i18n.ts._mfm.flipDescription }}</p> @@ -146,7 +146,7 @@ </div> </div> </div> - <div class="section _block"> + <div class="section"> <div class="title">{{ i18n.ts._mfm.font }}</div> <div class="content"> <p>{{ i18n.ts._mfm.fontDescription }}</p> @@ -156,7 +156,7 @@ </div> </div> </div> - <div class="section _block"> + <div class="section"> <div class="title">{{ i18n.ts._mfm.x2 }}</div> <div class="content"> <p>{{ i18n.ts._mfm.x2Description }}</p> @@ -166,7 +166,7 @@ </div> </div> </div> - <div class="section _block"> + <div class="section"> <div class="title">{{ i18n.ts._mfm.x3 }}</div> <div class="content"> <p>{{ i18n.ts._mfm.x3Description }}</p> @@ -176,7 +176,7 @@ </div> </div> </div> - <div class="section _block"> + <div class="section"> <div class="title">{{ i18n.ts._mfm.x4 }}</div> <div class="content"> <p>{{ i18n.ts._mfm.x4Description }}</p> @@ -186,7 +186,7 @@ </div> </div> </div> - <div class="section _block"> + <div class="section"> <div class="title">{{ i18n.ts._mfm.blur }}</div> <div class="content"> <p>{{ i18n.ts._mfm.blurDescription }}</p> @@ -196,7 +196,7 @@ </div> </div> </div> - <div class="section _block"> + <div class="section"> <div class="title">{{ i18n.ts._mfm.jelly }}</div> <div class="content"> <p>{{ i18n.ts._mfm.jellyDescription }}</p> @@ -206,7 +206,7 @@ </div> </div> </div> - <div class="section _block"> + <div class="section"> <div class="title">{{ i18n.ts._mfm.tada }}</div> <div class="content"> <p>{{ i18n.ts._mfm.tadaDescription }}</p> @@ -216,7 +216,7 @@ </div> </div> </div> - <div class="section _block"> + <div class="section"> <div class="title">{{ i18n.ts._mfm.jump }}</div> <div class="content"> <p>{{ i18n.ts._mfm.jumpDescription }}</p> @@ -226,7 +226,7 @@ </div> </div> </div> - <div class="section _block"> + <div class="section"> <div class="title">{{ i18n.ts._mfm.bounce }}</div> <div class="content"> <p>{{ i18n.ts._mfm.bounceDescription }}</p> @@ -236,7 +236,7 @@ </div> </div> </div> - <div class="section _block"> + <div class="section"> <div class="title">{{ i18n.ts._mfm.spin }}</div> <div class="content"> <p>{{ i18n.ts._mfm.spinDescription }}</p> @@ -246,7 +246,7 @@ </div> </div> </div> - <div class="section _block"> + <div class="section"> <div class="title">{{ i18n.ts._mfm.shake }}</div> <div class="content"> <p>{{ i18n.ts._mfm.shakeDescription }}</p> @@ -256,7 +256,7 @@ </div> </div> </div> - <div class="section _block"> + <div class="section"> <div class="title">{{ i18n.ts._mfm.twitch }}</div> <div class="content"> <p>{{ i18n.ts._mfm.twitchDescription }}</p> @@ -266,7 +266,7 @@ </div> </div> </div> - <div class="section _block"> + <div class="section"> <div class="title">{{ i18n.ts._mfm.rainbow }}</div> <div class="content"> <p>{{ i18n.ts._mfm.rainbowDescription }}</p> @@ -276,7 +276,7 @@ </div> </div> </div> - <div class="section _block"> + <div class="section"> <div class="title">{{ i18n.ts._mfm.sparkle }}</div> <div class="content"> <p>{{ i18n.ts._mfm.sparkleDescription }}</p> @@ -286,7 +286,7 @@ </div> </div> </div> - <div class="section _block"> + <div class="section"> <div class="title">{{ i18n.ts._mfm.rotate }}</div> <div class="content"> <p>{{ i18n.ts._mfm.rotateDescription }}</p> @@ -296,7 +296,7 @@ </div> </div> </div> - <div class="section _block"> + <div class="section"> <div class="title">{{ i18n.ts._mfm.plain }}</div> <div class="content"> <p>{{ i18n.ts._mfm.plainDescription }}</p> diff --git a/packages/frontend/src/pages/miauth.vue b/packages/frontend/src/pages/miauth.vue index 11a8d30942..a01c7c5c4b 100644 --- a/packages/frontend/src/pages/miauth.vue +++ b/packages/frontend/src/pages/miauth.vue @@ -1,32 +1,32 @@ <template> <MkSpacer :content-max="800"> <div v-if="$i"> - <div v-if="state == 'waiting'" class="waiting _section"> - <div class="_content"> + <div v-if="state == 'waiting'" class="waiting"> + <div class=""> <MkLoading/> </div> </div> - <div v-if="state == 'denied'" class="denied _section"> - <div class="_content"> + <div v-if="state == 'denied'" class="denied"> + <div class=""> <p>{{ i18n.ts._auth.denied }}</p> </div> </div> - <div v-else-if="state == 'accepted'" class="accepted _section"> - <div class="_content"> + <div v-else-if="state == 'accepted'" class="accepted"> + <div class=""> <p v-if="callback">{{ i18n.ts._auth.callback }}<MkEllipsis/></p> <p v-else>{{ i18n.ts._auth.pleaseGoBack }}</p> </div> </div> - <div v-else class="_section"> - <div v-if="name" class="_title">{{ $t('_auth.shareAccess', { name: name }) }}</div> - <div v-else class="_title">{{ i18n.ts._auth.shareAccessAsk }}</div> - <div class="_content"> + <div v-else class=""> + <div v-if="name" class="">{{ $t('_auth.shareAccess', { name: name }) }}</div> + <div v-else class="">{{ i18n.ts._auth.shareAccessAsk }}</div> + <div class=""> <p>{{ i18n.ts._auth.permissionAsk }}</p> <ul> <li v-for="p in _permissions" :key="p">{{ $t(`_permissions.${p}`) }}</li> </ul> </div> - <div class="_footer"> + <div class=""> <MkButton inline @click="deny">{{ i18n.ts.cancel }}</MkButton> <MkButton inline primary @click="accept">{{ i18n.ts.accept }}</MkButton> </div> diff --git a/packages/frontend/src/pages/my-lists/index.vue b/packages/frontend/src/pages/my-lists/index.vue index 510e0173df..8a96b54881 100644 --- a/packages/frontend/src/pages/my-lists/index.vue +++ b/packages/frontend/src/pages/my-lists/index.vue @@ -5,7 +5,7 @@ <div class="qkcjvfiv"> <MkButton primary class="add" @click="create"><i class="ti ti-plus"></i> {{ i18n.ts.createList }}</MkButton> - <MkPagination v-slot="{items}" ref="pagingComponent" :pagination="pagination" class="lists _content"> + <MkPagination v-slot="{items}" ref="pagingComponent" :pagination="pagination" class="lists"> <MkA v-for="list in items" :key="list.id" class="list _panel" :to="`/my/lists/${ list.id }`"> <div class="name">{{ list.name }}</div> <MkAvatars :user-ids="list.userIds"/> diff --git a/packages/frontend/src/pages/my-lists/list.vue b/packages/frontend/src/pages/my-lists/list.vue index 48dde790e9..2c624d68f4 100644 --- a/packages/frontend/src/pages/my-lists/list.vue +++ b/packages/frontend/src/pages/my-lists/list.vue @@ -4,8 +4,8 @@ <MkSpacer :content-max="700"> <div class="mk-list-page"> <Transition :name="$store.state.animation ? '_transition_zoom' : ''" mode="out-in"> - <div v-if="list" class="_section"> - <div class="_content"> + <div v-if="list" class=""> + <div class=""> <MkButton inline @click="addUser()">{{ i18n.ts.addUser }}</MkButton> <MkButton inline @click="renameList()">{{ i18n.ts.rename }}</MkButton> <MkButton inline @click="deleteList()">{{ i18n.ts.delete }}</MkButton> @@ -14,9 +14,9 @@ </Transition> <Transition :name="$store.state.animation ? '_transition_zoom' : ''" mode="out-in"> - <div v-if="list" class="_section members _margin"> - <div class="_title">{{ i18n.ts.members }}</div> - <div class="_content"> + <div v-if="list" class="members _margin"> + <div class="">{{ i18n.ts.members }}</div> + <div class=""> <div class="users"> <div v-for="user in users" :key="user.id" class="user _panel"> <MkAvatar :user="user" class="avatar" :show-indicator="true"/> diff --git a/packages/frontend/src/pages/note.vue b/packages/frontend/src/pages/note.vue index 64032ac7f9..7fd74d2aee 100644 --- a/packages/frontend/src/pages/note.vue +++ b/packages/frontend/src/pages/note.vue @@ -6,7 +6,7 @@ <Transition :name="$store.state.animation ? 'fade' : ''" mode="out-in"> <div v-if="note" class="note"> <div v-if="showNext" class="_margin"> - <XNotes class="_content" :pagination="nextPagination" :no-gap="true"/> + <XNotes class="" :pagination="nextPagination" :no-gap="true"/> </div> <div class="main _margin"> @@ -15,7 +15,7 @@ <MkRemoteCaution v-if="note.user.host != null" :href="note.url ?? note.uri"/> <XNoteDetailed :key="note.id" v-model:note="note" class="note"/> </div> - <div v-if="clips && clips.length > 0" class="_content clips _margin"> + <div v-if="clips && clips.length > 0" class="clips _margin"> <div class="title">{{ i18n.ts.clip }}</div> <MkA v-for="item in clips" :key="item.id" :to="`/clips/${item.id}`" class="item _panel _margin"> <b>{{ item.name }}</b> @@ -29,7 +29,7 @@ </div> <div v-if="showPrev" class="_margin"> - <XNotes class="_content" :pagination="prevPagination" :no-gap="true"/> + <XNotes class="" :pagination="prevPagination" :no-gap="true"/> </div> </div> <MkError v-else-if="error" @retry="fetch()"/> diff --git a/packages/frontend/src/pages/page.vue b/packages/frontend/src/pages/page.vue index 07e297f5d7..7f0871a5fb 100644 --- a/packages/frontend/src/pages/page.vue +++ b/packages/frontend/src/pages/page.vue @@ -4,7 +4,7 @@ <MkSpacer :content-max="700"> <Transition :name="$store.state.animation ? 'fade' : ''" mode="out-in"> <div v-if="page" :key="page.id" class="xcukqgmh"> - <div class="_block main"> + <div class="main"> <!-- <div class="header"> <h1>{{ page.title }}</h1> diff --git a/packages/frontend/src/pages/reset-password.vue b/packages/frontend/src/pages/reset-password.vue index eb79733169..0e790190b2 100644 --- a/packages/frontend/src/pages/reset-password.vue +++ b/packages/frontend/src/pages/reset-password.vue @@ -8,7 +8,7 @@ <template #label>{{ i18n.ts.newPassword }}</template> </FormInput> - <FormButton primary @click="save">{{ i18n.ts.save }}</FormButton> + <MkButton primary @click="save">{{ i18n.ts.save }}</MkButton> </div> </MkSpacer> </MkStickyContainer> @@ -17,7 +17,7 @@ <script lang="ts" setup> import { defineAsyncComponent, onMounted } from 'vue'; import FormInput from '@/components/form/input.vue'; -import FormButton from '@/components/MkButton.vue'; +import MkButton from '@/components/MkButton.vue'; import * as os from '@/os'; import { i18n } from '@/i18n'; import { mainRouter } from '@/router'; diff --git a/packages/frontend/src/pages/settings/accounts.vue b/packages/frontend/src/pages/settings/accounts.vue index fc5972660d..e27b11cf6f 100644 --- a/packages/frontend/src/pages/settings/accounts.vue +++ b/packages/frontend/src/pages/settings/accounts.vue @@ -1,7 +1,7 @@ <template> <div class="_autoGap"> <FormSuspense :p="init"> - <FormButton primary @click="addAccount"><i class="ti ti-plus"></i> {{ i18n.ts.addAccount }}</FormButton> + <MkButton primary @click="addAccount"><i class="ti ti-plus"></i> {{ i18n.ts.addAccount }}</MkButton> <div v-for="account in accounts" :key="account.id" class="_panel _button lcjjdxlm" @click="menu(account, $event)"> <div class="avatar"> @@ -23,7 +23,7 @@ <script lang="ts" setup> import { defineAsyncComponent, ref } from 'vue'; import FormSuspense from '@/components/form/suspense.vue'; -import FormButton from '@/components/MkButton.vue'; +import MkButton from '@/components/MkButton.vue'; import * as os from '@/os'; import { getAccounts, addAccount as addAccounts, removeAccount as _removeAccount, login, $i } from '@/account'; import { i18n } from '@/i18n'; diff --git a/packages/frontend/src/pages/settings/api.vue b/packages/frontend/src/pages/settings/api.vue index 919b45d3e6..49240b01f7 100644 --- a/packages/frontend/src/pages/settings/api.vue +++ b/packages/frontend/src/pages/settings/api.vue @@ -1,6 +1,6 @@ <template> <div class="_autoGap"> - <FormButton primary @click="generateToken">{{ i18n.ts.generateAccessToken }}</FormButton> + <MkButton primary @click="generateToken">{{ i18n.ts.generateAccessToken }}</MkButton> <FormLink to="/settings/apps">{{ i18n.ts.manageAccessTokens }}</FormLink> <FormLink to="/api-console" :behavior="isDesktop ? 'window' : null">API console</FormLink> </div> @@ -9,7 +9,7 @@ <script lang="ts" setup> import { defineAsyncComponent, ref } from 'vue'; import FormLink from '@/components/form/link.vue'; -import FormButton from '@/components/MkButton.vue'; +import MkButton from '@/components/MkButton.vue'; import * as os from '@/os'; import { i18n } from '@/i18n'; import { definePageMetadata } from '@/scripts/page-metadata'; diff --git a/packages/frontend/src/pages/settings/delete-account.vue b/packages/frontend/src/pages/settings/delete-account.vue index c74b0f2b18..58abe6001a 100644 --- a/packages/frontend/src/pages/settings/delete-account.vue +++ b/packages/frontend/src/pages/settings/delete-account.vue @@ -2,14 +2,14 @@ <div class="_autoGap"> <FormInfo warn>{{ i18n.ts._accountDelete.mayTakeTime }}</FormInfo> <FormInfo>{{ i18n.ts._accountDelete.sendEmail }}</FormInfo> - <FormButton v-if="!$i.isDeleted" danger @click="deleteAccount">{{ i18n.ts._accountDelete.requestAccountDelete }}</FormButton> - <FormButton v-else disabled>{{ i18n.ts._accountDelete.inProgress }}</FormButton> + <MkButton v-if="!$i.isDeleted" danger @click="deleteAccount">{{ i18n.ts._accountDelete.requestAccountDelete }}</MkButton> + <MkButton v-else disabled>{{ i18n.ts._accountDelete.inProgress }}</MkButton> </div> </template> <script lang="ts" setup> import FormInfo from '@/components/MkInfo.vue'; -import FormButton from '@/components/MkButton.vue'; +import MkButton from '@/components/MkButton.vue'; import * as os from '@/os'; import { signout } from '@/account'; import { i18n } from '@/i18n'; diff --git a/packages/frontend/src/pages/settings/navbar.vue b/packages/frontend/src/pages/settings/navbar.vue index 33b970528a..1cbb72ab12 100644 --- a/packages/frontend/src/pages/settings/navbar.vue +++ b/packages/frontend/src/pages/settings/navbar.vue @@ -13,7 +13,7 @@ <!-- <MkRadio v-model="menuDisplay" value="hide" disabled>{{ i18n.ts._menuDisplay.hide }}</MkRadio>--> <!-- TODO: サイドバーを完全に隠せるようにすると、別途ハンバーガーボタンのようなものをUIに表示する必要があり面倒 --> </FormRadios> - <FormButton danger @click="reset()"><i class="ti ti-reload"></i> {{ i18n.ts.default }}</FormButton> + <MkButton danger @click="reset()"><i class="ti ti-reload"></i> {{ i18n.ts.default }}</MkButton> </div> </template> @@ -21,7 +21,7 @@ import { computed, ref, watch } from 'vue'; import FormTextarea from '@/components/form/textarea.vue'; import FormRadios from '@/components/form/radios.vue'; -import FormButton from '@/components/MkButton.vue'; +import MkButton from '@/components/MkButton.vue'; import * as os from '@/os'; import { navbarItemDef } from '@/navbar'; import { defaultStore } from '@/store'; diff --git a/packages/frontend/src/pages/settings/notifications.vue b/packages/frontend/src/pages/settings/notifications.vue index 872d26c3fb..372e6a3511 100644 --- a/packages/frontend/src/pages/settings/notifications.vue +++ b/packages/frontend/src/pages/settings/notifications.vue @@ -29,7 +29,7 @@ <script lang="ts" setup> import { defineAsyncComponent } from 'vue'; import { notificationTypes } from 'misskey-js'; -import FormButton from '@/components/MkButton.vue'; +import MkButton from '@/components/MkButton.vue'; import FormLink from '@/components/form/link.vue'; import FormSection from '@/components/form/section.vue'; import FormSwitch from '@/components/form/switch.vue'; diff --git a/packages/frontend/src/pages/settings/plugin.install.vue b/packages/frontend/src/pages/settings/plugin.install.vue index 1950e7f089..eda8456f80 100644 --- a/packages/frontend/src/pages/settings/plugin.install.vue +++ b/packages/frontend/src/pages/settings/plugin.install.vue @@ -7,7 +7,7 @@ </FormTextarea> <div> - <FormButton :disabled="code == null" primary inline @click="install"><i class="ti ti-check"></i> {{ i18n.ts.install }}</FormButton> + <MkButton :disabled="code == null" primary inline @click="install"><i class="ti ti-check"></i> {{ i18n.ts.install }}</MkButton> </div> </div> </template> @@ -17,7 +17,7 @@ import { defineAsyncComponent, nextTick, ref } from 'vue'; import { Interpreter, Parser, utils } from '@syuilo/aiscript'; import { v4 as uuid } from 'uuid'; import FormTextarea from '@/components/form/textarea.vue'; -import FormButton from '@/components/MkButton.vue'; +import MkButton from '@/components/MkButton.vue'; import FormInfo from '@/components/MkInfo.vue'; import * as os from '@/os'; import { ColdDeviceStorage } from '@/store'; diff --git a/packages/frontend/src/pages/settings/plugin.vue b/packages/frontend/src/pages/settings/plugin.vue index 3ff1d64b8e..13955288a3 100644 --- a/packages/frontend/src/pages/settings/plugin.vue +++ b/packages/frontend/src/pages/settings/plugin.vue @@ -22,7 +22,7 @@ <template #value>{{ plugin.permission }}</template> </MkKeyValue> - <div style="display: flex; gap: var(--margin); flex-wrap: wrap;"> + <div class="_buttons"> <MkButton v-if="plugin.config" inline @click="config(plugin)"><i class="ti ti-settings"></i> {{ i18n.ts.settings }}</MkButton> <MkButton inline danger @click="uninstall(plugin)"><i class="ti ti-trash"></i> {{ i18n.ts.uninstall }}</MkButton> </div> diff --git a/packages/frontend/src/pages/settings/reaction.vue b/packages/frontend/src/pages/settings/reaction.vue index 7f2e930785..c5ffce2772 100644 --- a/packages/frontend/src/pages/settings/reaction.vue +++ b/packages/frontend/src/pages/settings/reaction.vue @@ -45,9 +45,9 @@ </FormSwitch> <FormSection> - <div style="display: flex; gap: var(--margin); flex-wrap: wrap;"> - <FormButton inline @click="preview"><i class="ti ti-eye"></i> {{ i18n.ts.preview }}</FormButton> - <FormButton inline danger @click="setDefault"><i class="ti ti-reload"></i> {{ i18n.ts.default }}</FormButton> + <div class="_buttons"> + <MkButton inline @click="preview"><i class="ti ti-eye"></i> {{ i18n.ts.preview }}</MkButton> + <MkButton inline danger @click="setDefault"><i class="ti ti-reload"></i> {{ i18n.ts.default }}</MkButton> </div> </FormSection> </div> @@ -59,7 +59,7 @@ import Sortable from 'vuedraggable'; import FormInput from '@/components/form/input.vue'; import FormRadios from '@/components/form/radios.vue'; import FromSlot from '@/components/form/slot.vue'; -import FormButton from '@/components/MkButton.vue'; +import MkButton from '@/components/MkButton.vue'; import FormSection from '@/components/form/section.vue'; import FormSwitch from '@/components/form/switch.vue'; import * as os from '@/os'; diff --git a/packages/frontend/src/pages/settings/security.vue b/packages/frontend/src/pages/settings/security.vue index e3b63ea1a9..34d6059ace 100644 --- a/packages/frontend/src/pages/settings/security.vue +++ b/packages/frontend/src/pages/settings/security.vue @@ -2,7 +2,7 @@ <div class="_autoGap"> <FormSection> <template #label>{{ i18n.ts.password }}</template> - <FormButton primary @click="change()">{{ i18n.ts.changePassword }}</FormButton> + <MkButton primary @click="change()">{{ i18n.ts.changePassword }}</MkButton> </FormSection> <FormSection> @@ -30,7 +30,7 @@ <FormSection> <FormSlot> - <FormButton danger @click="regenerateToken"><i class="ti ti-refresh"></i> {{ i18n.ts.regenerateLoginToken }}</FormButton> + <MkButton danger @click="regenerateToken"><i class="ti ti-refresh"></i> {{ i18n.ts.regenerateLoginToken }}</MkButton> <template #caption>{{ i18n.ts.regenerateLoginTokenDescription }}</template> </FormSlot> </FormSection> @@ -41,7 +41,7 @@ import X2fa from './2fa.vue'; import FormSection from '@/components/form/section.vue'; import FormSlot from '@/components/form/slot.vue'; -import FormButton from '@/components/MkButton.vue'; +import MkButton from '@/components/MkButton.vue'; import MkPagination from '@/components/MkPagination.vue'; import * as os from '@/os'; import { i18n } from '@/i18n'; diff --git a/packages/frontend/src/pages/settings/sounds.sound.vue b/packages/frontend/src/pages/settings/sounds.sound.vue index 122b0d499f..20070e674b 100644 --- a/packages/frontend/src/pages/settings/sounds.sound.vue +++ b/packages/frontend/src/pages/settings/sounds.sound.vue @@ -8,9 +8,9 @@ <template #label>{{ i18n.ts.volume }}</template> </FormRange> - <div style="display: flex; gap: var(--margin); flex-wrap: wrap;"> - <FormButton inline @click="listen"><i class="ti ti-player-play"></i> {{ i18n.ts.listen }}</FormButton> - <FormButton inline primary @click="save"><i class="ti ti-check"></i> {{ i18n.ts.save }}</FormButton> + <div class="_buttons"> + <MkButton inline @click="listen"><i class="ti ti-player-play"></i> {{ i18n.ts.listen }}</MkButton> + <MkButton inline primary @click="save"><i class="ti ti-check"></i> {{ i18n.ts.save }}</MkButton> </div> </div> </template> @@ -18,7 +18,7 @@ <script lang="ts" setup> import { } from 'vue'; import FormSelect from '@/components/form/select.vue'; -import FormButton from '@/components/MkButton.vue'; +import MkButton from '@/components/MkButton.vue'; import FormRange from '@/components/form/range.vue'; import { i18n } from '@/i18n'; import { playFile, soundsTypes } from '@/scripts/sound'; diff --git a/packages/frontend/src/pages/settings/sounds.vue b/packages/frontend/src/pages/settings/sounds.vue index 4d7ea95be4..d6ccb4ccdb 100644 --- a/packages/frontend/src/pages/settings/sounds.vue +++ b/packages/frontend/src/pages/settings/sounds.vue @@ -16,7 +16,7 @@ </div> </FormSection> - <FormButton danger @click="reset()"><i class="ti ti-reload"></i> {{ i18n.ts.default }}</FormButton> + <MkButton danger @click="reset()"><i class="ti ti-reload"></i> {{ i18n.ts.default }}</MkButton> </div> </template> @@ -24,7 +24,7 @@ import { computed, ref } from 'vue'; import XSound from './sounds.sound.vue'; import FormRange from '@/components/form/range.vue'; -import FormButton from '@/components/MkButton.vue'; +import MkButton from '@/components/MkButton.vue'; import FormLink from '@/components/form/link.vue'; import FormSection from '@/components/form/section.vue'; import FormFolder from '@/components/form/folder.vue'; diff --git a/packages/frontend/src/pages/settings/statusbar.statusbar.vue b/packages/frontend/src/pages/settings/statusbar.statusbar.vue index 328a7f8e10..78ed1c4b45 100644 --- a/packages/frontend/src/pages/settings/statusbar.statusbar.vue +++ b/packages/frontend/src/pages/settings/statusbar.statusbar.vue @@ -74,8 +74,8 @@ </MkSwitch> </template> - <div style="display: flex; gap: var(--margin); flex-wrap: wrap;"> - <FormButton danger @click="del">{{ i18n.ts.remove }}</FormButton> + <div class="_buttons"> + <MkButton danger @click="del">{{ i18n.ts.remove }}</MkButton> </div> </div> </template> @@ -86,7 +86,7 @@ import FormSelect from '@/components/form/select.vue'; import MkInput from '@/components/form/input.vue'; import MkSwitch from '@/components/form/switch.vue'; import FormRadios from '@/components/form/radios.vue'; -import FormButton from '@/components/MkButton.vue'; +import MkButton from '@/components/MkButton.vue'; import FormRange from '@/components/form/range.vue'; import * as os from '@/os'; import { defaultStore } from '@/store'; diff --git a/packages/frontend/src/pages/settings/statusbar.vue b/packages/frontend/src/pages/settings/statusbar.vue index 1363c79748..2435bd6bad 100644 --- a/packages/frontend/src/pages/settings/statusbar.vue +++ b/packages/frontend/src/pages/settings/statusbar.vue @@ -5,7 +5,7 @@ <template #suffix>{{ x.name }}</template> <XStatusbar :_id="x.id" :user-lists="userLists"/> </FormFolder> - <FormButton primary @click="add">{{ i18n.ts.add }}</FormButton> + <MkButton primary @click="add">{{ i18n.ts.add }}</MkButton> </div> </template> @@ -15,7 +15,7 @@ import { v4 as uuid } from 'uuid'; import XStatusbar from './statusbar.statusbar.vue'; import FormRadios from '@/components/form/radios.vue'; import FormFolder from '@/components/form/folder.vue'; -import FormButton from '@/components/MkButton.vue'; +import MkButton from '@/components/MkButton.vue'; import * as os from '@/os'; import { defaultStore } from '@/store'; import { unisonReload } from '@/scripts/unison-reload'; diff --git a/packages/frontend/src/pages/settings/theme.install.vue b/packages/frontend/src/pages/settings/theme.install.vue index e08a03d0c9..b1010624c7 100644 --- a/packages/frontend/src/pages/settings/theme.install.vue +++ b/packages/frontend/src/pages/settings/theme.install.vue @@ -4,9 +4,9 @@ <template #label>{{ i18n.ts._theme.code }}</template> </FormTextarea> - <div style="display: flex; gap: var(--margin); flex-wrap: wrap;"> - <FormButton :disabled="installThemeCode == null" inline @click="() => preview(installThemeCode)"><i class="ti ti-eye"></i> {{ i18n.ts.preview }}</FormButton> - <FormButton :disabled="installThemeCode == null" primary inline @click="() => install(installThemeCode)"><i class="ti ti-check"></i> {{ i18n.ts.install }}</FormButton> + <div class="_buttons"> + <MkButton :disabled="installThemeCode == null" inline @click="() => preview(installThemeCode)"><i class="ti ti-eye"></i> {{ i18n.ts.preview }}</MkButton> + <MkButton :disabled="installThemeCode == null" primary inline @click="() => install(installThemeCode)"><i class="ti ti-check"></i> {{ i18n.ts.install }}</MkButton> </div> </div> </template> @@ -15,7 +15,7 @@ import { } from 'vue'; import JSON5 from 'json5'; import FormTextarea from '@/components/form/textarea.vue'; -import FormButton from '@/components/MkButton.vue'; +import MkButton from '@/components/MkButton.vue'; import { applyTheme, validateTheme } from '@/scripts/theme'; import * as os from '@/os'; import { addTheme, getThemes } from '@/theme-store'; diff --git a/packages/frontend/src/pages/settings/theme.manage.vue b/packages/frontend/src/pages/settings/theme.manage.vue index afbc40a18d..660e8ca02e 100644 --- a/packages/frontend/src/pages/settings/theme.manage.vue +++ b/packages/frontend/src/pages/settings/theme.manage.vue @@ -20,7 +20,7 @@ <template #label>{{ i18n.ts._theme.code }}</template> <template #caption><button class="_textButton" @click="copyThemeCode()">{{ i18n.ts.copy }}</button></template> </FormTextarea> - <FormButton v-if="!builtinThemes.some(t => t.id == selectedTheme.id)" danger @click="uninstall()"><i class="ti ti-trash"></i> {{ i18n.ts.uninstall }}</FormButton> + <MkButton v-if="!builtinThemes.some(t => t.id == selectedTheme.id)" danger @click="uninstall()"><i class="ti ti-trash"></i> {{ i18n.ts.uninstall }}</MkButton> </template> </div> </template> @@ -31,7 +31,7 @@ import JSON5 from 'json5'; import FormTextarea from '@/components/form/textarea.vue'; import FormSelect from '@/components/form/select.vue'; import FormInput from '@/components/form/input.vue'; -import FormButton from '@/components/MkButton.vue'; +import MkButton from '@/components/MkButton.vue'; import { Theme, getBuiltinThemesRef } from '@/scripts/theme'; import copyToClipboard from '@/scripts/copy-to-clipboard'; import * as os from '@/os'; diff --git a/packages/frontend/src/pages/settings/theme.vue b/packages/frontend/src/pages/settings/theme.vue index d2fc0980d4..17aaa6e21b 100644 --- a/packages/frontend/src/pages/settings/theme.vue +++ b/packages/frontend/src/pages/settings/theme.vue @@ -60,8 +60,8 @@ </div> </FormSection> - <FormButton v-if="wallpaper == null" @click="setWallpaper">{{ i18n.ts.setWallpaper }}</FormButton> - <FormButton v-else @click="wallpaper = null">{{ i18n.ts.removeWallpaper }}</FormButton> + <MkButton v-if="wallpaper == null" @click="setWallpaper">{{ i18n.ts.setWallpaper }}</MkButton> + <MkButton v-else @click="wallpaper = null">{{ i18n.ts.removeWallpaper }}</MkButton> </div> </template> @@ -72,7 +72,7 @@ import FormSwitch from '@/components/form/switch.vue'; import FormSelect from '@/components/form/select.vue'; import FormSection from '@/components/form/section.vue'; import FormLink from '@/components/form/link.vue'; -import FormButton from '@/components/MkButton.vue'; +import MkButton from '@/components/MkButton.vue'; import { getBuiltinThemesRef } from '@/scripts/theme'; import { selectFile } from '@/scripts/select-file'; import { isDeviceDarkmode } from '@/scripts/is-device-darkmode'; diff --git a/packages/frontend/src/pages/settings/webhook.edit.vue b/packages/frontend/src/pages/settings/webhook.edit.vue index 439a27ed39..4bb2c1d27e 100644 --- a/packages/frontend/src/pages/settings/webhook.edit.vue +++ b/packages/frontend/src/pages/settings/webhook.edit.vue @@ -29,8 +29,8 @@ <FormSwitch v-model="active">Active</FormSwitch> - <div style="display: flex; gap: var(--margin); flex-wrap: wrap;"> - <FormButton primary inline @click="save"><i class="ti ti-check"></i> {{ i18n.ts.save }}</FormButton> + <div class="_buttons"> + <MkButton primary inline @click="save"><i class="ti ti-check"></i> {{ i18n.ts.save }}</MkButton> </div> </div> </template> @@ -40,7 +40,7 @@ import { } from 'vue'; import FormInput from '@/components/form/input.vue'; import FormSection from '@/components/form/section.vue'; import FormSwitch from '@/components/form/switch.vue'; -import FormButton from '@/components/MkButton.vue'; +import MkButton from '@/components/MkButton.vue'; import * as os from '@/os'; import { i18n } from '@/i18n'; import { definePageMetadata } from '@/scripts/page-metadata'; diff --git a/packages/frontend/src/pages/settings/webhook.new.vue b/packages/frontend/src/pages/settings/webhook.new.vue index 3ef19e3f54..5b34de55f0 100644 --- a/packages/frontend/src/pages/settings/webhook.new.vue +++ b/packages/frontend/src/pages/settings/webhook.new.vue @@ -27,8 +27,8 @@ </div> </FormSection> - <div style="display: flex; gap: var(--margin); flex-wrap: wrap;"> - <FormButton primary inline @click="create"><i class="ti ti-check"></i> {{ i18n.ts.create }}</FormButton> + <div class="_buttons"> + <MkButton primary inline @click="create"><i class="ti ti-check"></i> {{ i18n.ts.create }}</MkButton> </div> </div> </template> @@ -38,7 +38,7 @@ import { } from 'vue'; import FormInput from '@/components/form/input.vue'; import FormSection from '@/components/form/section.vue'; import FormSwitch from '@/components/form/switch.vue'; -import FormButton from '@/components/MkButton.vue'; +import MkButton from '@/components/MkButton.vue'; import * as os from '@/os'; import { i18n } from '@/i18n'; import { definePageMetadata } from '@/scripts/page-metadata'; diff --git a/packages/frontend/src/pages/tag.vue b/packages/frontend/src/pages/tag.vue index 72775ed5c9..5d6d01d2ae 100644 --- a/packages/frontend/src/pages/tag.vue +++ b/packages/frontend/src/pages/tag.vue @@ -2,7 +2,7 @@ <MkStickyContainer> <template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template> <MkSpacer :content-max="800"> - <XNotes class="_content" :pagination="pagination"/> + <XNotes class="" :pagination="pagination"/> </MkSpacer> </MkStickyContainer> </template> diff --git a/packages/frontend/src/pages/theme-editor.vue b/packages/frontend/src/pages/theme-editor.vue index 4b058a5366..8f2ad4d668 100644 --- a/packages/frontend/src/pages/theme-editor.vue +++ b/packages/frontend/src/pages/theme-editor.vue @@ -49,7 +49,7 @@ <FormTextarea v-model="themeCode" tall> <template #label>{{ i18n.ts._theme.code }}</template> </FormTextarea> - <FormButton primary @click="applyThemeCode">{{ i18n.ts.apply }}</FormButton> + <MkButton primary @click="applyThemeCode">{{ i18n.ts.apply }}</MkButton> </div> </FormFolder> @@ -74,7 +74,7 @@ import tinycolor from 'tinycolor2'; import { v4 as uuid } from 'uuid'; import JSON5 from 'json5'; -import FormButton from '@/components/MkButton.vue'; +import MkButton from '@/components/MkButton.vue'; import FormTextarea from '@/components/form/textarea.vue'; import FormFolder from '@/components/form/folder.vue'; diff --git a/packages/frontend/src/pages/timeline.vue b/packages/frontend/src/pages/timeline.vue index 75f62d38f7..5f08d32532 100644 --- a/packages/frontend/src/pages/timeline.vue +++ b/packages/frontend/src/pages/timeline.vue @@ -3,11 +3,11 @@ <template #header><MkPageHeader v-model:tab="src" :actions="headerActions" :tabs="headerTabs" :display-my-avatar="true"/></template> <MkSpacer :content-max="800"> <div ref="rootEl" v-hotkey.global="keymap" class="cmuxhskf"> - <XTutorial v-if="$store.reactiveState.tutorial.value != -1" class="tutorial _block"/> - <XPostForm v-if="$store.reactiveState.showFixedPostForm.value" class="post-form _block" fixed/> + <XTutorial v-if="$store.reactiveState.tutorial.value != -1" class="tutorial"/> + <XPostForm v-if="$store.reactiveState.showFixedPostForm.value" class="post-form" fixed/> <div v-if="queue > 0" class="new"><button class="_buttonPrimary" @click="top()">{{ i18n.ts.newNoteRecived }}</button></div> - <div class="tl _block"> + <div class="tl"> <XTimeline ref="tl" :key="src" class="tl" diff --git a/packages/frontend/src/pages/user-info.vue b/packages/frontend/src/pages/user-info.vue index d879c95dd7..c2b5cd5f4a 100644 --- a/packages/frontend/src/pages/user-info.vue +++ b/packages/frontend/src/pages/user-info.vue @@ -75,7 +75,7 @@ </MkKeyValue> </div> - <FormButton v-if="user.host != null" @click="updateRemoteUser"><i class="ti ti-refresh"></i> {{ i18n.ts.updateRemoteUser }}</FormButton> + <MkButton v-if="user.host != null" @click="updateRemoteUser"><i class="ti ti-refresh"></i> {{ i18n.ts.updateRemoteUser }}</MkButton> <FormFolder> <template #label>Raw</template> @@ -92,8 +92,8 @@ <FormSwitch v-model="suspended" @update:model-value="toggleSuspend">{{ i18n.ts.suspend }}</FormSwitch> {{ i18n.ts.reflectMayTakeTime }} <div> - <FormButton v-if="user.host == null && iAmModerator" inline style="margin-right: 8px;" @click="resetPassword"><i class="ti ti-key"></i> {{ i18n.ts.resetPassword }}</FormButton> - <FormButton v-if="$i.isAdmin" inline danger @click="deleteAccount">{{ i18n.ts.deleteAccount }}</FormButton> + <MkButton v-if="user.host == null && iAmModerator" inline style="margin-right: 8px;" @click="resetPassword"><i class="ti ti-key"></i> {{ i18n.ts.resetPassword }}</MkButton> + <MkButton v-if="$i.isAdmin" inline danger @click="deleteAccount">{{ i18n.ts.deleteAccount }}</MkButton> </div> <FormTextarea v-model="moderationNote" manual-save> <template #label>Moderation note</template> @@ -162,7 +162,7 @@ import FormTextarea from '@/components/form/textarea.vue'; import FormSwitch from '@/components/form/switch.vue'; import FormLink from '@/components/form/link.vue'; import FormSection from '@/components/form/section.vue'; -import FormButton from '@/components/MkButton.vue'; +import MkButton from '@/components/MkButton.vue'; import FormInput from '@/components/form/input.vue'; import FormSplit from '@/components/form/split.vue'; import FormFolder from '@/components/form/folder.vue'; diff --git a/packages/frontend/src/pages/user-list-timeline.vue b/packages/frontend/src/pages/user-list-timeline.vue index 542c280594..6817d44d8c 100644 --- a/packages/frontend/src/pages/user-list-timeline.vue +++ b/packages/frontend/src/pages/user-list-timeline.vue @@ -3,7 +3,7 @@ <template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template> <div ref="rootEl" class="eqqrhokj"> <div v-if="queue > 0" class="new"><button class="_buttonPrimary" @click="top()">{{ i18n.ts.newNoteRecived }}</button></div> - <div class="tl _block"> + <div class="tl"> <XTimeline ref="tlEl" :key="listId" class="tl" diff --git a/packages/frontend/src/pages/user/home.vue b/packages/frontend/src/pages/user/home.vue index 40da7f9d57..bdf43bb04a 100644 --- a/packages/frontend/src/pages/user/home.vue +++ b/packages/frontend/src/pages/user/home.vue @@ -9,7 +9,7 @@ <div class="profile"> <MkRemoteCaution v-if="user.host != null" :href="user.url" class="warn"/> - <div :key="user.id" class="_block main"> + <div :key="user.id" class="main"> <div class="banner-container" :style="style"> <div ref="bannerEl" class="banner" :style="style"></div> <div class="fade"></div> @@ -87,7 +87,7 @@ <div class="contents"> <div v-if="user.pinnedNotes.length > 0" class="_margin"> - <XNote v-for="note in user.pinnedNotes" :key="note.id" class="note _block" :note="note" :pinned="true"/> + <XNote v-for="note in user.pinnedNotes" :key="note.id" class="note" :note="note" :pinned="true"/> </div> <MkInfo v-else-if="$i && $i.id === user.id">{{ i18n.ts.userPagePinTip }}</MkInfo> <template v-if="narrow"> diff --git a/packages/frontend/src/style.scss b/packages/frontend/src/style.scss index e0ff1c23e0..356e793bab 100644 --- a/packages/frontend/src/style.scss +++ b/packages/frontend/src/style.scss @@ -252,15 +252,6 @@ hr { overflow: clip; } -// TODO: 廃止 -._block { - @extend ._panel; - - & + ._block { - margin-top: var(--margin); - } -} - ._margin { margin: var(--margin) 0; } @@ -277,52 +268,10 @@ hr { gap: 0.75em; } -// TODO: 廃止 -._card { - @extend ._panel; - - // TODO: _cardTitle に - > ._title { - margin: 0; - padding: 22px 32px; - font-size: 1em; - border-bottom: solid 1px var(--panelHeaderDivider); - font-weight: bold; - background: var(--panelHeaderBg); - color: var(--panelHeaderFg); - - @media (max-width: 500px) { - padding: 16px; - font-size: 1em; - } - } - - // TODO: _cardContent に - > ._content { - padding: 32px; - - @media (max-width: 500px) { - padding: 16px; - } - - &._noPad { - padding: 0 !important; - } - - & + ._content { - border-top: solid 0.5px var(--divider); - } - } - - // TODO: _cardFooter に - > ._footer { - border-top: solid 0.5px var(--divider); - padding: 24px 32px; - - @media (max-width: 500px) { - padding: 16px; - } - } +._buttons { + display: flex; + gap: 8px; + flex-wrap: wrap; } ._borderButton { @@ -346,37 +295,6 @@ hr { contain: content; } -// TODO: 廃止 -._monolithic_ { - ._section:not(:empty) { - box-sizing: border-box; - padding: var(--root-margin, 32px); - - @media (max-width: 500px) { - --root-margin: 10px; - } - - & + ._section:not(:empty) { - border-top: solid 0.5px var(--divider); - } - } -} - -._narrow_ ._card { - > ._title { - padding: 16px; - font-size: 1em; - } - - > ._content { - padding: 16px; - } - - > ._footer { - padding: 16px; - } -} - ._acrylic { background: var(--acrylicPanel); -webkit-backdrop-filter: var(--blur, blur(15px)); diff --git a/packages/frontend/src/ui/deck/column.vue b/packages/frontend/src/ui/deck/column.vue index 775bdf6c1e..cb945aa14b 100644 --- a/packages/frontend/src/ui/deck/column.vue +++ b/packages/frontend/src/ui/deck/column.vue @@ -1,7 +1,7 @@ <template> <!-- sectionを利用しているのは、deck.vue側でcolumnに対してfirst-of-typeを効かせるため --> <section - v-hotkey="keymap" class="dnpfarvg _narrow_" + v-hotkey="keymap" class="dnpfarvg" :class="{ paged: isMainColumn, naked, active, isStacked, draghover, dragging, dropready }" @dragover.prevent.stop="onDragover" @dragleave="onDragleave" From aefc8fb7b54c47ac05d0c9d872b10b8177dc5f39 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Fri, 6 Jan 2023 09:56:33 +0900 Subject: [PATCH 27/60] :art: --- packages/frontend/src/init.ts | 1 + .../frontend/src/pages/settings/email.vue | 2 +- .../src/pages/settings/import-export.vue | 104 ++++++++++-------- .../frontend/src/pages/settings/security.vue | 2 +- packages/frontend/src/pages/user/home.vue | 10 +- 5 files changed, 65 insertions(+), 54 deletions(-) diff --git a/packages/frontend/src/init.ts b/packages/frontend/src/init.ts index 508d3262b3..45ade64127 100644 --- a/packages/frontend/src/init.ts +++ b/packages/frontend/src/init.ts @@ -172,6 +172,7 @@ import { getAccountFromId } from '@/scripts/get-account-from-id'; app.config.performance = true; } + // TODO: 廃止 app.config.globalProperties = { $i, $store: defaultStore, diff --git a/packages/frontend/src/pages/settings/email.vue b/packages/frontend/src/pages/settings/email.vue index 6b4b3a70c4..d7b03d9eac 100644 --- a/packages/frontend/src/pages/settings/email.vue +++ b/packages/frontend/src/pages/settings/email.vue @@ -1,6 +1,6 @@ <template> <div class="_autoGap"> - <FormSection> + <FormSection first> <template #label>{{ i18n.ts.emailAddress }}</template> <FormInput v-model="emailAddress" type="email" manual-save> <template #prefix><i class="ti ti-mail"></i></template> diff --git a/packages/frontend/src/pages/settings/import-export.vue b/packages/frontend/src/pages/settings/import-export.vue index 13dcc627a2..9b6a59bee2 100644 --- a/packages/frontend/src/pages/settings/import-export.vue +++ b/packages/frontend/src/pages/settings/import-export.vue @@ -1,6 +1,6 @@ <template> <div class="_autoGap"> - <FormSection> + <FormSection first> <template #label><i class="ti ti-pencil"></i> {{ i18n.ts._exportOrImport.allNotes }}</template> <FormFolder> <template #label>{{ i18n.ts.export }}</template> @@ -18,61 +18,71 @@ </FormSection> <FormSection> <template #label><i class="ti ti-users"></i> {{ i18n.ts._exportOrImport.followingList }}</template> - <FormFolder> - <template #label>{{ i18n.ts.export }}</template> - <template #icon><i class="ti ti-download"></i></template> - <FormSwitch v-model="excludeMutingUsers"> - {{ i18n.ts._exportOrImport.excludeMutingUsers }} - </FormSwitch> - <FormSwitch v-model="excludeInactiveUsers"> - {{ i18n.ts._exportOrImport.excludeInactiveUsers }} - </FormSwitch> - <MkButton primary :class="$style.button" inline @click="exportFollowing()"><i class="ti ti-download"></i> {{ i18n.ts.export }}</MkButton> - </FormFolder> - <FormFolder> - <template #label>{{ i18n.ts.import }}</template> - <template #icon><i class="ti ti-upload"></i></template> - <MkButton primary :class="$style.button" inline @click="importFollowing($event)"><i class="ti ti-upload"></i> {{ i18n.ts.import }}</MkButton> - </FormFolder> + <div class="_autoGap_half"> + <FormFolder> + <template #label>{{ i18n.ts.export }}</template> + <template #icon><i class="ti ti-download"></i></template> + <div class="_autoGap_half"> + <FormSwitch v-model="excludeMutingUsers"> + {{ i18n.ts._exportOrImport.excludeMutingUsers }} + </FormSwitch> + <FormSwitch v-model="excludeInactiveUsers"> + {{ i18n.ts._exportOrImport.excludeInactiveUsers }} + </FormSwitch> + <MkButton primary :class="$style.button" inline @click="exportFollowing()"><i class="ti ti-download"></i> {{ i18n.ts.export }}</MkButton> + </div> + </FormFolder> + <FormFolder> + <template #label>{{ i18n.ts.import }}</template> + <template #icon><i class="ti ti-upload"></i></template> + <MkButton primary :class="$style.button" inline @click="importFollowing($event)"><i class="ti ti-upload"></i> {{ i18n.ts.import }}</MkButton> + </FormFolder> + </div> </FormSection> <FormSection> <template #label><i class="ti ti-users"></i> {{ i18n.ts._exportOrImport.userLists }}</template> - <FormFolder> - <template #label>{{ i18n.ts.export }}</template> - <template #icon><i class="ti ti-download"></i></template> - <MkButton primary :class="$style.button" inline @click="exportUserLists()"><i class="ti ti-download"></i> {{ i18n.ts.export }}</MkButton> - </FormFolder> - <FormFolder> - <template #label>{{ i18n.ts.import }}</template> - <template #icon><i class="ti ti-upload"></i></template> - <MkButton primary :class="$style.button" inline @click="importUserLists($event)"><i class="ti ti-upload"></i> {{ i18n.ts.import }}</MkButton> - </FormFolder> + <div class="_autoGap_half"> + <FormFolder> + <template #label>{{ i18n.ts.export }}</template> + <template #icon><i class="ti ti-download"></i></template> + <MkButton primary :class="$style.button" inline @click="exportUserLists()"><i class="ti ti-download"></i> {{ i18n.ts.export }}</MkButton> + </FormFolder> + <FormFolder> + <template #label>{{ i18n.ts.import }}</template> + <template #icon><i class="ti ti-upload"></i></template> + <MkButton primary :class="$style.button" inline @click="importUserLists($event)"><i class="ti ti-upload"></i> {{ i18n.ts.import }}</MkButton> + </FormFolder> + </div> </FormSection> <FormSection> <template #label><i class="ti ti-user-off"></i> {{ i18n.ts._exportOrImport.muteList }}</template> - <FormFolder> - <template #label>{{ i18n.ts.export }}</template> - <template #icon><i class="ti ti-download"></i></template> - <MkButton primary :class="$style.button" inline @click="exportMuting()"><i class="ti ti-download"></i> {{ i18n.ts.export }}</MkButton> - </FormFolder> - <FormFolder> - <template #label>{{ i18n.ts.import }}</template> - <template #icon><i class="ti ti-upload"></i></template> - <MkButton primary :class="$style.button" inline @click="importMuting($event)"><i class="ti ti-upload"></i> {{ i18n.ts.import }}</MkButton> - </FormFolder> + <div class="_autoGap_half"> + <FormFolder> + <template #label>{{ i18n.ts.export }}</template> + <template #icon><i class="ti ti-download"></i></template> + <MkButton primary :class="$style.button" inline @click="exportMuting()"><i class="ti ti-download"></i> {{ i18n.ts.export }}</MkButton> + </FormFolder> + <FormFolder> + <template #label>{{ i18n.ts.import }}</template> + <template #icon><i class="ti ti-upload"></i></template> + <MkButton primary :class="$style.button" inline @click="importMuting($event)"><i class="ti ti-upload"></i> {{ i18n.ts.import }}</MkButton> + </FormFolder> + </div> </FormSection> <FormSection> <template #label><i class="ti ti-user-off"></i> {{ i18n.ts._exportOrImport.blockingList }}</template> - <FormFolder> - <template #label>{{ i18n.ts.export }}</template> - <template #icon><i class="ti ti-download"></i></template> - <MkButton primary :class="$style.button" inline @click="exportBlocking()"><i class="ti ti-download"></i> {{ i18n.ts.export }}</MkButton> - </FormFolder> - <FormFolder> - <template #label>{{ i18n.ts.import }}</template> - <template #icon><i class="ti ti-upload"></i></template> - <MkButton primary :class="$style.button" inline @click="importBlocking($event)"><i class="ti ti-upload"></i> {{ i18n.ts.import }}</MkButton> - </FormFolder> + <div class="_autoGap_half"> + <FormFolder> + <template #label>{{ i18n.ts.export }}</template> + <template #icon><i class="ti ti-download"></i></template> + <MkButton primary :class="$style.button" inline @click="exportBlocking()"><i class="ti ti-download"></i> {{ i18n.ts.export }}</MkButton> + </FormFolder> + <FormFolder> + <template #label>{{ i18n.ts.import }}</template> + <template #icon><i class="ti ti-upload"></i></template> + <MkButton primary :class="$style.button" inline @click="importBlocking($event)"><i class="ti ti-upload"></i> {{ i18n.ts.import }}</MkButton> + </FormFolder> + </div> </FormSection> </div> </template> diff --git a/packages/frontend/src/pages/settings/security.vue b/packages/frontend/src/pages/settings/security.vue index 34d6059ace..c83612292b 100644 --- a/packages/frontend/src/pages/settings/security.vue +++ b/packages/frontend/src/pages/settings/security.vue @@ -1,6 +1,6 @@ <template> <div class="_autoGap"> - <FormSection> + <FormSection first> <template #label>{{ i18n.ts.password }}</template> <MkButton primary @click="change()">{{ i18n.ts.changePassword }}</MkButton> </FormSection> diff --git a/packages/frontend/src/pages/user/home.vue b/packages/frontend/src/pages/user/home.vue index bdf43bb04a..11a7ee3b8a 100644 --- a/packages/frontend/src/pages/user/home.vue +++ b/packages/frontend/src/pages/user/home.vue @@ -1,15 +1,15 @@ <template> <MkSpacer :content-max="narrow ? 800 : 1100"> <div ref="rootEl" class="ftskorzw" :class="{ wide: !narrow }" style="container-type: inline-size;"> - <div class="main"> + <div class="main _autoGap"> <!-- TODO --> <!-- <div class="punished" v-if="user.isSuspended"><i class="ti ti-alert-triangle" style="margin-right: 8px;"></i> {{ i18n.ts.userSuspended }}</div> --> <!-- <div class="punished" v-if="user.isSilenced"><i class="ti ti-alert-triangle" style="margin-right: 8px;"></i> {{ i18n.ts.userSilenced }}</div> --> - <div class="profile"> + <div class="profile _autoGap"> <MkRemoteCaution v-if="user.host != null" :href="user.url" class="warn"/> - <div :key="user.id" class="main"> + <div :key="user.id" class="main _panel"> <div class="banner-container" :style="style"> <div ref="bannerEl" class="banner" :style="style"></div> <div class="fade"></div> @@ -86,8 +86,8 @@ </div> <div class="contents"> - <div v-if="user.pinnedNotes.length > 0" class="_margin"> - <XNote v-for="note in user.pinnedNotes" :key="note.id" class="note" :note="note" :pinned="true"/> + <div v-if="user.pinnedNotes.length > 0" class="_autoGap_half"> + <XNote v-for="note in user.pinnedNotes" :key="note.id" class="note _panel" :note="note" :pinned="true"/> </div> <MkInfo v-else-if="$i && $i.id === user.id">{{ i18n.ts.userPagePinTip }}</MkInfo> <template v-if="narrow"> From d25f214a09479f403c959e5bc7d88b017147380b Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Fri, 6 Jan 2023 09:59:17 +0900 Subject: [PATCH 28/60] :art: --- .../src/components/MkAbuseReportWindow.vue | 30 ++++++++++--------- .../frontend/src/components/MkDriveWindow.vue | 6 ++-- .../frontend/src/components/MkPageWindow.vue | 8 ++--- packages/frontend/src/components/MkWindow.vue | 1 + .../src/components/MkYoutubePlayer.vue | 6 ++-- 5 files changed, 27 insertions(+), 24 deletions(-) diff --git a/packages/frontend/src/components/MkAbuseReportWindow.vue b/packages/frontend/src/components/MkAbuseReportWindow.vue index e20cf1b735..6481c0d876 100644 --- a/packages/frontend/src/components/MkAbuseReportWindow.vue +++ b/packages/frontend/src/components/MkAbuseReportWindow.vue @@ -1,5 +1,5 @@ <template> -<XWindow ref="uiWindow" :initial-width="400" :initial-height="500" :can-resize="true" @closed="emit('closed')"> +<MkWindow ref="uiWindow" :initial-width="400" :initial-height="500" :can-resize="true" @closed="emit('closed')"> <template #header> <i class="ti ti-exclamation-circle" style="margin-right: 0.5em;"></i> <I18n :src="i18n.ts.reportAbuseOf" tag="span"> @@ -8,24 +8,26 @@ </template> </I18n> </template> - <div class="dpvffvvy _autoGap"> - <div class=""> - <MkTextarea v-model="comment"> - <template #label>{{ i18n.ts.details }}</template> - <template #caption>{{ i18n.ts.fillAbuseReportDescription }}</template> - </MkTextarea> + <MkSpacer :margin-min="20" :margin-max="28"> + <div class="dpvffvvy _autoGap"> + <div class=""> + <MkTextarea v-model="comment"> + <template #label>{{ i18n.ts.details }}</template> + <template #caption>{{ i18n.ts.fillAbuseReportDescription }}</template> + </MkTextarea> + </div> + <div class=""> + <MkButton primary full :disabled="comment.length === 0" @click="send">{{ i18n.ts.send }}</MkButton> + </div> </div> - <div class=""> - <MkButton primary full :disabled="comment.length === 0" @click="send">{{ i18n.ts.send }}</MkButton> - </div> - </div> -</XWindow> + </MkSpacer> +</MkWindow> </template> <script setup lang="ts"> import { ref, shallowRef } from 'vue'; import * as Misskey from 'misskey-js'; -import XWindow from '@/components/MkWindow.vue'; +import MkWindow from '@/components/MkWindow.vue'; import MkTextarea from '@/components/form/textarea.vue'; import MkButton from '@/components/MkButton.vue'; import * as os from '@/os'; @@ -40,7 +42,7 @@ const emit = defineEmits<{ (ev: 'closed'): void; }>(); -const uiWindow = shallowRef<InstanceType<typeof XWindow>>(); +const uiWindow = shallowRef<InstanceType<typeof MkWindow>>(); const comment = ref(props.initialComment || ''); function send() { diff --git a/packages/frontend/src/components/MkDriveWindow.vue b/packages/frontend/src/components/MkDriveWindow.vue index 617200321b..8b2abc15a3 100644 --- a/packages/frontend/src/components/MkDriveWindow.vue +++ b/packages/frontend/src/components/MkDriveWindow.vue @@ -1,5 +1,5 @@ <template> -<XWindow +<MkWindow ref="window" :initial-width="800" :initial-height="500" @@ -10,14 +10,14 @@ {{ i18n.ts.drive }} </template> <XDrive :initial-folder="initialFolder"/> -</XWindow> +</MkWindow> </template> <script lang="ts" setup> import { } from 'vue'; import * as Misskey from 'misskey-js'; import XDrive from '@/components/MkDrive.vue'; -import XWindow from '@/components/MkWindow.vue'; +import MkWindow from '@/components/MkWindow.vue'; import { i18n } from '@/i18n'; defineProps<{ diff --git a/packages/frontend/src/components/MkPageWindow.vue b/packages/frontend/src/components/MkPageWindow.vue index e25737d50c..f80974772b 100644 --- a/packages/frontend/src/components/MkPageWindow.vue +++ b/packages/frontend/src/components/MkPageWindow.vue @@ -1,5 +1,5 @@ <template> -<XWindow +<MkWindow ref="windowEl" :initial-width="500" :initial-height="500" @@ -20,13 +20,13 @@ <div class="yrolvcoq" :style="{ background: pageMetadata?.value?.bg }" style="container-type: inline-size;"> <RouterView :router="router"/> </div> -</XWindow> +</MkWindow> </template> <script lang="ts" setup> import { ComputedRef, inject, provide } from 'vue'; import RouterView from '@/components/global/RouterView.vue'; -import XWindow from '@/components/MkWindow.vue'; +import MkWindow from '@/components/MkWindow.vue'; import { popout as _popout } from '@/scripts/popout'; import copyToClipboard from '@/scripts/copy-to-clipboard'; import { url } from '@/config'; @@ -47,7 +47,7 @@ defineEmits<{ const router = new Router(routes, props.initialPath); let pageMetadata = $ref<null | ComputedRef<PageMetadata>>(); -let windowEl = $shallowRef<InstanceType<typeof XWindow>>(); +let windowEl = $shallowRef<InstanceType<typeof MkWindow>>(); const history = $ref<{ path: string; key: any; }[]>([{ path: router.getCurrentPath(), key: router.getCurrentKey(), diff --git a/packages/frontend/src/components/MkWindow.vue b/packages/frontend/src/components/MkWindow.vue index b28c5e4b85..401f0f4a2e 100644 --- a/packages/frontend/src/components/MkWindow.vue +++ b/packages/frontend/src/components/MkWindow.vue @@ -489,6 +489,7 @@ defineExpose({ flex: 1; overflow: auto; background: var(--panel); + container-type: inline-size; } } diff --git a/packages/frontend/src/components/MkYoutubePlayer.vue b/packages/frontend/src/components/MkYoutubePlayer.vue index c12b03572d..d1f1f9e9c5 100644 --- a/packages/frontend/src/components/MkYoutubePlayer.vue +++ b/packages/frontend/src/components/MkYoutubePlayer.vue @@ -1,5 +1,5 @@ <template> -<XWindow :initial-width="640" :initial-height="402" :can-resize="true" :close-button="true"> +<MkWindow :initial-width="640" :initial-height="402" :can-resize="true" :close-button="true"> <template #header> <i class="icon ti ti-brand-youtube" style="margin-right: 0.5em;"></i> <span>{{ title ?? 'YouTube' }}</span> @@ -14,11 +14,11 @@ <MkLoading v-if="fetching"/> <MkError v-else-if="!player.url" @retry="ytFetch()"/> </div> -</XWindow> +</MkWindow> </template> <script lang="ts" setup> -import XWindow from '@/components/MkWindow.vue'; +import MkWindow from '@/components/MkWindow.vue'; import { versatileLang } from '@/scripts/intl-const'; const props = defineProps<{ From 9aececc921584781247934101b83bfb0f792c889 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Fri, 6 Jan 2023 10:11:47 +0900 Subject: [PATCH 29/60] :art: --- .../frontend/src/components/MkPopupMenu.vue | 3 ++- .../frontend/src/components/form/select.vue | 17 ++++++++++++++++- packages/frontend/src/os.ts | 4 ++++ 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/packages/frontend/src/components/MkPopupMenu.vue b/packages/frontend/src/components/MkPopupMenu.vue index b5987715a9..34af209c06 100644 --- a/packages/frontend/src/components/MkPopupMenu.vue +++ b/packages/frontend/src/components/MkPopupMenu.vue @@ -1,5 +1,5 @@ <template> -<MkModal ref="modal" v-slot="{ type, maxHeight }" :z-priority="'high'" :src="src" :transparent-bg="true" @click="modal.close()" @closed="emit('closed')"> +<MkModal ref="modal" v-slot="{ type, maxHeight }" :z-priority="'high'" :src="src" :transparent-bg="true" @click="modal.close()" @close="emit('closing')" @closed="emit('closed')"> <MkMenu :items="items" :align="align" :width="width" :max-height="maxHeight" :as-drawer="type === 'drawer'" class="sfhdhdhq" :class="{ drawer: type === 'drawer' }" @close="modal.close()"/> </MkModal> </template> @@ -20,6 +20,7 @@ defineProps<{ const emit = defineEmits<{ (ev: 'closed'): void; + (ev: 'closing'): void; }>(); let modal = $shallowRef<InstanceType<typeof MkModal>>(); diff --git a/packages/frontend/src/components/form/select.vue b/packages/frontend/src/components/form/select.vue index 068ca2ebc6..4b5a14f5be 100644 --- a/packages/frontend/src/components/form/select.vue +++ b/packages/frontend/src/components/form/select.vue @@ -18,7 +18,7 @@ > <slot></slot> </select> - <div ref="suffixEl" class="suffix"><i class="ti ti-chevron-down"></i></div> + <div ref="suffixEl" class="suffix"><i class="ti ti-chevron-down" :class="[$style.chevron, { [$style.chevronOpening]: opening }]"></i></div> </div> <div class="caption"><slot name="caption"></slot></div> @@ -56,6 +56,7 @@ const slots = useSlots(); const { modelValue, autofocus } = toRefs(props); const v = ref(modelValue.value); const focused = ref(false); +const opening = ref(false); const changed = ref(false); const invalid = ref(false); const filled = computed(() => v.value !== '' && v.value != null); @@ -119,6 +120,7 @@ onMounted(() => { const onClick = (ev: MouseEvent) => { focused.value = true; + opening.value = true; const menu = []; let options = slots.default!(); @@ -158,6 +160,9 @@ const onClick = (ev: MouseEvent) => { os.popupMenu(menu, container.value, { width: container.value.offsetWidth, + onClosing: () => { + opening.value = false; + }, }).then(() => { focused.value = false; }); @@ -277,3 +282,13 @@ const onClick = (ev: MouseEvent) => { } } </style> + +<style lang="scss" module> +.chevron { + transition: transform 0.5s ease; +} + +.chevronOpening { + transform: rotateX(180deg); +} +</style> diff --git a/packages/frontend/src/os.ts b/packages/frontend/src/os.ts index a4c34104c6..6e36f18374 100644 --- a/packages/frontend/src/os.ts +++ b/packages/frontend/src/os.ts @@ -515,6 +515,7 @@ export function popupMenu(items: MenuItem[] | Ref<MenuItem[]>, src?: HTMLElement align?: string; width?: number; viaKeyboard?: boolean; + onClosing?: () => void; }) { return new Promise((resolve, reject) => { let dispose; @@ -529,6 +530,9 @@ export function popupMenu(items: MenuItem[] | Ref<MenuItem[]>, src?: HTMLElement resolve(); dispose(); }, + closing: () => { + if (options?.onClosing) options.onClosing(); + }, }).then(res => { dispose = res.dispose; }); From e2ca90b0a15e2bd1817b7a2fad3f125157f0e5a4 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Fri, 6 Jan 2023 10:20:42 +0900 Subject: [PATCH 30/60] enhance(server): SSR for flash --- .../src/server/web/ClientServerService.ts | 31 ++++++++++++++++++- .../backend/src/server/web/views/flash.pug | 31 +++++++++++++++++++ 2 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 packages/backend/src/server/web/views/flash.pug diff --git a/packages/backend/src/server/web/ClientServerService.ts b/packages/backend/src/server/web/ClientServerService.ts index 7ef178403b..263b15cc5e 100644 --- a/packages/backend/src/server/web/ClientServerService.ts +++ b/packages/backend/src/server/web/ClientServerService.ts @@ -26,9 +26,10 @@ import { PageEntityService } from '@/core/entities/PageEntityService.js'; import { GalleryPostEntityService } from '@/core/entities/GalleryPostEntityService.js'; import { ClipEntityService } from '@/core/entities/ClipEntityService.js'; import { ChannelEntityService } from '@/core/entities/ChannelEntityService.js'; -import type { ChannelsRepository, ClipsRepository, EmojisRepository, GalleryPostsRepository, NotesRepository, PagesRepository, UserProfilesRepository, UsersRepository } from '@/models/index.js'; +import type { ChannelsRepository, ClipsRepository, EmojisRepository, FlashsRepository, GalleryPostsRepository, NotesRepository, PagesRepository, UserProfilesRepository, UsersRepository } from '@/models/index.js'; import { deepClone } from '@/misc/clone.js'; import { bindThis } from '@/decorators.js'; +import { FlashEntityService } from '@/core/entities/FlashEntityService.js'; import manifest from './manifest.json' assert { type: 'json' }; import { FeedService } from './FeedService.js'; import { UrlPreviewService } from './UrlPreviewService.js'; @@ -73,6 +74,10 @@ export class ClientServerService { @Inject(DI.emojisRepository) private emojisRepository: EmojisRepository, + @Inject(DI.flashsRepository) + private flashsRepository: FlashsRepository, + + private flashEntityService: FlashEntityService, private userEntityService: UserEntityService, private noteEntityService: NoteEntityService, private pageEntityService: PageEntityService, @@ -545,6 +550,30 @@ export class ClientServerService { } }); + // Flash + fastify.get<{ Params: { id: string; } }>('/play/:id', async (request, reply) => { + const flash = await this.flashsRepository.findOneBy({ + id: request.params.id, + }); + + if (flash) { + const _flash = await this.flashEntityService.pack(flash); + const profile = await this.userProfilesRepository.findOneByOrFail({ userId: flash.userId }); + const meta = await this.metaService.fetch(); + reply.header('Cache-Control', 'public, max-age=15'); + return await reply.view('flash', { + flash: _flash, + profile, + avatarUrl: await this.userEntityService.getAvatarUrl(await this.usersRepository.findOneByOrFail({ id: flash.userId })), + instanceName: meta.name ?? 'Misskey', + icon: meta.iconUrl, + themeColor: meta.themeColor, + }); + } else { + return await renderBase(reply); + } + }); + // Clip // TODO: 非publicなclipのハンドリング fastify.get<{ Params: { clip: string; } }>('/clips/:clip', async (request, reply) => { diff --git a/packages/backend/src/server/web/views/flash.pug b/packages/backend/src/server/web/views/flash.pug new file mode 100644 index 0000000000..dc529b21df --- /dev/null +++ b/packages/backend/src/server/web/views/flash.pug @@ -0,0 +1,31 @@ +extends ./base + +block vars + - const user = flash.user; + - const title = flash.title; + - const url = `${config.url}/play/${flash.id}`; + +block title + = `${title} | ${instanceName}` + +block desc + meta(name='description' content= flash.summary) + +block og + meta(property='og:type' content='article') + meta(property='og:title' content= title) + meta(property='og:description' content= flash.summary) + meta(property='og:url' content= url) + meta(property='og:image' content= avatarUrl) + +block meta + if profile.noCrawle + meta(name='robots' content='noindex') + + meta(name='misskey:user-username' content=user.username) + meta(name='misskey:user-id' content=user.id) + meta(name='misskey:page-id' content=page.id) + + // todo + if user.twitter + meta(name='twitter:creator' content=`@${user.twitter.screenName}`) From 6b2b403d94546db244dd66d2960749be527cf791 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Fri, 6 Jan 2023 10:34:43 +0900 Subject: [PATCH 31/60] Update flash.pug --- packages/backend/src/server/web/views/flash.pug | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/src/server/web/views/flash.pug b/packages/backend/src/server/web/views/flash.pug index dc529b21df..5166855ea2 100644 --- a/packages/backend/src/server/web/views/flash.pug +++ b/packages/backend/src/server/web/views/flash.pug @@ -24,7 +24,7 @@ block meta meta(name='misskey:user-username' content=user.username) meta(name='misskey:user-id' content=user.id) - meta(name='misskey:page-id' content=page.id) + meta(name='misskey:flash-id' content=flash.id) // todo if user.twitter From f6b6f1bc8b30253ae23f539456f3551792b7538c Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Fri, 6 Jan 2023 10:34:49 +0900 Subject: [PATCH 32/60] :art: --- packages/frontend/src/components/MkFlashPreview.vue | 2 +- packages/frontend/src/pages/flash/flash-index.vue | 12 +++++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/packages/frontend/src/components/MkFlashPreview.vue b/packages/frontend/src/components/MkFlashPreview.vue index a1ce80c98c..a96934ddb1 100644 --- a/packages/frontend/src/components/MkFlashPreview.vue +++ b/packages/frontend/src/components/MkFlashPreview.vue @@ -1,5 +1,5 @@ <template> -<MkA :to="`/play/${flash.id}`" class="vhpxefrk" tabindex="-1"> +<MkA :to="`/play/${flash.id}`" class="vhpxefrk _panel" tabindex="-1"> <article> <header> <h1 :title="flash.title">{{ flash.title }}</h1> diff --git a/packages/frontend/src/pages/flash/flash-index.vue b/packages/frontend/src/pages/flash/flash-index.vue index bc4828f416..9964b34150 100644 --- a/packages/frontend/src/pages/flash/flash-index.vue +++ b/packages/frontend/src/pages/flash/flash-index.vue @@ -4,20 +4,26 @@ <MkSpacer :content-max="700"> <div v-if="tab === 'featured'" class=""> <MkPagination v-slot="{items}" :pagination="featuredFlashsPagination"> - <MkFlashPreview v-for="flash in items" :key="flash.id" class="" :flash="flash"/> + <div class="_autoGap_half"> + <MkFlashPreview v-for="flash in items" :key="flash.id" class="" :flash="flash"/> + </div> </MkPagination> </div> <div v-else-if="tab === 'my'" class="my"> <MkButton class="new" @click="create()"><i class="ti ti-plus"></i></MkButton> <MkPagination v-slot="{items}" :pagination="myFlashsPagination"> - <MkFlashPreview v-for="flash in items" :key="flash.id" class="" :flash="flash"/> + <div class="_autoGap_half"> + <MkFlashPreview v-for="flash in items" :key="flash.id" class="" :flash="flash"/> + </div> </MkPagination> </div> <div v-else-if="tab === 'liked'" class=""> <MkPagination v-slot="{items}" :pagination="likedFlashsPagination"> - <MkFlashPreview v-for="like in items" :key="like.flash.id" class="" :flash="like.flash"/> + <div class="_autoGap_half"> + <MkFlashPreview v-for="like in items" :key="like.flash.id" class="" :flash="like.flash"/> + </div> </MkPagination> </div> </MkSpacer> From 4abef6161ef144540bc1ff2fcd9e9dad43a5728a Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Fri, 6 Jan 2023 10:47:58 +0900 Subject: [PATCH 33/60] 13.0.0-beta.24 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9036172f1c..098d21e34b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "13.0.0-beta.23", + "version": "13.0.0-beta.24", "codename": "indigo", "repository": { "type": "git", From 88f0c10d09735b0cdfe94160035c6f51ce9bdced Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Fri, 6 Jan 2023 11:20:09 +0900 Subject: [PATCH 34/60] Update CHANGELOG.md --- CHANGELOG.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8adf88bae3..5be28149d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,7 @@ You should also include the user name that made the change. ## 13.0.0 (unreleased) ### Changes -#### For sabakans +#### For server admins - Node.js 18.x or later is required - Elasticsearchのサポートが削除されました - 代わりに今後任意の検索プロバイダを設定できる仕組みを構想しています。その仕組みを使えば今まで通りElasticsearchも利用できます @@ -27,8 +27,9 @@ You should also include the user name that made the change. - 0.12.xの変更点についてはこちら https://github.com/syuilo/aiscript/blob/master/CHANGELOG.md#0120 - 0.12.1未満のプラグインは読み込むことはできません - iOS15以下のデバイスはサポートされなくなりました +- Firefox109以下はサポートされなくなりました -#### For developers +#### For app developers - API: カスタム絵文字エンティティに`url`プロパティが含まれなくなりました - 絵文字画像を表示するには、`<instance host>/emoji/<emoji name>.webp`にリクエストすると画像が返ります。 - e.g. `https://p1.a9z.dev/emoji/misskey.webp` From 9ac86dacbbd52f50eb633594fee987ffae030e61 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Fri, 6 Jan 2023 12:13:34 +0900 Subject: [PATCH 35/60] Update CHANGELOG.md --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5be28149d1..d585a1f86d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,13 @@ You should also include the user name that made the change. ## 13.0.0 (unreleased) +### TL;DR +- New features (Play, new widgets, new charts, etc) +- Rewriten backend +- Better performance (backend and frontend) +- Various usability improvements +- Various UI tweaks + ### Changes #### For server admins - Node.js 18.x or later is required From 6adc0521d8e5bdd2fb34e9f657fbde1cd68beebb Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Fri, 6 Jan 2023 12:26:18 +0900 Subject: [PATCH 36/60] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d585a1f86d..556fe12868 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,8 @@ You should also include the user name that made the change. ### Changes #### For server admins - Node.js 18.x or later is required +- PostgreSQL 15.x is required + - Misskey not using 15 specific features at 13.0.0, but may do so in the future. - Elasticsearchのサポートが削除されました - 代わりに今後任意の検索プロバイダを設定できる仕組みを構想しています。その仕組みを使えば今まで通りElasticsearchも利用できます - Migrate to Yarn Berry (v3.2.1) @ThatOneCalculator From dc5b4a04026f88367e484933334028ac417c9d67 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Fri, 6 Jan 2023 13:25:49 +0900 Subject: [PATCH 37/60] enhance(client): show fireworks when visit user who today is birthday Resolve #9476 --- CHANGELOG.md | 1 + packages/frontend/package.json | 1 + packages/frontend/src/pages/user/home.vue | 34 +++++++++++++++++++++++ yarn.lock | 8 ++++++ 4 files changed, 44 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 556fe12868..6cda5537dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -80,6 +80,7 @@ You should also include the user name that made the change. - Client: add user list widget @syuilo - Client: add heatmap of daily active users to about page @syuilo - Client: introduce fluent emoji @syuilo +- Client: show fireworks when visit user who today is birthday @syuilo - Client: show bot warning on screen when logged in as bot account @syuilo - Client: improve overall performance of client @syuilo - Client: ui tweaks @syuilo diff --git a/packages/frontend/package.json b/packages/frontend/package.json index d151a7b3af..98230d6ceb 100644 --- a/packages/frontend/package.json +++ b/packages/frontend/package.json @@ -20,6 +20,7 @@ "blurhash": "2.0.4", "broadcast-channel": "4.19.1", "browser-image-resizer": "git+https://github.com/misskey-dev/browser-image-resizer#v2.2.1-misskey.3", + "canvas-confetti": "^1.6.0", "chart.js": "4.1.1", "chartjs-adapter-date-fns": "3.0.0", "chartjs-chart-matrix": "^1.3.0", diff --git a/packages/frontend/src/pages/user/home.vue b/packages/frontend/src/pages/user/home.vue index 11a7ee3b8a..d9d7fc826c 100644 --- a/packages/frontend/src/pages/user/home.vue +++ b/packages/frontend/src/pages/user/home.vue @@ -110,6 +110,7 @@ <script lang="ts" setup> import { defineAsyncComponent, computed, inject, onMounted, onUnmounted, watch } from 'vue'; import calcAge from 's-age'; +import confetti from 'canvas-confetti'; import * as misskey from 'misskey-js'; import XUserTimeline from './index.timeline.vue'; import XNote from '@/components/MkNote.vue'; @@ -155,6 +156,29 @@ const age = $computed(() => { return calcAge(props.user.birthday); }); +function birthdayEffect() { + const duration = 1000 * 5; + const animationEnd = Date.now() + duration; + const defaults = { startVelocity: 30, spread: 360, ticks: 60, zIndex: os.claimZIndex('high') }; + + function randomInRange(min, max) { + return Math.random() * (max - min) + min; + } + + const interval = setInterval(() => { + const timeLeft = animationEnd - Date.now(); + + if (timeLeft <= 0) { + return clearInterval(interval); + } + + const particleCount = 50 * (timeLeft / duration); + // since particles fall down, start a bit higher than random + confetti(Object.assign({}, defaults, { particleCount, origin: { x: randomInRange(0.1, 0.3), y: Math.random() - 0.2 } })); + confetti(Object.assign({}, defaults, { particleCount, origin: { x: randomInRange(0.7, 0.9), y: Math.random() - 0.2 } })); + }, 250); +} + function menu(ev) { os.popupMenu(getUserMenu(props.user, router), ev.currentTarget ?? ev.target); } @@ -180,6 +204,16 @@ function parallax() { onMounted(() => { window.requestAnimationFrame(parallaxLoop); narrow = rootEl!.clientWidth < 1000; + + if (props.user.birthday) { + const m = new Date().getMonth() + 1; + const d = new Date().getDate(); + const bm = parseInt(props.user.birthday.split('-')[1]); + const bd = parseInt(props.user.birthday.split('-')[2]); + if (m === bm && d === bd) { + birthdayEffect(); + } + } }); onUnmounted(() => { diff --git a/yarn.lock b/yarn.lock index 208f0724d5..49ae07dd00 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4833,6 +4833,13 @@ __metadata: languageName: node linkType: hard +"canvas-confetti@npm:^1.6.0": + version: 1.6.0 + resolution: "canvas-confetti@npm:1.6.0" + checksum: be19e3be736ab28aa8af7b53cba9f4146f5714edadbf4d5a7d7b1899914dc59a8ac5574260fe6b7d9995c51df5787b0b707adfbb72dbacbc61fc03f9f2b25291 + languageName: node + linkType: hard + "caseless@npm:~0.12.0": version: 0.12.0 resolution: "caseless@npm:0.12.0" @@ -8052,6 +8059,7 @@ __metadata: blurhash: 2.0.4 broadcast-channel: 4.19.1 browser-image-resizer: "git+https://github.com/misskey-dev/browser-image-resizer#v2.2.1-misskey.3" + canvas-confetti: ^1.6.0 chart.js: 4.1.1 chartjs-adapter-date-fns: 3.0.0 chartjs-chart-matrix: ^1.3.0 From 82c4f694a08507d2ed3a8ba4450e5ccbb95fad3a Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Fri, 6 Jan 2023 13:40:17 +0900 Subject: [PATCH 38/60] :art: --- .../src/components/MkAbuseReportWindow.vue | 2 +- .../frontend/src/components/MkForgotPassword.vue | 2 +- packages/frontend/src/components/MkFormDialog.vue | 2 +- .../src/components/MkNotificationSettingWindow.vue | 2 +- packages/frontend/src/components/MkSignin.vue | 4 ++-- packages/frontend/src/components/MkSignup.vue | 2 +- .../src/components/MkTokenGenerateWindow.vue | 2 +- packages/frontend/src/pages/about-misskey.vue | 2 +- packages/frontend/src/pages/about.vue | 6 +++--- packages/frontend/src/pages/admin-file.vue | 6 +++--- packages/frontend/src/pages/admin/ads.vue | 2 +- .../frontend/src/pages/admin/announcements.vue | 4 ++-- .../frontend/src/pages/admin/bot-protection.vue | 2 +- .../frontend/src/pages/admin/email-settings.vue | 4 ++-- .../frontend/src/pages/admin/emoji-edit-dialog.vue | 2 +- .../src/pages/admin/integrations.discord.vue | 2 +- .../src/pages/admin/integrations.github.vue | 2 +- .../src/pages/admin/integrations.twitter.vue | 2 +- packages/frontend/src/pages/admin/integrations.vue | 2 +- .../frontend/src/pages/admin/object-storage.vue | 2 +- packages/frontend/src/pages/admin/security.vue | 10 +++++----- packages/frontend/src/pages/admin/settings.vue | 14 +++++++------- packages/frontend/src/pages/announcements.vue | 2 +- packages/frontend/src/pages/api-console.vue | 4 ++-- packages/frontend/src/pages/channel-editor.vue | 2 +- packages/frontend/src/pages/flash/flash-edit.vue | 2 +- packages/frontend/src/pages/flash/flash-index.vue | 6 +++--- packages/frontend/src/pages/instance-info.vue | 10 +++++----- packages/frontend/src/pages/my-antennas/editor.vue | 2 +- .../frontend/src/pages/page-editor/page-editor.vue | 2 +- packages/frontend/src/pages/registry.keys.vue | 2 +- packages/frontend/src/pages/registry.value.vue | 2 +- packages/frontend/src/pages/reset-password.vue | 2 +- .../frontend/src/pages/settings/account-info.vue | 2 +- packages/frontend/src/pages/settings/accounts.vue | 2 +- packages/frontend/src/pages/settings/api.vue | 2 +- packages/frontend/src/pages/settings/apps.vue | 2 +- .../frontend/src/pages/settings/custom-css.vue | 2 +- packages/frontend/src/pages/settings/deck.vue | 2 +- .../frontend/src/pages/settings/delete-account.vue | 2 +- packages/frontend/src/pages/settings/drive.vue | 6 +++--- packages/frontend/src/pages/settings/email.vue | 4 ++-- packages/frontend/src/pages/settings/general.vue | 10 +++++----- .../frontend/src/pages/settings/import-export.vue | 12 ++++++------ .../frontend/src/pages/settings/instance-mute.vue | 2 +- .../frontend/src/pages/settings/integration.vue | 2 +- .../frontend/src/pages/settings/mute-block.vue | 2 +- packages/frontend/src/pages/settings/navbar.vue | 2 +- .../frontend/src/pages/settings/notifications.vue | 6 +++--- packages/frontend/src/pages/settings/other.vue | 2 +- .../frontend/src/pages/settings/plugin.install.vue | 2 +- packages/frontend/src/pages/settings/plugin.vue | 4 ++-- .../src/pages/settings/preferences-backups.vue | 4 ++-- packages/frontend/src/pages/settings/privacy.vue | 6 +++--- packages/frontend/src/pages/settings/profile.vue | 6 +++--- packages/frontend/src/pages/settings/reaction.vue | 2 +- packages/frontend/src/pages/settings/security.vue | 2 +- .../frontend/src/pages/settings/sounds.sound.vue | 2 +- packages/frontend/src/pages/settings/sounds.vue | 4 ++-- .../src/pages/settings/statusbar.statusbar.vue | 2 +- packages/frontend/src/pages/settings/statusbar.vue | 2 +- .../frontend/src/pages/settings/theme.install.vue | 2 +- .../frontend/src/pages/settings/theme.manage.vue | 2 +- packages/frontend/src/pages/settings/theme.vue | 2 +- .../frontend/src/pages/settings/webhook.edit.vue | 4 ++-- .../frontend/src/pages/settings/webhook.new.vue | 4 ++-- packages/frontend/src/pages/settings/webhook.vue | 2 +- packages/frontend/src/pages/settings/word-mute.vue | 6 +++--- packages/frontend/src/pages/theme-editor.vue | 6 +++--- packages/frontend/src/pages/user-info.vue | 10 +++++----- packages/frontend/src/pages/user/home.vue | 14 +++++++------- packages/frontend/src/pages/welcome.setup.vue | 2 +- packages/frontend/src/style.scss | 10 ++++++++-- 73 files changed, 141 insertions(+), 135 deletions(-) diff --git a/packages/frontend/src/components/MkAbuseReportWindow.vue b/packages/frontend/src/components/MkAbuseReportWindow.vue index 6481c0d876..d4f9622fbf 100644 --- a/packages/frontend/src/components/MkAbuseReportWindow.vue +++ b/packages/frontend/src/components/MkAbuseReportWindow.vue @@ -9,7 +9,7 @@ </I18n> </template> <MkSpacer :margin-min="20" :margin-max="28"> - <div class="dpvffvvy _autoGap"> + <div class="dpvffvvy _gaps_m"> <div class=""> <MkTextarea v-model="comment"> <template #label>{{ i18n.ts.details }}</template> diff --git a/packages/frontend/src/components/MkForgotPassword.vue b/packages/frontend/src/components/MkForgotPassword.vue index f74bb24b8d..dc38e42779 100644 --- a/packages/frontend/src/components/MkForgotPassword.vue +++ b/packages/frontend/src/components/MkForgotPassword.vue @@ -9,7 +9,7 @@ <template #header>{{ i18n.ts.forgotPassword }}</template> <form v-if="instance.enableEmail" class="bafeceda" @submit.prevent="onSubmit"> - <div class="main _autoGap"> + <div class="main _gaps_m"> <MkInput v-model="username" type="text" pattern="^[a-zA-Z0-9_]+$" :spellcheck="false" autofocus required> <template #label>{{ i18n.ts.username }}</template> <template #prefix>@</template> diff --git a/packages/frontend/src/components/MkFormDialog.vue b/packages/frontend/src/components/MkFormDialog.vue index 2f440e4b9f..76c6c164d4 100644 --- a/packages/frontend/src/components/MkFormDialog.vue +++ b/packages/frontend/src/components/MkFormDialog.vue @@ -15,7 +15,7 @@ </template> <MkSpacer :margin-min="20" :margin-max="32"> - <div class="xkpnjxcv _autoGap"> + <div class="xkpnjxcv _gaps_m"> <template v-for="item in Object.keys(form).filter(item => !form[item].hidden)"> <FormInput v-if="form[item].type === 'number'" v-model="values[item]" type="number" :step="form[item].step || 1"> <template #label><span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ $ts.optional }})</span></template> diff --git a/packages/frontend/src/components/MkNotificationSettingWindow.vue b/packages/frontend/src/components/MkNotificationSettingWindow.vue index 671865f629..cb054b1a29 100644 --- a/packages/frontend/src/components/MkNotificationSettingWindow.vue +++ b/packages/frontend/src/components/MkNotificationSettingWindow.vue @@ -12,7 +12,7 @@ <template #header>{{ i18n.ts.notificationSetting }}</template> <MkSpacer :margin-min="20" :margin-max="28"> - <div class="_autoGap"> + <div class="_gaps_m"> <template v-if="showGlobalToggle"> <MkSwitch v-model="useGlobalSetting"> {{ i18n.ts.useGlobalSetting }} diff --git a/packages/frontend/src/components/MkSignin.vue b/packages/frontend/src/components/MkSignin.vue index eb015b08f2..b87f0643d7 100644 --- a/packages/frontend/src/components/MkSignin.vue +++ b/packages/frontend/src/components/MkSignin.vue @@ -1,11 +1,11 @@ <template> <form class="eppvobhk" :class="{ signing, totpLogin }" @submit.prevent="onSubmit"> - <div class="auth _autoGap"> + <div class="auth _gaps_m"> <div v-show="withAvatar" class="avatar" :style="{ backgroundImage: user ? `url('${ user.avatarUrl }')` : null, marginBottom: message ? '1.5em' : null }"></div> <MkInfo v-if="message"> {{ message }} </MkInfo> - <div v-if="!totpLogin" class="normal-signin _autoGap"> + <div v-if="!totpLogin" class="normal-signin _gaps_m"> <MkInput v-model="username" :placeholder="i18n.ts.username" type="text" pattern="^[a-zA-Z0-9_]+$" :spellcheck="false" autofocus required data-cy-signin-username @update:model-value="onUsernameChange"> <template #prefix>@</template> <template #suffix>@{{ host }}</template> diff --git a/packages/frontend/src/components/MkSignup.vue b/packages/frontend/src/components/MkSignup.vue index 66a14e5608..dcffee47bd 100644 --- a/packages/frontend/src/components/MkSignup.vue +++ b/packages/frontend/src/components/MkSignup.vue @@ -1,5 +1,5 @@ <template> -<form class="qlvuhzng _autoGap" autocomplete="new-password" @submit.prevent="onSubmit"> +<form class="qlvuhzng _gaps_m" autocomplete="new-password" @submit.prevent="onSubmit"> <MkInput v-if="instance.disableRegistration" v-model="invitationCode" type="text" :spellcheck="false" required> <template #label>{{ i18n.ts.invitationCode }}</template> <template #prefix><i class="ti ti-key"></i></template> diff --git a/packages/frontend/src/components/MkTokenGenerateWindow.vue b/packages/frontend/src/components/MkTokenGenerateWindow.vue index 1a5a92caff..5e12aa8319 100644 --- a/packages/frontend/src/components/MkTokenGenerateWindow.vue +++ b/packages/frontend/src/components/MkTokenGenerateWindow.vue @@ -13,7 +13,7 @@ <template #header>{{ title || $ts.generateAccessToken }}</template> <MkSpacer :margin-min="20" :margin-max="28"> - <div class="_autoGap"> + <div class="_gaps_m"> <div v-if="information"> <MkInfo warn>{{ information }}</MkInfo> </div> diff --git a/packages/frontend/src/pages/about-misskey.vue b/packages/frontend/src/pages/about-misskey.vue index 2f88dae738..1c3535a833 100644 --- a/packages/frontend/src/pages/about-misskey.vue +++ b/packages/frontend/src/pages/about-misskey.vue @@ -3,7 +3,7 @@ <template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template> <div style="overflow: clip;"> <MkSpacer :content-max="600" :margin-min="20"> - <div class="_autoGap znqjceqz"> + <div class="_gaps_m znqjceqz"> <div ref="containerEl" v-panel class="about" :class="{ playing: easterEggEngine != null }"> <img src="/client-assets/about-icon.png" alt="" class="icon" draggable="false" @load="iconLoaded" @click="gravity"/> <div class="misskey">Misskey</div> diff --git a/packages/frontend/src/pages/about.vue b/packages/frontend/src/pages/about.vue index 27470d1a8e..4d971c5a9f 100644 --- a/packages/frontend/src/pages/about.vue +++ b/packages/frontend/src/pages/about.vue @@ -2,7 +2,7 @@ <MkStickyContainer> <template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template> <MkSpacer v-if="tab === 'overview'" :content-max="600" :margin-min="20"> - <div class="_autoGap"> + <div class="_gaps_m"> <div class="fwhjspax" :style="{ backgroundImage: `url(${ $instance.bannerUrl })` }"> <div class="content"> <img :src="$instance.iconUrl ?? $instance.faviconUrl ?? '/favicon.ico'" alt="" class="icon"/> @@ -18,7 +18,7 @@ </MkKeyValue> <FormSection> - <div class="_autoGap"> + <div class="_gaps_m"> <MkKeyValue :copy="version"> <template #key>Misskey</template> <template #value>{{ version }}</template> @@ -30,7 +30,7 @@ </FormSection> <FormSection> - <div class="_autoGap"> + <div class="_gaps_m"> <FormSplit> <MkKeyValue> <template #key>{{ i18n.ts.administrator }}</template> diff --git a/packages/frontend/src/pages/admin-file.vue b/packages/frontend/src/pages/admin-file.vue index 92972c1168..f8a860e8b6 100644 --- a/packages/frontend/src/pages/admin-file.vue +++ b/packages/frontend/src/pages/admin-file.vue @@ -2,7 +2,7 @@ <MkStickyContainer> <template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template> <MkSpacer v-if="file" :content-max="600" :margin-min="16" :margin-max="32"> - <div v-if="tab === 'overview'" class="cxqhhsmd _autoGap"> + <div v-if="tab === 'overview'" class="cxqhhsmd _gaps_m"> <a class="thumbnail" :href="file.url" target="_blank"> <MkDriveFileThumbnail class="thumbnail" :file="file" fit="contain"/> </a> @@ -39,7 +39,7 @@ <MkButton danger @click="del"><i class="ti ti-trash"></i> {{ i18n.ts.delete }}</MkButton> </div> </div> - <div v-else-if="tab === 'ip' && info" class="_autoGap"> + <div v-else-if="tab === 'ip' && info" class="_gaps_m"> <MkInfo v-if="!iAmAdmin" warn>{{ i18n.ts.requireAdminForView }}</MkInfo> <MkKeyValue v-if="info.requestIp" class="_monospace" :copy="info.requestIp" oneline> <template #key>IP</template> @@ -53,7 +53,7 @@ </MkKeyValue> </FormSection> </div> - <div v-else-if="tab === 'raw'" class="_autoGap"> + <div v-else-if="tab === 'raw'" class="_gaps_m"> <MkObjectView v-if="info" tall :value="info"> </MkObjectView> </div> diff --git a/packages/frontend/src/pages/admin/ads.vue b/packages/frontend/src/pages/admin/ads.vue index e7ab1c8556..d8776c9175 100644 --- a/packages/frontend/src/pages/admin/ads.vue +++ b/packages/frontend/src/pages/admin/ads.vue @@ -3,7 +3,7 @@ <template #header><XHeader :actions="headerActions" :tabs="headerTabs"/></template> <MkSpacer :content-max="900"> <div class="uqshojas"> - <div v-for="ad in ads" class="_panel _autoGap ad"> + <div v-for="ad in ads" class="_panel _gaps_m ad"> <MkAd v-if="ad.url" :specify="ad"/> <MkInput v-model="ad.url" type="url"> <template #label>URL</template> diff --git a/packages/frontend/src/pages/admin/announcements.vue b/packages/frontend/src/pages/admin/announcements.vue index d9300da2e5..71af03484b 100644 --- a/packages/frontend/src/pages/admin/announcements.vue +++ b/packages/frontend/src/pages/admin/announcements.vue @@ -2,9 +2,9 @@ <MkStickyContainer> <template #header><XHeader :actions="headerActions" :tabs="headerTabs"/></template> <MkSpacer :content-max="900"> - <div class="ztgjmzrw _autoGap"> + <div class="ztgjmzrw _gaps_m"> <section v-for="announcement in announcements" class=""> - <div class="_panel _autoGap" style="padding: 24px;"> + <div class="_panel _gaps_m" style="padding: 24px;"> <MkInput v-model="announcement.title"> <template #label>{{ i18n.ts.title }}</template> </MkInput> diff --git a/packages/frontend/src/pages/admin/bot-protection.vue b/packages/frontend/src/pages/admin/bot-protection.vue index 22fac54913..995ea483d7 100644 --- a/packages/frontend/src/pages/admin/bot-protection.vue +++ b/packages/frontend/src/pages/admin/bot-protection.vue @@ -1,7 +1,7 @@ <template> <div> <FormSuspense :p="init"> - <div class="_autoGap"> + <div class="_gaps_m"> <FormRadios v-model="provider"> <option :value="null">{{ i18n.ts.none }} ({{ i18n.ts.notRecommended }})</option> <option value="hcaptcha">hCaptcha</option> diff --git a/packages/frontend/src/pages/admin/email-settings.vue b/packages/frontend/src/pages/admin/email-settings.vue index 0bf6c79192..ab19366ea6 100644 --- a/packages/frontend/src/pages/admin/email-settings.vue +++ b/packages/frontend/src/pages/admin/email-settings.vue @@ -3,7 +3,7 @@ <template #header><XHeader :actions="headerActions" :tabs="headerTabs"/></template> <MkSpacer :content-max="700" :margin-min="16" :margin-max="32"> <FormSuspense :p="init"> - <div class="_autoGap"> + <div class="_gaps_m"> <FormSwitch v-model="enableEmail"> <template #label>{{ i18n.ts.enableEmail }} ({{ i18n.ts.recommended }})</template> <template #caption>{{ i18n.ts.emailConfigInfo }}</template> @@ -17,7 +17,7 @@ <FormSection> <template #label>{{ i18n.ts.smtpConfig }}</template> - <div class="_autoGap"> + <div class="_gaps_m"> <FormSplit :min-width="280"> <FormInput v-model="smtpHost"> <template #label>{{ i18n.ts.smtpHost }}</template> diff --git a/packages/frontend/src/pages/admin/emoji-edit-dialog.vue b/packages/frontend/src/pages/admin/emoji-edit-dialog.vue index 08b73d2ac2..46011876ee 100644 --- a/packages/frontend/src/pages/admin/emoji-edit-dialog.vue +++ b/packages/frontend/src/pages/admin/emoji-edit-dialog.vue @@ -10,7 +10,7 @@ <template #header>:{{ emoji.name }}:</template> <MkSpacer :margin-min="20" :margin-max="28"> - <div class="yigymqpb _autoGap"> + <div class="yigymqpb _gaps_m"> <img :src="`/emoji/${emoji.name}.webp`" class="img"/> <MkInput v-model="name"> <template #label>{{ i18n.ts.name }}</template> diff --git a/packages/frontend/src/pages/admin/integrations.discord.vue b/packages/frontend/src/pages/admin/integrations.discord.vue index 09270896e7..6e2156160c 100644 --- a/packages/frontend/src/pages/admin/integrations.discord.vue +++ b/packages/frontend/src/pages/admin/integrations.discord.vue @@ -1,6 +1,6 @@ <template> <FormSuspense :p="init"> - <div class="_autoGap"> + <div class="_gaps_m"> <FormSwitch v-model="enableDiscordIntegration"> <template #label>{{ i18n.ts.enable }}</template> </FormSwitch> diff --git a/packages/frontend/src/pages/admin/integrations.github.vue b/packages/frontend/src/pages/admin/integrations.github.vue index 530fa6e037..352840adcf 100644 --- a/packages/frontend/src/pages/admin/integrations.github.vue +++ b/packages/frontend/src/pages/admin/integrations.github.vue @@ -1,6 +1,6 @@ <template> <FormSuspense :p="init"> - <div class="_autoGap"> + <div class="_gaps_m"> <FormSwitch v-model="enableGithubIntegration"> <template #label>{{ i18n.ts.enable }}</template> </FormSwitch> diff --git a/packages/frontend/src/pages/admin/integrations.twitter.vue b/packages/frontend/src/pages/admin/integrations.twitter.vue index b807c12074..a7b56fbad0 100644 --- a/packages/frontend/src/pages/admin/integrations.twitter.vue +++ b/packages/frontend/src/pages/admin/integrations.twitter.vue @@ -1,6 +1,6 @@ <template> <FormSuspense :p="init"> - <div class="_autoGap"> + <div class="_gaps_m"> <FormSwitch v-model="enableTwitterIntegration"> <template #label>{{ i18n.ts.enable }}</template> </FormSwitch> diff --git a/packages/frontend/src/pages/admin/integrations.vue b/packages/frontend/src/pages/admin/integrations.vue index 4ce0a29ae7..e319d48617 100644 --- a/packages/frontend/src/pages/admin/integrations.vue +++ b/packages/frontend/src/pages/admin/integrations.vue @@ -3,7 +3,7 @@ <template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template> <MkSpacer :content-max="700" :margin-min="16" :margin-max="32"> <FormSuspense :p="init"> - <div class="_autoGap"> + <div class="_gaps_m"> <FormFolder> <template #icon><i class="ti ti-brand-twitter"></i></template> <template #label>Twitter</template> diff --git a/packages/frontend/src/pages/admin/object-storage.vue b/packages/frontend/src/pages/admin/object-storage.vue index 7effa16542..f56e8bab93 100644 --- a/packages/frontend/src/pages/admin/object-storage.vue +++ b/packages/frontend/src/pages/admin/object-storage.vue @@ -3,7 +3,7 @@ <template #header><XHeader :actions="headerActions" :tabs="headerTabs"/></template> <MkSpacer :content-max="700" :margin-min="16" :margin-max="32"> <FormSuspense :p="init"> - <div class="_autoGap"> + <div class="_gaps_m"> <FormSwitch v-model="useObjectStorage">{{ i18n.ts.useObjectStorage }}</FormSwitch> <template v-if="useObjectStorage"> diff --git a/packages/frontend/src/pages/admin/security.vue b/packages/frontend/src/pages/admin/security.vue index 92f3c259b9..997ee31fe3 100644 --- a/packages/frontend/src/pages/admin/security.vue +++ b/packages/frontend/src/pages/admin/security.vue @@ -3,7 +3,7 @@ <template #header><XHeader :actions="headerActions" :tabs="headerTabs"/></template> <MkSpacer :content-max="700" :margin-min="16" :margin-max="32"> <FormSuspense :p="init"> - <div class="_autoGap"> + <div class="_gaps_m"> <FormFolder> <template #icon><i class="ti ti-shield"></i></template> <template #label>{{ i18n.ts.botProtection }}</template> @@ -23,7 +23,7 @@ <template v-else-if="sensitiveMediaDetection === 'remote'" #suffix>{{ i18n.ts.remoteOnly }}</template> <template v-else #suffix>{{ i18n.ts.none }}</template> - <div class="_autoGap"> + <div class="_gaps_m"> <span>{{ i18n.ts._sensitiveMediaDetection.description }}</span> <FormRadios v-model="sensitiveMediaDetection"> @@ -63,7 +63,7 @@ <template v-if="enableActiveEmailValidation" #suffix>Enabled</template> <template v-else #suffix>Disabled</template> - <div class="_autoGap"> + <div class="_gaps_m"> <span>{{ i18n.ts.activeEmailValidationDescription }}</span> <FormSwitch v-model="enableActiveEmailValidation" @update:model-value="save"> <template #label>Enable</template> @@ -76,7 +76,7 @@ <template v-if="enableIpLogging" #suffix>Enabled</template> <template v-else #suffix>Disabled</template> - <div class="_autoGap"> + <div class="_gaps_m"> <FormSwitch v-model="enableIpLogging" @update:model-value="save"> <template #label>Enable</template> </FormSwitch> @@ -86,7 +86,7 @@ <FormFolder> <template #label>Summaly Proxy</template> - <div class="_autoGap"> + <div class="_gaps_m"> <FormInput v-model="summalyProxy"> <template #prefix><i class="ti ti-link"></i></template> <template #label>Summaly Proxy URL</template> diff --git a/packages/frontend/src/pages/admin/settings.vue b/packages/frontend/src/pages/admin/settings.vue index 01c7b51e39..2d7822efbe 100644 --- a/packages/frontend/src/pages/admin/settings.vue +++ b/packages/frontend/src/pages/admin/settings.vue @@ -4,7 +4,7 @@ <template #header><XHeader :actions="headerActions" :tabs="headerTabs"/></template> <MkSpacer :content-max="700" :margin-min="16" :margin-max="32"> <FormSuspense :p="init"> - <div class="_autoGap"> + <div class="_gaps_m"> <FormInput v-model="name"> <template #label>{{ i18n.ts.instanceName }}</template> </FormInput> @@ -35,7 +35,7 @@ </FormTextarea> <FormSection> - <div class="_autoGap_half"> + <div class="_gaps_s"> <FormSwitch v-model="enableRegistration"> <template #label>{{ i18n.ts.enableRegistration }}</template> </FormSwitch> @@ -47,7 +47,7 @@ </FormSection> <FormSection> - <div class="_autoGap_half"> + <div class="_gaps_s"> <FormSwitch v-model="enableLocalTimeline">{{ i18n.ts.enableLocalTimeline }}</FormSwitch> <FormSwitch v-model="enableGlobalTimeline">{{ i18n.ts.enableGlobalTimeline }}</FormSwitch> <FormInfo>{{ i18n.ts.disablingTimelinesInfo }}</FormInfo> @@ -57,7 +57,7 @@ <FormSection> <template #label>{{ i18n.ts.theme }}</template> - <div class="_autoGap"> + <div class="_gaps_m"> <FormInput v-model="iconUrl"> <template #prefix><i class="ti ti-link"></i></template> <template #label>{{ i18n.ts.iconUrl }}</template> @@ -94,7 +94,7 @@ <FormSection> <template #label>{{ i18n.ts.files }}</template> - <div class="_autoGap"> + <div class="_gaps_m"> <FormSwitch v-model="cacheRemoteFiles"> <template #label>{{ i18n.ts.cacheRemoteFiles }}</template> <template #caption>{{ i18n.ts.cacheRemoteFilesDescription }}</template> @@ -119,7 +119,7 @@ <FormSection> <template #label>ServiceWorker</template> - <div class="_autoGap"> + <div class="_gaps_m"> <FormSwitch v-model="enableServiceWorker"> <template #label>{{ i18n.ts.enableServiceworker }}</template> <template #caption>{{ i18n.ts.serviceworkerInfo }}</template> @@ -142,7 +142,7 @@ <FormSection> <template #label>DeepL Translation</template> - <div class="_autoGap"> + <div class="_gaps_m"> <FormInput v-model="deeplAuthKey"> <template #prefix><i class="ti ti-key"></i></template> <template #label>DeepL Auth Key</template> diff --git a/packages/frontend/src/pages/announcements.vue b/packages/frontend/src/pages/announcements.vue index 702cf05e68..b06bd30245 100644 --- a/packages/frontend/src/pages/announcements.vue +++ b/packages/frontend/src/pages/announcements.vue @@ -2,7 +2,7 @@ <MkStickyContainer> <template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template> <MkSpacer :content-max="800"> - <MkPagination v-slot="{items}" :pagination="pagination" class="ruryvtyk _autoGap"> + <MkPagination v-slot="{items}" :pagination="pagination" class="ruryvtyk _gaps_m"> <section v-for="(announcement, i) in items" :key="announcement.id" class="announcement _panel"> <div class="header"><span v-if="$i && !announcement.isRead">🆕 </span>{{ announcement.title }}</div> <div class="content"> diff --git a/packages/frontend/src/pages/api-console.vue b/packages/frontend/src/pages/api-console.vue index d62e6aabd1..c8ca3fffa7 100644 --- a/packages/frontend/src/pages/api-console.vue +++ b/packages/frontend/src/pages/api-console.vue @@ -2,8 +2,8 @@ <MkStickyContainer> <template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template> <MkSpacer :content-max="700"> - <div class="_autoGap"> - <div class="_autoGap"> + <div class="_gaps_m"> + <div class="_gaps_m"> <MkInput v-model="endpoint" :datalist="endpoints" @update:model-value="onEndpointChange()"> <template #label>Endpoint</template> </MkInput> diff --git a/packages/frontend/src/pages/channel-editor.vue b/packages/frontend/src/pages/channel-editor.vue index 091573b876..8eaea86917 100644 --- a/packages/frontend/src/pages/channel-editor.vue +++ b/packages/frontend/src/pages/channel-editor.vue @@ -2,7 +2,7 @@ <MkStickyContainer> <template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template> <MkSpacer :content-max="700"> - <div class="_autoGap"> + <div class="_gaps_m"> <MkInput v-model="name"> <template #label>{{ i18n.ts.name }}</template> </MkInput> diff --git a/packages/frontend/src/pages/flash/flash-edit.vue b/packages/frontend/src/pages/flash/flash-edit.vue index b9b588a121..3b7535071f 100644 --- a/packages/frontend/src/pages/flash/flash-edit.vue +++ b/packages/frontend/src/pages/flash/flash-edit.vue @@ -2,7 +2,7 @@ <MkStickyContainer> <template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template> <MkSpacer :content-max="700"> - <div class="_autoGap"> + <div class="_gaps_m"> <MkInput v-model="title"> <template #label>{{ i18n.ts._play.title }}</template> </MkInput> diff --git a/packages/frontend/src/pages/flash/flash-index.vue b/packages/frontend/src/pages/flash/flash-index.vue index 9964b34150..fb377be579 100644 --- a/packages/frontend/src/pages/flash/flash-index.vue +++ b/packages/frontend/src/pages/flash/flash-index.vue @@ -4,7 +4,7 @@ <MkSpacer :content-max="700"> <div v-if="tab === 'featured'" class=""> <MkPagination v-slot="{items}" :pagination="featuredFlashsPagination"> - <div class="_autoGap_half"> + <div class="_gaps_s"> <MkFlashPreview v-for="flash in items" :key="flash.id" class="" :flash="flash"/> </div> </MkPagination> @@ -13,7 +13,7 @@ <div v-else-if="tab === 'my'" class="my"> <MkButton class="new" @click="create()"><i class="ti ti-plus"></i></MkButton> <MkPagination v-slot="{items}" :pagination="myFlashsPagination"> - <div class="_autoGap_half"> + <div class="_gaps_s"> <MkFlashPreview v-for="flash in items" :key="flash.id" class="" :flash="flash"/> </div> </MkPagination> @@ -21,7 +21,7 @@ <div v-else-if="tab === 'liked'" class=""> <MkPagination v-slot="{items}" :pagination="likedFlashsPagination"> - <div class="_autoGap_half"> + <div class="_gaps_s"> <MkFlashPreview v-for="like in items" :key="like.flash.id" class="" :flash="like.flash"/> </div> </MkPagination> diff --git a/packages/frontend/src/pages/instance-info.vue b/packages/frontend/src/pages/instance-info.vue index 5fc341198e..e0f0d855eb 100644 --- a/packages/frontend/src/pages/instance-info.vue +++ b/packages/frontend/src/pages/instance-info.vue @@ -2,7 +2,7 @@ <MkStickyContainer> <template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template> <MkSpacer v-if="instance" :content-max="600" :margin-min="16" :margin-max="32"> - <div v-if="tab === 'overview'" class="_autoGap"> + <div v-if="tab === 'overview'" class="_gaps_m"> <div class="fnfelxur"> <img :src="faviconUrl" alt="" class="icon"/> <span class="name">{{ instance.name || `(${i18n.ts.unknown})` }}</span> @@ -28,7 +28,7 @@ <FormSection v-if="iAmModerator"> <template #label>Moderation</template> - <div class="_autoGap_half"> + <div class="_gaps_s"> <FormSwitch v-model="suspended" @update:model-value="toggleSuspend">{{ i18n.ts.stopActivityDelivery }}</FormSwitch> <FormSwitch v-model="isBlocked" @update:model-value="toggleBlock">{{ i18n.ts.blockThisInstance }}</FormSwitch> <MkButton @click="refreshMetadata"><i class="ti ti-refresh"></i> Refresh metadata</MkButton> @@ -70,7 +70,7 @@ <FormLink :to="`https://${host}/manifest.json`" external style="margin-bottom: 8px;">manifest.json</FormLink> </FormSection> </div> - <div v-else-if="tab === 'chart'" class="_autoGap"> + <div v-else-if="tab === 'chart'" class="_gaps_m"> <div class="cmhjzshl"> <div class="selects"> <MkSelect v-model="chartSrc" style="margin: 0 10px 0 0; flex: 1;"> @@ -95,14 +95,14 @@ </div> </div> </div> - <div v-else-if="tab === 'users'" class="_autoGap"> + <div v-else-if="tab === 'users'" class="_gaps_m"> <MkPagination v-slot="{items}" :pagination="usersPagination" style="display: grid; grid-template-columns: repeat(auto-fill,minmax(270px,1fr)); grid-gap: 12px;"> <MkA v-for="user in items" :key="user.id" v-tooltip.mfm="`Last posted: ${dateString(user.updatedAt)}`" class="user" :to="`/user-info/${user.id}`"> <MkUserCardMini :user="user"/> </MkA> </MkPagination> </div> - <div v-else-if="tab === 'raw'" class="_autoGap"> + <div v-else-if="tab === 'raw'" class="_gaps_m"> <MkObjectView tall :value="instance"> </MkObjectView> </div> diff --git a/packages/frontend/src/pages/my-antennas/editor.vue b/packages/frontend/src/pages/my-antennas/editor.vue index ccbc6d1551..6e7405735e 100644 --- a/packages/frontend/src/pages/my-antennas/editor.vue +++ b/packages/frontend/src/pages/my-antennas/editor.vue @@ -1,7 +1,7 @@ <template> <MkSpacer :content-max="700"> <div class="shaynizk"> - <div class="_autoGap"> + <div class="_gaps_m"> <MkInput v-model="name"> <template #label>{{ i18n.ts.name }}</template> </MkInput> diff --git a/packages/frontend/src/pages/page-editor/page-editor.vue b/packages/frontend/src/pages/page-editor/page-editor.vue index ef48a2033d..a01daf9a27 100644 --- a/packages/frontend/src/pages/page-editor/page-editor.vue +++ b/packages/frontend/src/pages/page-editor/page-editor.vue @@ -10,7 +10,7 @@ </div> <div v-if="tab === 'settings'"> - <div class="_autoGap"> + <div class="_gaps_m"> <MkInput v-model="title"> <template #label>{{ $ts._pages.title }}</template> </MkInput> diff --git a/packages/frontend/src/pages/registry.keys.vue b/packages/frontend/src/pages/registry.keys.vue index 1689380650..2c2a1444c1 100644 --- a/packages/frontend/src/pages/registry.keys.vue +++ b/packages/frontend/src/pages/registry.keys.vue @@ -2,7 +2,7 @@ <MkStickyContainer> <template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template> <MkSpacer :content-max="600" :margin-min="16"> - <div class="_autoGap"> + <div class="_gaps_m"> <FormSplit> <MkKeyValue> <template #key>{{ i18n.ts._registry.domain }}</template> diff --git a/packages/frontend/src/pages/registry.value.vue b/packages/frontend/src/pages/registry.value.vue index 537dd596a8..5c747564f6 100644 --- a/packages/frontend/src/pages/registry.value.vue +++ b/packages/frontend/src/pages/registry.value.vue @@ -2,7 +2,7 @@ <MkStickyContainer> <template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template> <MkSpacer :content-max="600" :margin-min="16"> - <div class="_autoGap"> + <div class="_gaps_m"> <FormInfo warn>{{ i18n.ts.editTheseSettingsMayBreakAccount }}</FormInfo> <template v-if="value"> diff --git a/packages/frontend/src/pages/reset-password.vue b/packages/frontend/src/pages/reset-password.vue index 0e790190b2..1ecf883c14 100644 --- a/packages/frontend/src/pages/reset-password.vue +++ b/packages/frontend/src/pages/reset-password.vue @@ -2,7 +2,7 @@ <MkStickyContainer> <template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template> <MkSpacer v-if="token" :content-max="700" :margin-min="16" :margin-max="32"> - <div class="_autoGap"> + <div class="_gaps_m"> <FormInput v-model="password" type="password"> <template #prefix><i class="ti ti-lock"></i></template> <template #label>{{ i18n.ts.newPassword }}</template> diff --git a/packages/frontend/src/pages/settings/account-info.vue b/packages/frontend/src/pages/settings/account-info.vue index 8f04d0cbe4..584808b0b4 100644 --- a/packages/frontend/src/pages/settings/account-info.vue +++ b/packages/frontend/src/pages/settings/account-info.vue @@ -1,5 +1,5 @@ <template> -<div class="_autoGap"> +<div class="_gaps_m"> <MkKeyValue> <template #key>ID</template> <template #value><span class="_monospace">{{ $i.id }}</span></template> diff --git a/packages/frontend/src/pages/settings/accounts.vue b/packages/frontend/src/pages/settings/accounts.vue index e27b11cf6f..c66cc12925 100644 --- a/packages/frontend/src/pages/settings/accounts.vue +++ b/packages/frontend/src/pages/settings/accounts.vue @@ -1,5 +1,5 @@ <template> -<div class="_autoGap"> +<div class="_gaps_m"> <FormSuspense :p="init"> <MkButton primary @click="addAccount"><i class="ti ti-plus"></i> {{ i18n.ts.addAccount }}</MkButton> diff --git a/packages/frontend/src/pages/settings/api.vue b/packages/frontend/src/pages/settings/api.vue index 49240b01f7..0fb44d9fad 100644 --- a/packages/frontend/src/pages/settings/api.vue +++ b/packages/frontend/src/pages/settings/api.vue @@ -1,5 +1,5 @@ <template> -<div class="_autoGap"> +<div class="_gaps_m"> <MkButton primary @click="generateToken">{{ i18n.ts.generateAccessToken }}</MkButton> <FormLink to="/settings/apps">{{ i18n.ts.manageAccessTokens }}</FormLink> <FormLink to="/api-console" :behavior="isDesktop ? 'window' : null">API console</FormLink> diff --git a/packages/frontend/src/pages/settings/apps.vue b/packages/frontend/src/pages/settings/apps.vue index 43ccdb1a97..861414cef8 100644 --- a/packages/frontend/src/pages/settings/apps.vue +++ b/packages/frontend/src/pages/settings/apps.vue @@ -1,5 +1,5 @@ <template> -<div class="_autoGap"> +<div class="_gaps_m"> <FormPagination ref="list" :pagination="pagination"> <template #empty> <div class="_fullinfo"> diff --git a/packages/frontend/src/pages/settings/custom-css.vue b/packages/frontend/src/pages/settings/custom-css.vue index 4d054fd2c7..9fa9bdd658 100644 --- a/packages/frontend/src/pages/settings/custom-css.vue +++ b/packages/frontend/src/pages/settings/custom-css.vue @@ -1,5 +1,5 @@ <template> -<div class="_autoGap"> +<div class="_gaps_m"> <FormInfo warn>{{ i18n.ts.customCssWarn }}</FormInfo> <FormTextarea v-model="localCustomCss" manual-save tall class="_monospace" style="tab-size: 2;"> diff --git a/packages/frontend/src/pages/settings/deck.vue b/packages/frontend/src/pages/settings/deck.vue index 5506f61509..4455b90c0b 100644 --- a/packages/frontend/src/pages/settings/deck.vue +++ b/packages/frontend/src/pages/settings/deck.vue @@ -1,5 +1,5 @@ <template> -<div class="_autoGap"> +<div class="_gaps_m"> <FormSwitch v-model="navWindow">{{ i18n.ts.defaultNavigationBehaviour }}: {{ i18n.ts.openInWindow }}</FormSwitch> <FormSwitch v-model="alwaysShowMainColumn">{{ i18n.ts._deck.alwaysShowMainColumn }}</FormSwitch> diff --git a/packages/frontend/src/pages/settings/delete-account.vue b/packages/frontend/src/pages/settings/delete-account.vue index 58abe6001a..bbd5513954 100644 --- a/packages/frontend/src/pages/settings/delete-account.vue +++ b/packages/frontend/src/pages/settings/delete-account.vue @@ -1,5 +1,5 @@ <template> -<div class="_autoGap"> +<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">{{ i18n.ts._accountDelete.requestAccountDelete }}</MkButton> diff --git a/packages/frontend/src/pages/settings/drive.vue b/packages/frontend/src/pages/settings/drive.vue index bbf4da02e2..acfee9537b 100644 --- a/packages/frontend/src/pages/settings/drive.vue +++ b/packages/frontend/src/pages/settings/drive.vue @@ -1,9 +1,9 @@ <template> -<div class="_autoGap"> +<div class="_gaps_m"> <FormSection v-if="!fetching" first> <template #label>{{ i18n.ts.usageAmount }}</template> - <div class="_autoGap"> + <div class="_gaps_m"> <div class="uawsfosz"> <div class="meter"><div :style="meterStyle"></div></div> </div> @@ -26,7 +26,7 @@ </FormSection> <FormSection> - <div class="_autoGap"> + <div class="_gaps_m"> <FormLink @click="chooseUploadFolder()"> {{ i18n.ts.uploadFolder }} <template #suffix>{{ uploadFolder ? uploadFolder.name : '-' }}</template> diff --git a/packages/frontend/src/pages/settings/email.vue b/packages/frontend/src/pages/settings/email.vue index d7b03d9eac..569115bda8 100644 --- a/packages/frontend/src/pages/settings/email.vue +++ b/packages/frontend/src/pages/settings/email.vue @@ -1,5 +1,5 @@ <template> -<div class="_autoGap"> +<div class="_gaps_m"> <FormSection first> <template #label>{{ i18n.ts.emailAddress }}</template> <FormInput v-model="emailAddress" type="email" manual-save> @@ -18,7 +18,7 @@ <FormSection> <template #label>{{ i18n.ts.emailNotification }}</template> - <div class="_autoGap_half"> + <div class="_gaps_s"> <FormSwitch v-model="emailNotification_mention"> {{ i18n.ts._notification._types.mention }} </FormSwitch> diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue index f2d6bbe1f7..b90dc3da0e 100644 --- a/packages/frontend/src/pages/settings/general.vue +++ b/packages/frontend/src/pages/settings/general.vue @@ -1,5 +1,5 @@ <template> -<div class="_autoGap"> +<div class="_gaps_m"> <FormSelect v-model="lang"> <template #label>{{ i18n.ts.uiLanguage }}</template> <option v-for="x in langs" :key="x[0]" :value="x[0]">{{ x[1] }}</option> @@ -25,8 +25,8 @@ <FormSection> <template #label>{{ i18n.ts.behavior }}</template> - <div class="_autoGap"> - <div class="_autoGap_half"> + <div class="_gaps_m"> + <div class="_gaps_s"> <FormSwitch v-model="imageNewTab">{{ i18n.ts.openImageInNewTab }}</FormSwitch> <FormSwitch v-model="enableInfiniteScroll">{{ i18n.ts.enableInfiniteScroll }}</FormSwitch> <FormSwitch v-model="useReactionPickerForContextMenu">{{ i18n.ts.useReactionPickerForContextMenu }}</FormSwitch> @@ -43,8 +43,8 @@ <FormSection> <template #label>{{ i18n.ts.appearance }}</template> - <div class="_autoGap"> - <div class="_autoGap_half"> + <div class="_gaps_m"> + <div class="_gaps_s"> <FormSwitch v-model="disableAnimatedMfm">{{ i18n.ts.disableAnimatedMfm }}</FormSwitch> <FormSwitch v-model="reduceAnimation">{{ i18n.ts.reduceUiAnimation }}</FormSwitch> <FormSwitch v-model="useBlurEffect">{{ i18n.ts.useBlurEffect }}</FormSwitch> diff --git a/packages/frontend/src/pages/settings/import-export.vue b/packages/frontend/src/pages/settings/import-export.vue index 9b6a59bee2..d055304824 100644 --- a/packages/frontend/src/pages/settings/import-export.vue +++ b/packages/frontend/src/pages/settings/import-export.vue @@ -1,5 +1,5 @@ <template> -<div class="_autoGap"> +<div class="_gaps_m"> <FormSection first> <template #label><i class="ti ti-pencil"></i> {{ i18n.ts._exportOrImport.allNotes }}</template> <FormFolder> @@ -18,11 +18,11 @@ </FormSection> <FormSection> <template #label><i class="ti ti-users"></i> {{ i18n.ts._exportOrImport.followingList }}</template> - <div class="_autoGap_half"> + <div class="_gaps_s"> <FormFolder> <template #label>{{ i18n.ts.export }}</template> <template #icon><i class="ti ti-download"></i></template> - <div class="_autoGap_half"> + <div class="_gaps_s"> <FormSwitch v-model="excludeMutingUsers"> {{ i18n.ts._exportOrImport.excludeMutingUsers }} </FormSwitch> @@ -41,7 +41,7 @@ </FormSection> <FormSection> <template #label><i class="ti ti-users"></i> {{ i18n.ts._exportOrImport.userLists }}</template> - <div class="_autoGap_half"> + <div class="_gaps_s"> <FormFolder> <template #label>{{ i18n.ts.export }}</template> <template #icon><i class="ti ti-download"></i></template> @@ -56,7 +56,7 @@ </FormSection> <FormSection> <template #label><i class="ti ti-user-off"></i> {{ i18n.ts._exportOrImport.muteList }}</template> - <div class="_autoGap_half"> + <div class="_gaps_s"> <FormFolder> <template #label>{{ i18n.ts.export }}</template> <template #icon><i class="ti ti-download"></i></template> @@ -71,7 +71,7 @@ </FormSection> <FormSection> <template #label><i class="ti ti-user-off"></i> {{ i18n.ts._exportOrImport.blockingList }}</template> - <div class="_autoGap_half"> + <div class="_gaps_s"> <FormFolder> <template #label>{{ i18n.ts.export }}</template> <template #icon><i class="ti ti-download"></i></template> diff --git a/packages/frontend/src/pages/settings/instance-mute.vue b/packages/frontend/src/pages/settings/instance-mute.vue index 6436be5b08..ccfbc89b87 100644 --- a/packages/frontend/src/pages/settings/instance-mute.vue +++ b/packages/frontend/src/pages/settings/instance-mute.vue @@ -1,5 +1,5 @@ <template> -<div class="_autoGap"> +<div class="_gaps_m"> <MkInfo>{{ i18n.ts._instanceMute.title }}</MkInfo> <FormTextarea v-model="instanceMutes"> <template #label>{{ i18n.ts._instanceMute.heading }}</template> diff --git a/packages/frontend/src/pages/settings/integration.vue b/packages/frontend/src/pages/settings/integration.vue index fe8087eb62..1e5a785465 100644 --- a/packages/frontend/src/pages/settings/integration.vue +++ b/packages/frontend/src/pages/settings/integration.vue @@ -1,5 +1,5 @@ <template> -<div class="_autoGap"> +<div class="_gaps_m"> <FormSection v-if="instance.enableTwitterIntegration"> <template #label><i class="ti ti-brand-twitter"></i> Twitter</template> <p v-if="integrations.twitter">{{ i18n.ts.connectedTo }}: <a :href="`https://twitter.com/${integrations.twitter.screenName}`" rel="nofollow noopener" target="_blank">@{{ integrations.twitter.screenName }}</a></p> diff --git a/packages/frontend/src/pages/settings/mute-block.vue b/packages/frontend/src/pages/settings/mute-block.vue index 7a72d4a643..48579aa069 100644 --- a/packages/frontend/src/pages/settings/mute-block.vue +++ b/packages/frontend/src/pages/settings/mute-block.vue @@ -1,5 +1,5 @@ <template> -<div class="_autoGap"> +<div class="_gaps_m"> <MkTab v-model="tab" style="margin-bottom: var(--margin);"> <option value="mute">{{ i18n.ts.mutedUsers }}</option> <option value="block">{{ i18n.ts.blockedUsers }}</option> diff --git a/packages/frontend/src/pages/settings/navbar.vue b/packages/frontend/src/pages/settings/navbar.vue index 1cbb72ab12..0492f2e8af 100644 --- a/packages/frontend/src/pages/settings/navbar.vue +++ b/packages/frontend/src/pages/settings/navbar.vue @@ -1,5 +1,5 @@ <template> -<div class="_autoGap"> +<div class="_gaps_m"> <FormTextarea v-model="items" tall manual-save> <template #label>{{ i18n.ts.navbar }}</template> <template #caption><button class="_textButton" @click="addItem">{{ i18n.ts.addItem }}</button></template> diff --git a/packages/frontend/src/pages/settings/notifications.vue b/packages/frontend/src/pages/settings/notifications.vue index 372e6a3511..8799f44041 100644 --- a/packages/frontend/src/pages/settings/notifications.vue +++ b/packages/frontend/src/pages/settings/notifications.vue @@ -1,8 +1,8 @@ <template> -<div class="_autoGap"> +<div class="_gaps_m"> <FormLink @click="configure"><template #icon><i class="ti ti-settings"></i></template>{{ i18n.ts.notificationSetting }}</FormLink> <FormSection> - <div class="_autoGap"> + <div class="_gaps_m"> <FormLink @click="readAllNotifications">{{ i18n.ts.markAsReadAllNotifications }}</FormLink> <FormLink @click="readAllUnreadNotes">{{ i18n.ts.markAsReadAllUnreadNotes }}</FormLink> <FormLink @click="readAllMessagingMessages">{{ i18n.ts.markAsReadAllTalkMessages }}</FormLink> @@ -11,7 +11,7 @@ <FormSection> <template #label>{{ i18n.ts.pushNotification }}</template> - <div class="_autoGap"> + <div class="_gaps_m"> <MkPushNotificationAllowButton ref="allowButton"/> <FormSwitch :disabled="!pushRegistrationInServer" :model-value="sendReadMessage" @update:model-value="onChangeSendReadMessage"> <template #label>{{ i18n.ts.sendPushNotificationReadMessage }}</template> diff --git a/packages/frontend/src/pages/settings/other.vue b/packages/frontend/src/pages/settings/other.vue index 82fca7fd14..f3c1b3dc2d 100644 --- a/packages/frontend/src/pages/settings/other.vue +++ b/packages/frontend/src/pages/settings/other.vue @@ -1,5 +1,5 @@ <template> -<div class="_autoGap"> +<div class="_gaps_m"> <FormSwitch v-model="$i.injectFeaturedNote" @update:model-value="onChangeInjectFeaturedNote"> {{ i18n.ts.showFeaturedNotesInTimeline }} </FormSwitch> diff --git a/packages/frontend/src/pages/settings/plugin.install.vue b/packages/frontend/src/pages/settings/plugin.install.vue index eda8456f80..09bd9dd9e1 100644 --- a/packages/frontend/src/pages/settings/plugin.install.vue +++ b/packages/frontend/src/pages/settings/plugin.install.vue @@ -1,5 +1,5 @@ <template> -<div class="_autoGap"> +<div class="_gaps_m"> <FormInfo warn>{{ i18n.ts._plugin.installWarn }}</FormInfo> <FormTextarea v-model="code" tall> diff --git a/packages/frontend/src/pages/settings/plugin.vue b/packages/frontend/src/pages/settings/plugin.vue index 13955288a3..c2f80ceac2 100644 --- a/packages/frontend/src/pages/settings/plugin.vue +++ b/packages/frontend/src/pages/settings/plugin.vue @@ -1,10 +1,10 @@ <template> -<div class="_autoGap"> +<div class="_gaps_m"> <FormLink 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> - <div v-for="plugin in plugins" :key="plugin.id" class="_panel _autoGap_half" style="padding: 20px;"> + <div v-for="plugin in plugins" :key="plugin.id" class="_panel _gaps_s" style="padding: 20px;"> <span style="display: flex;"><b>{{ plugin.name }}</b><span style="margin-left: auto;">v{{ plugin.version }}</span></span> <FormSwitch :model-value="plugin.active" @update:model-value="changeActive(plugin, $event)">{{ i18n.ts.makeActive }}</FormSwitch> diff --git a/packages/frontend/src/pages/settings/preferences-backups.vue b/packages/frontend/src/pages/settings/preferences-backups.vue index bc19234c43..0c32676c89 100644 --- a/packages/frontend/src/pages/settings/preferences-backups.vue +++ b/packages/frontend/src/pages/settings/preferences-backups.vue @@ -1,5 +1,5 @@ <template> -<div class="_autoGap"> +<div class="_gaps_m"> <div :class="$style.buttons"> <MkButton inline primary @click="saveNew">{{ ts._preferencesBackups.saveNew }}</MkButton> <MkButton inline @click="loadFile">{{ ts._preferencesBackups.loadFile }}</MkButton> @@ -8,7 +8,7 @@ <FormSection> <template #label>{{ ts._preferencesBackups.list }}</template> <template v-if="profiles && Object.keys(profiles).length > 0"> - <div class="_autoGap_half"> + <div class="_gaps_s"> <div v-for="(profile, id) in profiles" :key="id" diff --git a/packages/frontend/src/pages/settings/privacy.vue b/packages/frontend/src/pages/settings/privacy.vue index 94ad35df4e..b0e59e7967 100644 --- a/packages/frontend/src/pages/settings/privacy.vue +++ b/packages/frontend/src/pages/settings/privacy.vue @@ -1,5 +1,5 @@ <template> -<div class="_autoGap"> +<div class="_gaps_m"> <FormSwitch v-model="isLocked" @update:model-value="save()">{{ i18n.ts.makeFollowManuallyApprove }}<template #caption>{{ i18n.ts.lockedAccountInfo }}</template></FormSwitch> <FormSwitch v-if="isLocked" v-model="autoAcceptFollowed" @update:model-value="save()">{{ i18n.ts.autoAcceptFollowed }}</FormSwitch> @@ -30,7 +30,7 @@ </FormSwitch> <FormSection> - <div class="_autoGap"> + <div class="_gaps_m"> <FormSwitch v-model="rememberNoteVisibility" @update:model-value="save()">{{ i18n.ts.rememberNoteVisibility }}</FormSwitch> <FormFolder v-if="!rememberNoteVisibility"> <template #label>{{ i18n.ts.defaultNoteVisibility }}</template> @@ -39,7 +39,7 @@ <template v-else-if="defaultNoteVisibility === 'followers'" #suffix>{{ i18n.ts._visibility.followers }}</template> <template v-else-if="defaultNoteVisibility === 'specified'" #suffix>{{ i18n.ts._visibility.specified }}</template> - <div class="_autoGap"> + <div class="_gaps_m"> <FormSelect v-model="defaultNoteVisibility"> <option value="public">{{ i18n.ts._visibility.public }}</option> <option value="home">{{ i18n.ts._visibility.home }}</option> diff --git a/packages/frontend/src/pages/settings/profile.vue b/packages/frontend/src/pages/settings/profile.vue index 6ee2efe467..9727cf9c95 100644 --- a/packages/frontend/src/pages/settings/profile.vue +++ b/packages/frontend/src/pages/settings/profile.vue @@ -1,5 +1,5 @@ <template> -<div class="_autoGap"> +<div class="_gaps_m"> <div class="llvierxe" :style="{ backgroundImage: $i.bannerUrl ? `url(${ $i.bannerUrl })` : null }"> <div class="avatar"> <MkAvatar class="avatar" :user="$i" :disable-link="true" @click="changeAvatar"/> @@ -37,7 +37,7 @@ <template #icon><i class="ti ti-list"></i></template> <template #label>{{ i18n.ts._profile.metadataEdit }}</template> - <div class="_autoGap"> + <div class="_gaps_m"> <FormSplit v-for="(record, i) in fields" :min-width="250"> <FormInput v-model="record.name" small> <template #label>{{ i18n.ts._profile.metadataLabel }} #{{ i + 1 }}</template> @@ -58,7 +58,7 @@ <FormFolder> <template #label>{{ i18n.ts.advancedSettings }}</template> - <div class="_autoGap"> + <div class="_gaps_m"> <FormSwitch v-model="profile.isCat">{{ i18n.ts.flagAsCat }}<template #caption>{{ i18n.ts.flagAsCatDescription }}</template></FormSwitch> <FormSwitch v-model="profile.isBot">{{ i18n.ts.flagAsBot }}<template #caption>{{ i18n.ts.flagAsBotDescription }}</template></FormSwitch> </div> diff --git a/packages/frontend/src/pages/settings/reaction.vue b/packages/frontend/src/pages/settings/reaction.vue index c5ffce2772..95c3a8f5bb 100644 --- a/packages/frontend/src/pages/settings/reaction.vue +++ b/packages/frontend/src/pages/settings/reaction.vue @@ -1,5 +1,5 @@ <template> -<div class="_autoGap"> +<div class="_gaps_m"> <FromSlot> <template #label>{{ i18n.ts.reactionSettingDescription }}</template> <div v-panel style="border-radius: 6px;"> diff --git a/packages/frontend/src/pages/settings/security.vue b/packages/frontend/src/pages/settings/security.vue index c83612292b..0f9ef99713 100644 --- a/packages/frontend/src/pages/settings/security.vue +++ b/packages/frontend/src/pages/settings/security.vue @@ -1,5 +1,5 @@ <template> -<div class="_autoGap"> +<div class="_gaps_m"> <FormSection first> <template #label>{{ i18n.ts.password }}</template> <MkButton primary @click="change()">{{ i18n.ts.changePassword }}</MkButton> diff --git a/packages/frontend/src/pages/settings/sounds.sound.vue b/packages/frontend/src/pages/settings/sounds.sound.vue index 20070e674b..23ecea86cc 100644 --- a/packages/frontend/src/pages/settings/sounds.sound.vue +++ b/packages/frontend/src/pages/settings/sounds.sound.vue @@ -1,5 +1,5 @@ <template> -<div class="_autoGap"> +<div class="_gaps_m"> <FormSelect v-model="type"> <template #label>{{ i18n.ts.sound }}</template> <option v-for="x in soundsTypes" :key="x" :value="x">{{ x == null ? i18n.ts.none : x }}</option> diff --git a/packages/frontend/src/pages/settings/sounds.vue b/packages/frontend/src/pages/settings/sounds.vue index d6ccb4ccdb..dae6ad3037 100644 --- a/packages/frontend/src/pages/settings/sounds.vue +++ b/packages/frontend/src/pages/settings/sounds.vue @@ -1,12 +1,12 @@ <template> -<div class="_autoGap"> +<div class="_gaps_m"> <FormRange v-model="masterVolume" :min="0" :max="1" :step="0.05" :text-converter="(v) => `${Math.floor(v * 100)}%`"> <template #label>{{ i18n.ts.masterVolume }}</template> </FormRange> <FormSection> <template #label>{{ i18n.ts.sounds }}</template> - <div class="_autoGap_half"> + <div class="_gaps_s"> <FormFolder v-for="type in Object.keys(sounds)" :key="type"> <template #label>{{ $t('_sfx.' + type) }}</template> <template #suffix>{{ sounds[type].type ?? i18n.ts.none }}</template> diff --git a/packages/frontend/src/pages/settings/statusbar.statusbar.vue b/packages/frontend/src/pages/settings/statusbar.statusbar.vue index 78ed1c4b45..890f92672e 100644 --- a/packages/frontend/src/pages/settings/statusbar.statusbar.vue +++ b/packages/frontend/src/pages/settings/statusbar.statusbar.vue @@ -1,5 +1,5 @@ <template> -<div class="_autoGap"> +<div class="_gaps_m"> <FormSelect v-model="statusbar.type" placeholder="Please select"> <template #label>{{ i18n.ts.type }}</template> <option value="rss">RSS</option> diff --git a/packages/frontend/src/pages/settings/statusbar.vue b/packages/frontend/src/pages/settings/statusbar.vue index 2435bd6bad..26cb75e938 100644 --- a/packages/frontend/src/pages/settings/statusbar.vue +++ b/packages/frontend/src/pages/settings/statusbar.vue @@ -1,5 +1,5 @@ <template> -<div class="_autoGap"> +<div class="_gaps_m"> <FormFolder v-for="x in statusbars" :key="x.id"> <template #label>{{ x.type ?? i18n.ts.notSet }}</template> <template #suffix>{{ x.name }}</template> diff --git a/packages/frontend/src/pages/settings/theme.install.vue b/packages/frontend/src/pages/settings/theme.install.vue index b1010624c7..633792195b 100644 --- a/packages/frontend/src/pages/settings/theme.install.vue +++ b/packages/frontend/src/pages/settings/theme.install.vue @@ -1,5 +1,5 @@ <template> -<div class="_autoGap"> +<div class="_gaps_m"> <FormTextarea v-model="installThemeCode"> <template #label>{{ i18n.ts._theme.code }}</template> </FormTextarea> diff --git a/packages/frontend/src/pages/settings/theme.manage.vue b/packages/frontend/src/pages/settings/theme.manage.vue index 660e8ca02e..215482065a 100644 --- a/packages/frontend/src/pages/settings/theme.manage.vue +++ b/packages/frontend/src/pages/settings/theme.manage.vue @@ -1,5 +1,5 @@ <template> -<div class="_autoGap"> +<div class="_gaps_m"> <FormSelect v-model="selectedThemeId"> <template #label>{{ i18n.ts.theme }}</template> <optgroup :label="i18n.ts._theme.installedThemes"> diff --git a/packages/frontend/src/pages/settings/theme.vue b/packages/frontend/src/pages/settings/theme.vue index 17aaa6e21b..60aa80647d 100644 --- a/packages/frontend/src/pages/settings/theme.vue +++ b/packages/frontend/src/pages/settings/theme.vue @@ -1,5 +1,5 @@ <template> -<div class="_autoGap rsljpzjq"> +<div class="_gaps_m rsljpzjq"> <div v-adaptive-border class="rfqxtzch _panel"> <div class="toggle"> <div class="toggleWrapper"> diff --git a/packages/frontend/src/pages/settings/webhook.edit.vue b/packages/frontend/src/pages/settings/webhook.edit.vue index 4bb2c1d27e..8c8492ba5f 100644 --- a/packages/frontend/src/pages/settings/webhook.edit.vue +++ b/packages/frontend/src/pages/settings/webhook.edit.vue @@ -1,5 +1,5 @@ <template> -<div class="_autoGap"> +<div class="_gaps_m"> <FormInput v-model="name"> <template #label>Name</template> </FormInput> @@ -16,7 +16,7 @@ <FormSection> <template #label>Events</template> - <div class="_autoGap_half"> + <div class="_gaps_s"> <FormSwitch v-model="event_follow">Follow</FormSwitch> <FormSwitch v-model="event_followed">Followed</FormSwitch> <FormSwitch v-model="event_note">Note</FormSwitch> diff --git a/packages/frontend/src/pages/settings/webhook.new.vue b/packages/frontend/src/pages/settings/webhook.new.vue index 5b34de55f0..b408b6cb88 100644 --- a/packages/frontend/src/pages/settings/webhook.new.vue +++ b/packages/frontend/src/pages/settings/webhook.new.vue @@ -1,5 +1,5 @@ <template> -<div class="_autoGap"> +<div class="_gaps_m"> <FormInput v-model="name"> <template #label>Name</template> </FormInput> @@ -16,7 +16,7 @@ <FormSection> <template #label>Events</template> - <div class="_autoGap_half"> + <div class="_gaps_s"> <FormSwitch v-model="event_follow">Follow</FormSwitch> <FormSwitch v-model="event_followed">Followed</FormSwitch> <FormSwitch v-model="event_note">Note</FormSwitch> diff --git a/packages/frontend/src/pages/settings/webhook.vue b/packages/frontend/src/pages/settings/webhook.vue index e193f783f9..01c31688cc 100644 --- a/packages/frontend/src/pages/settings/webhook.vue +++ b/packages/frontend/src/pages/settings/webhook.vue @@ -1,5 +1,5 @@ <template> -<div class="_autoGap"> +<div class="_gaps_m"> <FormLink :to="`/settings/webhook/new`"> Create webhook </FormLink> diff --git a/packages/frontend/src/pages/settings/word-mute.vue b/packages/frontend/src/pages/settings/word-mute.vue index 3cd28f6015..d3242f30bf 100644 --- a/packages/frontend/src/pages/settings/word-mute.vue +++ b/packages/frontend/src/pages/settings/word-mute.vue @@ -1,18 +1,18 @@ <template> -<div class="_autoGap"> +<div class="_gaps_m"> <MkTab v-model="tab"> <option value="soft">{{ i18n.ts._wordMute.soft }}</option> <option value="hard">{{ i18n.ts._wordMute.hard }}</option> </MkTab> <div> - <div v-show="tab === 'soft'" class="_autoGap"> + <div v-show="tab === 'soft'" class="_gaps_m"> <MkInfo>{{ i18n.ts._wordMute.softDescription }}</MkInfo> <FormTextarea v-model="softMutedWords"> <span>{{ i18n.ts._wordMute.muteWords }}</span> <template #caption>{{ i18n.ts._wordMute.muteWordsDescription }}<br>{{ i18n.ts._wordMute.muteWordsDescription2 }}</template> </FormTextarea> </div> - <div v-show="tab === 'hard'" class="_autoGap"> + <div v-show="tab === 'hard'" class="_gaps_m"> <MkInfo>{{ i18n.ts._wordMute.hardDescription }} {{ i18n.ts.reflectMayTakeTime }}</MkInfo> <FormTextarea v-model="hardMutedWords"> <span>{{ i18n.ts._wordMute.muteWords }}</span> diff --git a/packages/frontend/src/pages/theme-editor.vue b/packages/frontend/src/pages/theme-editor.vue index 8f2ad4d668..4820bbb763 100644 --- a/packages/frontend/src/pages/theme-editor.vue +++ b/packages/frontend/src/pages/theme-editor.vue @@ -2,7 +2,7 @@ <MkStickyContainer> <template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template> <MkSpacer :content-max="800" :margin-min="16" :margin-max="32"> - <div class="cwepdizn _autoGap"> + <div class="cwepdizn _gaps_m"> <FormFolder :default-open="true"> <template #label>{{ i18n.ts.backgroundColor }}</template> <div class="cwepdizn-colors"> @@ -45,7 +45,7 @@ <template #icon><i class="ti ti-code"></i></template> <template #label>{{ i18n.ts.editCode }}</template> - <div class="_autoGap"> + <div class="_gaps_m"> <FormTextarea v-model="themeCode" tall> <template #label>{{ i18n.ts._theme.code }}</template> </FormTextarea> @@ -56,7 +56,7 @@ <FormFolder :default-open="false"> <template #label>{{ i18n.ts.addDescription }}</template> - <div class="_autoGap"> + <div class="_gaps_m"> <FormTextarea v-model="description"> <template #label>{{ i18n.ts._theme.description }}</template> </FormTextarea> diff --git a/packages/frontend/src/pages/user-info.vue b/packages/frontend/src/pages/user-info.vue index c2b5cd5f4a..5812eae9ff 100644 --- a/packages/frontend/src/pages/user-info.vue +++ b/packages/frontend/src/pages/user-info.vue @@ -3,7 +3,7 @@ <template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template> <MkSpacer :content-max="600" :margin-min="16" :margin-max="32"> <FormSuspense :p="init"> - <div v-if="tab === 'overview'" class="_autoGap"> + <div v-if="tab === 'overview'" class="_gaps_m"> <div class="aeakzknw"> <MkAvatar class="avatar" :user="user" :show-indicator="true"/> <div class="body"> @@ -55,7 +55,7 @@ <FormSection> <template #label>ActivityPub</template> - <div class="_autoGap"> + <div class="_gaps_m"> <div style="display: flex; flex-direction: column; gap: 1em;"> <MkKeyValue v-if="user.host" oneline> <template #key>{{ i18n.ts.instanceInfo }}</template> @@ -86,7 +86,7 @@ </div> </FormSection> </div> - <div v-else-if="tab === 'moderation'" class="_autoGap"> + <div v-else-if="tab === 'moderation'" class="_gaps_m"> <FormSwitch v-if="user.host == null && $i.isAdmin && (moderator || !user.isAdmin)" v-model="moderator" @update:model-value="toggleModerator">{{ i18n.ts.moderator }}</FormSwitch> <FormSwitch v-model="silenced" @update:model-value="toggleSilence">{{ i18n.ts.silence }}</FormSwitch> <FormSwitch v-model="suspended" @update:model-value="toggleSuspend">{{ i18n.ts.suspend }}</FormSwitch> @@ -126,7 +126,7 @@ </FormInput> </FormSection> </div> - <div v-else-if="tab === 'chart'" class="_autoGap"> + <div v-else-if="tab === 'chart'" class="_gaps_m"> <div class="cmhjzshm"> <div class="selects"> <MkSelect v-model="chartSrc" style="margin: 0 10px 0 0; flex: 1;"> @@ -141,7 +141,7 @@ </div> </div> </div> - <div v-else-if="tab === 'raw'" class="_autoGap"> + <div v-else-if="tab === 'raw'" class="_gaps_m"> <MkObjectView v-if="info && $i.isAdmin" tall :value="info"> </MkObjectView> diff --git a/packages/frontend/src/pages/user/home.vue b/packages/frontend/src/pages/user/home.vue index d9d7fc826c..0c66e7a837 100644 --- a/packages/frontend/src/pages/user/home.vue +++ b/packages/frontend/src/pages/user/home.vue @@ -1,12 +1,12 @@ <template> <MkSpacer :content-max="narrow ? 800 : 1100"> <div ref="rootEl" class="ftskorzw" :class="{ wide: !narrow }" style="container-type: inline-size;"> - <div class="main _autoGap"> + <div class="main _gaps"> <!-- TODO --> <!-- <div class="punished" v-if="user.isSuspended"><i class="ti ti-alert-triangle" style="margin-right: 8px;"></i> {{ i18n.ts.userSuspended }}</div> --> <!-- <div class="punished" v-if="user.isSilenced"><i class="ti ti-alert-triangle" style="margin-right: 8px;"></i> {{ i18n.ts.userSilenced }}</div> --> - <div class="profile _autoGap"> + <div class="profile _gaps"> <MkRemoteCaution v-if="user.host != null" :href="user.url" class="warn"/> <div :key="user.id" class="main _panel"> @@ -85,23 +85,23 @@ </div> </div> - <div class="contents"> - <div v-if="user.pinnedNotes.length > 0" class="_autoGap_half"> + <div class="contents _gaps"> + <div v-if="user.pinnedNotes.length > 0" class="_gaps"> <XNote v-for="note in user.pinnedNotes" :key="note.id" class="note _panel" :note="note" :pinned="true"/> </div> <MkInfo v-else-if="$i && $i.id === user.id">{{ i18n.ts.userPagePinTip }}</MkInfo> <template v-if="narrow"> <XPhotos :key="user.id" :user="user"/> - <XActivity :key="user.id" :user="user" style="margin-top: var(--margin);"/> + <XActivity :key="user.id" :user="user"/> </template> </div> <div> <XUserTimeline :user="user"/> </div> </div> - <div v-if="!narrow" class="sub" style="container-type: inline-size;"> + <div v-if="!narrow" class="sub _gaps" style="container-type: inline-size;"> <XPhotos :key="user.id" :user="user"/> - <XActivity :key="user.id" :user="user" style="margin-top: var(--margin);"/> + <XActivity :key="user.id" :user="user"/> </div> </div> </MkSpacer> diff --git a/packages/frontend/src/pages/welcome.setup.vue b/packages/frontend/src/pages/welcome.setup.vue index 0c627be8e4..ea03bd4a85 100644 --- a/packages/frontend/src/pages/welcome.setup.vue +++ b/packages/frontend/src/pages/welcome.setup.vue @@ -1,7 +1,7 @@ <template> <form class="mk-setup" @submit.prevent="submit()"> <h1>Welcome to Misskey!</h1> - <div class="_autoGap"> + <div class="_gaps_m"> <p>{{ $ts.intro }}</p> <MkInput v-model="username" pattern="^[a-zA-Z0-9_]{1,20}$" :spellcheck="false" required data-cy-admin-username> <template #label>{{ $ts.username }}</template> diff --git a/packages/frontend/src/style.scss b/packages/frontend/src/style.scss index 356e793bab..3cd7602423 100644 --- a/packages/frontend/src/style.scss +++ b/packages/frontend/src/style.scss @@ -256,18 +256,24 @@ hr { margin: var(--margin) 0; } -._autoGap { +._gaps_m { display: flex; flex-direction: column; gap: 1.5em; } -._autoGap_half { +._gaps_s { display: flex; flex-direction: column; gap: 0.75em; } +._gaps { + display: flex; + flex-direction: column; + gap: var(--margin); +} + ._buttons { display: flex; gap: 8px; From 3b617fafdd4bd821b0c4e6706a15279d11a24b7b Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Fri, 6 Jan 2023 13:43:10 +0900 Subject: [PATCH 39/60] =?UTF-8?q?enhance(client):=20=E3=82=A2=E3=83=83?= =?UTF-8?q?=E3=83=97=E3=83=87=E3=83=BC=E3=83=88=E6=99=82=E3=81=AB=E3=82=82?= =?UTF-8?q?=E8=8A=B1=E7=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../frontend/src/components/MkUpdated.vue | 7 ++++- packages/frontend/src/pages/user/home.vue | 27 ++----------------- packages/frontend/src/scripts/confetti.ts | 25 +++++++++++++++++ 3 files changed, 33 insertions(+), 26 deletions(-) create mode 100644 packages/frontend/src/scripts/confetti.ts diff --git a/packages/frontend/src/components/MkUpdated.vue b/packages/frontend/src/components/MkUpdated.vue index 2f2864220e..2ed0c6ea30 100644 --- a/packages/frontend/src/components/MkUpdated.vue +++ b/packages/frontend/src/components/MkUpdated.vue @@ -10,12 +10,13 @@ </template> <script lang="ts" setup> -import { shallowRef } from 'vue'; +import { onMounted, shallowRef } from 'vue'; import MkModal from '@/components/MkModal.vue'; import MkButton from '@/components/MkButton.vue'; import MkSparkle from '@/components/MkSparkle.vue'; import { version } from '@/config'; import { i18n } from '@/i18n'; +import { confetti } from '@/scripts/confetti'; const modal = shallowRef<InstanceType<typeof MkModal>>(); @@ -23,6 +24,10 @@ const whatIsNew = () => { modal.value.close(); window.open(`https://misskey-hub.net/docs/releases.html#_${version.replace(/\./g, '-')}`, '_blank'); }; + +onMounted(() => { + confetti(); +}); </script> <style lang="scss" scoped> diff --git a/packages/frontend/src/pages/user/home.vue b/packages/frontend/src/pages/user/home.vue index 0c66e7a837..8c5c4c309a 100644 --- a/packages/frontend/src/pages/user/home.vue +++ b/packages/frontend/src/pages/user/home.vue @@ -110,7 +110,6 @@ <script lang="ts" setup> import { defineAsyncComponent, computed, inject, onMounted, onUnmounted, watch } from 'vue'; import calcAge from 's-age'; -import confetti from 'canvas-confetti'; import * as misskey from 'misskey-js'; import XUserTimeline from './index.timeline.vue'; import XNote from '@/components/MkNote.vue'; @@ -129,6 +128,7 @@ import { useRouter } from '@/router'; import { i18n } from '@/i18n'; import { $i } from '@/account'; import { dateString } from '@/filters/date'; +import { confetti } from '@/scripts/confetti'; const XPhotos = defineAsyncComponent(() => import('./index.photos.vue')); const XActivity = defineAsyncComponent(() => import('./index.activity.vue')); @@ -156,29 +156,6 @@ const age = $computed(() => { return calcAge(props.user.birthday); }); -function birthdayEffect() { - const duration = 1000 * 5; - const animationEnd = Date.now() + duration; - const defaults = { startVelocity: 30, spread: 360, ticks: 60, zIndex: os.claimZIndex('high') }; - - function randomInRange(min, max) { - return Math.random() * (max - min) + min; - } - - const interval = setInterval(() => { - const timeLeft = animationEnd - Date.now(); - - if (timeLeft <= 0) { - return clearInterval(interval); - } - - const particleCount = 50 * (timeLeft / duration); - // since particles fall down, start a bit higher than random - confetti(Object.assign({}, defaults, { particleCount, origin: { x: randomInRange(0.1, 0.3), y: Math.random() - 0.2 } })); - confetti(Object.assign({}, defaults, { particleCount, origin: { x: randomInRange(0.7, 0.9), y: Math.random() - 0.2 } })); - }, 250); -} - function menu(ev) { os.popupMenu(getUserMenu(props.user, router), ev.currentTarget ?? ev.target); } @@ -211,7 +188,7 @@ onMounted(() => { const bm = parseInt(props.user.birthday.split('-')[1]); const bd = parseInt(props.user.birthday.split('-')[2]); if (m === bm && d === bd) { - birthdayEffect(); + confetti(); } } }); diff --git a/packages/frontend/src/scripts/confetti.ts b/packages/frontend/src/scripts/confetti.ts new file mode 100644 index 0000000000..2a9f393904 --- /dev/null +++ b/packages/frontend/src/scripts/confetti.ts @@ -0,0 +1,25 @@ +import _confetti from 'canvas-confetti'; +import * as os from '@/os'; + +export function confetti() { + const duration = 1000 * 5; + const animationEnd = Date.now() + duration; + const defaults = { startVelocity: 30, spread: 360, ticks: 60, zIndex: os.claimZIndex('high') }; + + function randomInRange(min, max) { + return Math.random() * (max - min) + min; + } + + const interval = setInterval(() => { + const timeLeft = animationEnd - Date.now(); + + if (timeLeft <= 0) { + return clearInterval(interval); + } + + const particleCount = 50 * (timeLeft / duration); + // since particles fall down, start a bit higher than random + _confetti(Object.assign({}, defaults, { particleCount, origin: { x: randomInRange(0.1, 0.3), y: Math.random() - 0.2 } })); + _confetti(Object.assign({}, defaults, { particleCount, origin: { x: randomInRange(0.7, 0.9), y: Math.random() - 0.2 } })); + }, 250); +} From 495d513efdb8b2a996287fd54ae5014832352616 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Fri, 6 Jan 2023 13:47:18 +0900 Subject: [PATCH 40/60] disable custom emoji proxy temporary https://github.com/misskey-dev/misskey/pull/9431#issuecomment-1373006446 --- packages/backend/src/server/web/ClientServerService.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/backend/src/server/web/ClientServerService.ts b/packages/backend/src/server/web/ClientServerService.ts index 263b15cc5e..9cb0c4b331 100644 --- a/packages/backend/src/server/web/ClientServerService.ts +++ b/packages/backend/src/server/web/ClientServerService.ts @@ -251,6 +251,10 @@ export class ClientServerService { reply.header('Content-Security-Policy', 'default-src \'none\'; style-src \'unsafe-inline\''); + // || emoji.originalUrl してるのは後方互換性のため(publicUrlはstringなので??はだめ) + return await reply.redirect(301, emoji.publicUrl ?? emoji.originalUrl); + + /* https://github.com/misskey-dev/misskey/pull/9431#issuecomment-1373006446 const url = new URL('/proxy/emoji.webp', this.config.url); // || emoji.originalUrl してるのは後方互換性のため(publicUrlはstringなので??はだめ) url.searchParams.set('url', emoji.publicUrl || emoji.originalUrl); @@ -261,6 +265,7 @@ export class ClientServerService { 301, url.toString(), ); + */ }); fastify.get<{ Params: { path: string } }>('/fluent-emoji/:path(.*)', async (request, reply) => { From 4d2d6154a3b6f5c402700e09c1b5c784bf301d43 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Fri, 6 Jan 2023 13:47:34 +0900 Subject: [PATCH 41/60] New Crowdin updates (#9471) * New translations ja-JP.yml (Romanian) * New translations ja-JP.yml (French) * New translations ja-JP.yml (Spanish) * New translations ja-JP.yml (Arabic) * New translations ja-JP.yml (Czech) * New translations ja-JP.yml (German) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Korean) * New translations ja-JP.yml (Polish) * New translations ja-JP.yml (Russian) * New translations ja-JP.yml (Slovak) * New translations ja-JP.yml (Ukrainian) * New translations ja-JP.yml (Chinese Simplified) * New translations ja-JP.yml (Chinese Traditional) * New translations ja-JP.yml (English) * New translations ja-JP.yml (Vietnamese) * New translations ja-JP.yml (Indonesian) * New translations ja-JP.yml (Bengali) * New translations ja-JP.yml (Thai) * New translations ja-JP.yml (Japanese, Kansai) * New translations ja-JP.yml (Chinese Simplified) * New translations ja-JP.yml (Korean) * New translations ja-JP.yml (Arabic) * New translations ja-JP.yml (German) * New translations ja-JP.yml (Chinese Simplified) * New translations ja-JP.yml (English) * New translations ja-JP.yml (Indonesian) * New translations ja-JP.yml (Korean) * New translations ja-JP.yml (Swedish) * New translations ja-JP.yml (Chinese Simplified) * New translations ja-JP.yml (Arabic) * New translations ja-JP.yml (Chinese Traditional) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) --- locales/ar-SA.yml | 10 ++++++++++ locales/bn-BD.yml | 8 ++++++++ locales/cs-CZ.yml | 4 ++++ locales/de-DE.yml | 22 +++++++++++++++++++++- locales/en-US.yml | 20 ++++++++++++++++++++ locales/es-ES.yml | 8 ++++++++ locales/fr-FR.yml | 8 ++++++++ locales/id-ID.yml | 20 ++++++++++++++++++++ locales/it-IT.yml | 24 ++++++++++++++++-------- locales/ja-KS.yml | 8 ++++++++ locales/ko-KR.yml | 20 ++++++++++++++++++++ locales/pl-PL.yml | 8 ++++++++ locales/ro-RO.yml | 4 ++++ locales/ru-RU.yml | 8 ++++++++ locales/sk-SK.yml | 8 ++++++++ locales/sv-SE.yml | 15 +++++++++++++++ locales/th-TH.yml | 8 ++++++++ locales/uk-UA.yml | 8 ++++++++ locales/vi-VN.yml | 8 ++++++++ locales/zh-CN.yml | 24 ++++++++++++++++++++++-- locales/zh-TW.yml | 9 +++++++++ 21 files changed, 241 insertions(+), 11 deletions(-) diff --git a/locales/ar-SA.yml b/locales/ar-SA.yml index 7a9c235235..161a393bc2 100644 --- a/locales/ar-SA.yml +++ b/locales/ar-SA.yml @@ -380,6 +380,7 @@ administrator: "المدير" token: "الرمز المميز" twoStepAuthentication: "الإستيثاق بعاملَيْن" moderator: "مشرِف" +moderation: "الإشراف" nUsersMentioned: "{n} مستخدمين أُشير إليهم" securityKey: "مفتاح الأمان" securityKeyName: "اسم المفتاح" @@ -813,6 +814,9 @@ colored: "ملوّن" label: "التسمية" localOnly: "المحلي فقط" account: "الحسابات" +cannotLoad: "تعذر التحميل" +like: "أعجبني" +show: "المظهر" _emailUnavailable: used: "هذا البريد الإلكتروني مستخدم" format: "صيغة البريد الإلكتروني غير صالحة" @@ -1228,6 +1232,11 @@ _timelines: local: "المحلي" social: "الاجتماعي" global: "الشامل" +_play: + viewSource: "اظهر المصدر" + featured: "الأكثر شعبية" + title: "العنوان" + summary: "الوصف" _pages: newPage: "أنشئ صفحة جديدة" editPage: "عدّل الصفحة" @@ -1293,6 +1302,7 @@ _notification: yourFollowRequestAccepted: "قُبل طلب المتابعة" youWereInvitedToGroup: "دُعيت إلى فريقٍ" pollEnded: "ظهرت نتائج الاستطلاع" + unreadAntennaNote: "هوائي {name}" _types: all: "الكل" follow: "متابِعون جدد" diff --git a/locales/bn-BD.yml b/locales/bn-BD.yml index a56c06c9da..593cbb1b32 100644 --- a/locales/bn-BD.yml +++ b/locales/bn-BD.yml @@ -851,6 +851,8 @@ colored: "রঙ্গিন" label: "লেবেল" localOnly: "শুধুমাত্র লোকাল" account: "অ্যাকাউন্টগুলি" +like: "পছন্দ করা" +show: "প্রদর্শন" _emailUnavailable: used: "এই ইমেইল ঠিকানাটি ইতোমধ্যে ব্যবহৃত হয়েছে" format: "এই ইমেল ঠিকানাটি সঠিকভাবে লিখা হয়নি" @@ -1319,6 +1321,12 @@ _timelines: local: "স্থানীয়" social: "সামাজিক" global: "গ্লোবাল" +_play: + viewSource: "উৎস দেখুন" + featured: "জনপ্রিয়" + title: "শিরোনাম" + script: "স্ক্রিপ্ট" + summary: "বর্ণনা" _pages: newPage: "নতুন পৃষ্ঠা বানান" editPage: "পৃষ্ঠাটি সম্পাদনা করুন" diff --git a/locales/cs-CZ.yml b/locales/cs-CZ.yml index ebe866651c..2d80008c47 100644 --- a/locales/cs-CZ.yml +++ b/locales/cs-CZ.yml @@ -610,6 +610,7 @@ speed: "Rychlost" slow: "Pomalá" fast: "Rychlá" account: "Účty" +show: "Zobrazit" _ad: back: "Zpět" _gallery: @@ -748,6 +749,9 @@ _charts: _timelines: home: "Domů" global: "Globální" +_play: + script: "Skript" + summary: "Popis" _pages: newPage: "Vytvořit novou stránku" editPage: "Upravit stránku" diff --git a/locales/de-DE.yml b/locales/de-DE.yml index 275e4677d6..db6bd9ab05 100644 --- a/locales/de-DE.yml +++ b/locales/de-DE.yml @@ -609,7 +609,7 @@ regexpErrorDescription: "Im regulären Ausdruck deiner {tab}en Wortstummschaltun instanceMute: "Instanzstummschaltungen" userSaysSomething: "{name} hat etwas gesagt" makeActive: "Aktivieren" -display: "Anzeigeart" +display: "Anzeigen" copy: "Kopieren" metrics: "Metriken" overview: "Übersicht" @@ -916,6 +916,10 @@ loggedInAsBot: "Momentan als Bot angemeldet" tools: "Werkzeuge" cannotLoad: "Kann nicht geladen werden" numberOfProfileView: "Profilaufrufe" +like: "Gefällt mir" +unlike: "\"Gefällt mir\" entfernen" +numberOfLikes: "\"Gefällt mir\"-Anzahl" +show: "Anzeigen" _sensitiveMediaDetection: description: "Ermöglicht eine Erleichterung der Servermoderation durch die automatische Erkennungen von NSFW-Medien unter Verwendung von Machine Learning. Hierdurch wird die Serverlast etwas erhöht." sensitivity: "Erkennungssensitivität" @@ -1315,6 +1319,7 @@ _widgets: jobQueue: "Job-Warteschlange" serverMetric: "Servermetriken" aiscript: "AiScript-Konsole" + aiscriptApp: "AiScript-Anwendung" aichan: "Ai" userList: "Benutzerliste" _userList: @@ -1420,6 +1425,21 @@ _timelines: local: "Lokal" social: "Sozial" global: "Global" +_play: + new: "Play erstellen" + edit: "Play bearbeiten" + created: "Play erfolgreich erstellt" + updated: "Play erfolgreich aktualisiert" + deleted: "Play erfolgreich gelöscht" + pageSetting: "Play-Einstellungen" + editThisPage: "Dieses Play bearbeiten" + viewSource: "Quelltext anzeigen" + my: "Meine Plays" + liked: "Mit \"Gefällt mir\" markierte Plays" + featured: "Beliebt" + title: "Titel" + script: "Skript" + summary: "Beschreibung" _pages: newPage: "Seite erstellen" editPage: "Seite bearbeiten" diff --git a/locales/en-US.yml b/locales/en-US.yml index 9ff4461522..e2a7b32be8 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -916,6 +916,10 @@ loggedInAsBot: "Currently logged in as bot" tools: "Tools" cannotLoad: "Unable to load" numberOfProfileView: "Profile views" +like: "Like" +unlike: "Unlike" +numberOfLikes: "Likes" +show: "Show" _sensitiveMediaDetection: description: "Reduces the effort of server moderation through automatically recognizing NSFW media via Machine Learning. This will slightly increase the load on the server." sensitivity: "Detection sensitivity" @@ -1315,6 +1319,7 @@ _widgets: jobQueue: "Job Queue" serverMetric: "Server metrics" aiscript: "AiScript console" + aiscriptApp: "AiScript App" aichan: "Ai" userList: "User list" _userList: @@ -1420,6 +1425,21 @@ _timelines: local: "Local" social: "Social" global: "Global" +_play: + new: "Create Play" + edit: "Edit Play" + created: "Play created" + updated: "Play edited" + deleted: "Play deleted" + pageSetting: "Play settings" + editThisPage: "Edit this Play" + viewSource: "View source" + my: "My Plays" + liked: "Liked Plays" + featured: "Popular" + title: "Title" + script: "Script" + summary: "Description" _pages: newPage: "Create a new Page" editPage: "Edit this Page" diff --git a/locales/es-ES.yml b/locales/es-ES.yml index 0a1157230a..c328737c4b 100644 --- a/locales/es-ES.yml +++ b/locales/es-ES.yml @@ -916,6 +916,8 @@ loggedInAsBot: "Inicio sesión como cuenta bot." tools: "Utilidades" cannotLoad: "No se puede cargar." numberOfProfileView: "Número de vistas de perfil" +like: "¡Muy bien!" +show: "Apariencia" _sensitiveMediaDetection: description: "Reduce el esfuerzo de la moderación el el servidor a través del reconocimiento automático de contenido NSFW usando 'Machine Learning'. Esto puede incrementar ligeramente la carga en el servidor." sensitivity: "Sensibilidad de detección" @@ -1420,6 +1422,12 @@ _timelines: local: "Local" social: "Social" global: "Global" +_play: + viewSource: "Ver la fuente" + featured: "Popular" + title: "Título" + script: "Script" + summary: "Descripción" _pages: newPage: "Crear página" editPage: "Editar página" diff --git a/locales/fr-FR.yml b/locales/fr-FR.yml index 35c490bcff..0d7399533d 100644 --- a/locales/fr-FR.yml +++ b/locales/fr-FR.yml @@ -910,6 +910,8 @@ caption: "Libellé" loggedInAsBot: "Connecté actuellement en tant que bot" tools: "Outils" cannotLoad: "Chargement impossible" +like: "J'aime" +show: "Affichage" _sensitiveMediaDetection: description: "L'apprentissage automatique peut être utilisé pour détecter automatiquement les médias sensibles à modérer. La sollicitation des serveurs augmente légèrement." sensitivity: "Sensibilité de la détection" @@ -1411,6 +1413,12 @@ _timelines: local: "Local" social: "Social" global: "Global" +_play: + viewSource: "Afficher la source" + featured: "Populaire" + title: "Titre" + script: "Script" + summary: "Description" _pages: newPage: "Créer une page" editPage: "Modifier une page" diff --git a/locales/id-ID.yml b/locales/id-ID.yml index 1fdba0dd40..3a2bf69a72 100644 --- a/locales/id-ID.yml +++ b/locales/id-ID.yml @@ -855,6 +855,10 @@ colored: "Diwarnai" label: "Label" localOnly: "Hanya lokal" account: "Akun" +like: "Suka" +unlike: "Tidak Suka" +numberOfLikes: "Jumlah yang disukai" +show: "Tampilkan" _emailUnavailable: used: "Alamat surel ini telah digunakan" format: "Format tidak valid." @@ -1220,6 +1224,7 @@ _widgets: jobQueue: "Antrian kerja" serverMetric: "Statistik peladen" aiscript: "Konsol AiScript" + aiscriptApp: "Aplikasi AiScript" aichan: "Ai" _userList: chooseList: "Pilih daftar" @@ -1323,6 +1328,21 @@ _timelines: local: "Lokal" social: "Sosial" global: "Global" +_play: + new: "Membuat Permainan" + edit: "Menyunting Permainan" + created: "Permainan sudah dibuat" + updated: "Permainan sudah diperbaharui" + deleted: "Hapus permainan" + pageSetting: "Pengaturan permainan" + editThisPage: "Sunting Permainan ini" + viewSource: "Lihat sumber" + my: "Permainan saya" + liked: "Permainan Disukai" + featured: "Populer" + title: "Judul" + script: "Script" + summary: "Deskripsi" _pages: newPage: "Buat halaman baru" editPage: "Sunting halaman" diff --git a/locales/it-IT.yml b/locales/it-IT.yml index 0298be19ea..3fba19985e 100644 --- a/locales/it-IT.yml +++ b/locales/it-IT.yml @@ -1,7 +1,7 @@ --- _lang_: "Italiano" headlineMisskey: "Rete collegata tramite note" -introMisskey: "Benvenut@! Misskey è un servizio di microblogging decentralizzato, libero e aperto. \nScrivi \"note\" per condividere ciò che sta succedendo adesso o per dire a tutti qualcosa di te. 📡\nGrazie alla funzione \"reazioni\" puoi anche mandare reazioni rapide alle note delle altre persone del Fediverso. 👍\nEsplora un nuovo mondo! 🚀" +introMisskey: "Eccoci! Misskey è un servizio di microblogging decentralizzato, libero e aperto. \n📡 Puoi pubblicare «Note» per condividere ciò che sta succedendo o per dire a tutti qualcosa su di te. \n👍 Puoi reagire inviando emoji rapidi alle «Note» provenienti da altri profili nel Fediverso.\n🚀 Esplora un nuovo mondo insieme a noi!" poweredByMisskeyDescription: "{name} è uno dei servizi (chiamati istanze) che utilizzano la piattaforma open source <b>Misskey</b>." monthAndDay: "{day}/{month}" search: "Cerca" @@ -876,7 +876,7 @@ deleteAccount: "Eliminazione profilo" document: "Documento" numberOfPageCache: "Numero di pagine cache" numberOfPageCacheDescription: "Aumenta l'usabilità, ma aumenta anche il carico e l'utilizzo della memoria." -logoutConfirm: "Sei sicuro di voler effettuare il logout?" +logoutConfirm: "Vuoi davvero uscire da Misskey? " lastActiveDate: "Data dell'ultimo utilizzo" statusbar: "Barra di stato" pleaseSelect: "Scegli un'opzione" @@ -916,6 +916,8 @@ loggedInAsBot: "Connessione come Bot" tools: "Strumenti" cannotLoad: "Caricamento impossibile" numberOfProfileView: "Visualizzazioni profilo" +like: "Mi piace!" +show: "Visualizza" _sensitiveMediaDetection: description: "L'apprendimento automatico può essere utilizzato per individuare automaticamente i media sensibili da moderare. Il carico del server aumenta leggermente." sensitivity: "Sensibilità di rilevamento" @@ -1067,7 +1069,7 @@ _mfm: sparkleDescription: "Aggiungere effetti particellari scintillanti." rotate: "Ruota" rotateDescription: "Ruota con un angolo specificato." - plain: "aereo" + plain: "Testo semplice" plainDescription: "Disattiva tutta la sintassi interna." _instanceTicker: none: "Nascondi" @@ -1205,13 +1207,13 @@ _time: day: "giorni" _tutorial: title: "Come usare Misskey" - step1_1: "Benvenuto/a!" + step1_1: "Eccoci!" step1_2: "Questa pagina si chiama una \" Timeline \". Mostra in ordine cronologico le \" note \" delle persone che segui." step1_3: "Attualmente la tua Timeline è vuota perché non segui alcun profilo e non hai pubblicato alcuna nota ancora." - step2_1: "Prima di scrivere una nota o di seguire altri profili, imposta il tuo di profilo!" + step2_1: "Prima di scrivere una «Nota» o di seguire altri profili, prepara il tuo profilo!" step2_2: "Aggiungere qualche informazione su di te aumenterà le tue possibilità di essere seguit@ da altre persone. " step3_1: "Hai finito di impostare il tuo profilo?" - step3_2: "Ora, puoi pubblicare una nota. Facciamo una prova! Premi il pulsante a forma di penna in cima allo schermo per aprire una finestra di dialogo. " + step3_2: "Ora puoi pubblicare una «Nota». Proviamo subito! Premi il bottone con l'icona «penna» per iniziare a scrivere in una finestra di dialogo. " step3_3: "Scritto il testo della nota, puoi pubblicarla premendo il pulsante nella parte superiore destra della finestra di dialogo." step3_4: "Non ti viene niente in mente? Perché non scrivi semplicemente \"Ho appena cominciato a usare Misskey\"?" step4_1: "Hai pubblicato qualcosa?" @@ -1223,7 +1225,7 @@ _tutorial: step6_1: "Adesso, dovresti essere in grado di vedere le note dagli altri profili sulla tua timeline." step6_2: "Puoi anche rispondere alle note con un click, scegliendo le reazioni immediate." step6_3: "Per inviare una reazione, premi l'icona + della nota e scegli l'emoji che vuoi mandare." - step7_1: "Complimenti! Sei arrivat@ alla fine dell'esercitazione di base su come usare Misskey. " + step7_1: "Congratulazioni! Hai completato l'esercitazione iniziale su come usare Misskey." step7_2: "Se vuoi saperne di più su Misskey, puoi dare un'occhiata alla sezione {help}." step7_3: "Da ultimo, buon divertimento su Misskey! 🚀" step8_1: "Per concludere, vuoi attivare le notifiche push?" @@ -1315,7 +1317,7 @@ _widgets: jobQueue: "Coda di lavoro" serverMetric: "Statistiche server" aiscript: "Console AiScript" - aichan: "indaco (tintura)" + aichan: "Mascotte Ai" userList: "Elenco utenti" _userList: chooseList: "Seleziona una lista" @@ -1420,6 +1422,12 @@ _timelines: local: "Locale" social: "Sociale" global: "Federata" +_play: + viewSource: "Visualizza sorgente" + featured: "Popolari" + title: "Titolo" + script: "Script" + summary: "Descrizione" _pages: newPage: "Crea pagina" editPage: "Modifica pagina" diff --git a/locales/ja-KS.yml b/locales/ja-KS.yml index 6e1bed8269..f8c045db00 100644 --- a/locales/ja-KS.yml +++ b/locales/ja-KS.yml @@ -915,6 +915,8 @@ caption: "キャプション" loggedInAsBot: "Botアカウントでログイン中やで" tools: "ツール" cannotLoad: "読み込めへんで" +like: "ええやん!" +show: "表示" _sensitiveMediaDetection: description: "機械学習を使って自動でセンシティブなメディアを検出して、モデレーションに役立てることができるで。サーバーの負荷が少し増えてまうなあ。" sensitivity: "検出感度やで" @@ -1418,6 +1420,12 @@ _timelines: local: "ローカル" social: "ソーシャル" global: "グローバル" +_play: + viewSource: "ソースを表示" + featured: "人気" + title: "タイトル" + script: "スクリプト" + summary: "説明" _pages: newPage: "ページを作る" editPage: "ページの編集" diff --git a/locales/ko-KR.yml b/locales/ko-KR.yml index 746b59fc2a..d3a4a40b49 100644 --- a/locales/ko-KR.yml +++ b/locales/ko-KR.yml @@ -916,6 +916,10 @@ loggedInAsBot: "봇 계정으로 로그인중" tools: "도구" cannotLoad: "불러오지 못했습니다" numberOfProfileView: "프로필 뷰 수" +like: "좋아요!" +unlike: "좋아요 취소" +numberOfLikes: "좋아요 수" +show: "표시" _sensitiveMediaDetection: description: "기계학습을 통해 자동으로 민감한 미디어를 탐지하여, 모더레이션에 참고할 수 있도록 합니다. 서버의 부하를 약간 증가시킵니다." sensitivity: "탐지 민감도" @@ -1315,6 +1319,7 @@ _widgets: jobQueue: "작업 대기열" serverMetric: "서버 통계" aiscript: "AiScript 콘솔" + aiscriptApp: "AiScript 앱" aichan: "아이" userList: "사용자 목록" _userList: @@ -1420,6 +1425,21 @@ _timelines: local: "로컬" social: "소셜" global: "글로벌" +_play: + new: "Play 만들기" + edit: "Play 수정하기" + created: "Play를 생성했습니다" + updated: "Play를 갱신했습니다" + deleted: "Play를 삭제했습니다" + pageSetting: "Play 설정" + editThisPage: "이 Play를 수정" + viewSource: "소스 보기" + my: "나의 Play" + liked: "좋아요 한 Play" + featured: "인기" + title: "제목" + script: "스크립트" + summary: "설명" _pages: newPage: "페이지 만들기" editPage: "페이지 수정" diff --git a/locales/pl-PL.yml b/locales/pl-PL.yml index f898f52929..712c05bb78 100644 --- a/locales/pl-PL.yml +++ b/locales/pl-PL.yml @@ -866,6 +866,8 @@ pushNotificationNotSupported: "Przeglądarka lub instancja nie obsługuje powiad sendPushNotificationReadMessage: "Usuń powiadomienia push po przeczytaniu powiadomień i wiadomości." sendPushNotificationReadMessageCaption: "Chwilowo pojawi się powiadomienie \"{emptyPushNotificationMessage}\". Może wzrosnąć zużycie baterii urządzenia." loggedInAsBot: "Jesteś obecnie zalogowany/a jako bot" +like: "Polub" +show: "Wyświetlanie" _sensitiveMediaDetection: description: "Zmniejsza wysiłek związany z moderacją serwera dzięki automatycznemu rozpoznawaniu zawartości NSFW za pomocą uczenia maszynowego. To nieznacznie zwiększy obciążenie serwera." setSensitiveFlagAutomatically: "Oznacz jako NSFW" @@ -1313,6 +1315,12 @@ _timelines: local: "Lokalne" social: "Społeczność" global: "Globalna" +_play: + viewSource: "Zobacz źródło" + featured: "Wyróżnione" + title: "Tytuł" + script: "Skrypt" + summary: "Opis" _pages: newPage: "Utwórz stronę" editPage: "Edytuj tę stronę" diff --git a/locales/ro-RO.yml b/locales/ro-RO.yml index 54d382474d..3c85045e5d 100644 --- a/locales/ro-RO.yml +++ b/locales/ro-RO.yml @@ -647,6 +647,7 @@ middle: "Mediu" sent: "Trimite" searchByGoogle: "Caută" file: "Fișiere" +show: "Arată" _email: _follow: title: "te-a urmărit" @@ -690,6 +691,9 @@ _charts: federation: "Federație" _timelines: home: "Acasă" +_play: + script: "Script" + summary: "Descriere" _pages: blocks: image: "Imagini" diff --git a/locales/ru-RU.yml b/locales/ru-RU.yml index 19dfcc3af9..553fa9e811 100644 --- a/locales/ru-RU.yml +++ b/locales/ru-RU.yml @@ -864,6 +864,8 @@ enableAutoSensitiveDescription: "Если доступно, используйт account: "Учётные записи" windowMaximize: "Развернуть" windowRestore: "Восстановить" +like: "Нравится!" +show: "Отображение" _sensitiveMediaDetection: description: "Машинное обучение может быть использовано для автоматического обнаружения чувствительных медиа для модерации. Нагрузка на сервер увеличивается незначительно." setSensitiveFlagAutomatically: "Установить флаг NSFW" @@ -1332,6 +1334,12 @@ _timelines: local: "Местная" social: "Социальная" global: "Всеобщая" +_play: + viewSource: "Просмотр исходника" + featured: "Популярные" + title: "Заголовок" + script: "Скрипт" + summary: "Описание" _pages: newPage: "Создать страницу" editPage: "Править страницу" diff --git a/locales/sk-SK.yml b/locales/sk-SK.yml index 1c57b6c2bb..3abfd8609d 100644 --- a/locales/sk-SK.yml +++ b/locales/sk-SK.yml @@ -911,6 +911,8 @@ windowRestore: "Obnoviť" caption: "Nadpis" tools: "Nástroje" cannotLoad: "Nedá sa načítať." +like: "Páči sa mi" +show: "Zobraziť" _sensitiveMediaDetection: description: "Strojové učenie sa použije na automatickú detekciu citlivých médií na účely ich moderovania. Mierne sa zvýši zaťaženie servera." sensitivity: "Citlivosť detekcie" @@ -1413,6 +1415,12 @@ _timelines: local: "Lokálne" social: "Sociálne" global: "Globálne" +_play: + viewSource: "Ukázať zdroj" + featured: "Význačné" + title: "Nadpis" + script: "Skript" + summary: "Popis" _pages: newPage: "Vytvoriť novú stránku" editPage: "Upraviť túto stránku" diff --git a/locales/sv-SE.yml b/locales/sv-SE.yml index e4e00e341d..8b87e36acd 100644 --- a/locales/sv-SE.yml +++ b/locales/sv-SE.yml @@ -2,6 +2,7 @@ _lang_: "Svenska" headlineMisskey: "Ett nätverk kopplat av noter" introMisskey: "Välkommen! Misskey är en öppen och decentraliserad mikrobloggningstjänst.\nSkapa en \"not\" och dela dina tankar med alla runtomkring dig. 📡\nMed \"reaktioner\" kan du snabbt uttrycka dina känslor kring andras noter.👍\nLåt oss utforska en nya värld!🚀" +poweredByMisskeyDescription: "{name} är en tjänst driven av den öppna källkodsplatformen <b>Misskey</b> (benämns \"Misskey instans\")." monthAndDay: "{day}/{month}" search: "Sök" notifications: "Notifikationer" @@ -12,6 +13,7 @@ fetchingAsApObject: "Hämtar från Fediversum..." ok: "OK" gotIt: "Uppfattat!" cancel: "Avbryt" +noThankYou: "Nej tack" enterUsername: "Ange användarnamn" renotedBy: "Omnoterad av {user}" noNotes: "Inga noteringar" @@ -47,11 +49,13 @@ deleteAndEdit: "Radera och ändra" deleteAndEditConfirm: "Är du säker att du vill radera denna not och ändra den? Du kommer förlora alla reaktioner, omnoteringar och svar till den." addToList: "Lägg till i lista" sendMessage: "Skicka ett meddelande" +copyRSS: "Kopiera RSS" copyUsername: "Kopiera användarnamn" searchUser: "Sök användare" reply: "Svara" loadMore: "Ladda mer" showMore: "Visa mer" +showLess: "Stäng" youGotNewFollower: "följde dig" receiveFollowRequest: "Följarförfrågan mottagen" followRequestAccepted: "Följarförfrågan accepterad" @@ -238,6 +242,17 @@ saved: "Sparad" messaging: "Chatt" upload: "Ladda upp" keepOriginalUploading: "Behåll originalbild" +keepOriginalUploadingDescription: "Sparar den originellt uppladdade bilden i sitt i befintliga skick. Om avstängd, kommer en webbversion bli genererad vid uppladdning." +fromDrive: "Från Drive" +fromUrl: "Från en länk" +uploadFromUrl: "Ladda upp från länk" +uploadFromUrlDescription: "Länken av filen du vill ladda upp" +uploadFromUrlRequested: "Uppladdning begärd" +uploadFromUrlMayTakeTime: "Det kan ta tid tills att uppladdningen blir klar." +explore: "Utforska" +messageRead: "Läs" +noMoreHistory: "Det finns ingen mer historik" +startMessaging: "Starta en chatt" nsfw: "Känsligt innehåll" pinnedNotes: "Fästad not" userList: "Listor" diff --git a/locales/th-TH.yml b/locales/th-TH.yml index 7fc54f5f19..58deeff6f1 100644 --- a/locales/th-TH.yml +++ b/locales/th-TH.yml @@ -916,6 +916,8 @@ loggedInAsBot: "ล็อกอินเป็นบอตอยู่ในข tools: "เครื่องมือ" cannotLoad: "ไม่สามารถโหลดได้" numberOfProfileView: "มุมมองโปรไฟล์" +like: "ชื่นชอบ" +show: "แสดงผล" _sensitiveMediaDetection: description: "ลดความพยายามในการดูแลเซิร์ฟเวอร์ผ่านการจดจำสื่อ NSFW โดยอัตโนมัติผ่านการเรียนรู้ของเครื่อง การทำสิ่งนี้อาจจะเพิ่มภาระบนเซิร์ฟเวอร์เล็กน้อย" sensitivity: "การตรวจจับความไว" @@ -1420,6 +1422,12 @@ _timelines: local: "ในพื้นที่" social: "โซเชี่ยล" global: "ทั่วโลก" +_play: + viewSource: "ดูต้นฉบับ" + featured: "เป็นที่นิยม" + title: "หัวข้อ" + script: "สคริปต์" + summary: "รายละเอียด" _pages: newPage: "สร้างหน้าเพจใหม่" editPage: "แก้ไขหน้าเพจ" diff --git a/locales/uk-UA.yml b/locales/uk-UA.yml index 49a59500ff..352fb354ee 100644 --- a/locales/uk-UA.yml +++ b/locales/uk-UA.yml @@ -892,6 +892,8 @@ unsubscribePushNotification: "Вимкнути push-сповіщення" windowMaximize: "Розгорнути" windowRestore: "Відновити" caption: "Підпис" +like: "Вподобати" +show: "Відображення" _sensitiveMediaDetection: sensitivity: "Чутливість детектування" setSensitiveFlagAutomatically: "Позначити як NSFW" @@ -1348,6 +1350,12 @@ _timelines: local: "Локальна" social: "Соціальна" global: "Глобальна" +_play: + viewSource: "Переглянути вихідний код" + featured: "Популярні" + title: "Заголовок" + script: "Скрипт" + summary: "Опис" _pages: newPage: "Створити сторінку" editPage: "Редагувати сторінку" diff --git a/locales/vi-VN.yml b/locales/vi-VN.yml index b496cfaf22..0070af56f0 100644 --- a/locales/vi-VN.yml +++ b/locales/vi-VN.yml @@ -894,6 +894,8 @@ navbar: "Thanh điều hướng" shuffle: "Xáo trộn" account: "Tài khoản của bạn" move: "Di chuyển" +like: "Thích" +show: "Hiển thị" _sensitiveMediaDetection: description: "Giảm nỗ lực kiểm duyệt máy chủ thông qua việc tự động nhận dạng media NSFW thông qua học máy. Điều này sẽ làm tăng một chút áp lực trên máy chủ." sensitivity: "Phát hiện nhạy cảm" @@ -1393,6 +1395,12 @@ _timelines: local: "Máy chủ này" social: "Xã hội" global: "Liên hợp" +_play: + viewSource: "Xem mã nguồn" + featured: "Nổi tiếng" + title: "Tựa đề" + script: "Kịch bản" + summary: "Mô tả" _pages: newPage: "Tạo Trang mới" editPage: "Sửa Trang này" diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml index 1e147752db..7c3efec86c 100644 --- a/locales/zh-CN.yml +++ b/locales/zh-CN.yml @@ -916,6 +916,10 @@ loggedInAsBot: "以Bot账户登录" tools: "工具" cannotLoad: "无法加载" numberOfProfileView: "个人资料展示次数" +like: "点赞!" +unlike: "取消赞" +numberOfLikes: "点赞数" +show: "显示" _sensitiveMediaDetection: description: "可以使用机器学习技术自动检测敏感媒体,以便进行审核。服务器负载将略微增加。" sensitivity: "检测敏感度" @@ -1049,8 +1053,8 @@ _mfm: shakeDescription: "显示摇晃的动画效果。" twitch: "动画(颤抖)" twitchDescription: "显示强烈颤抖的动画效果。" - spin: "动画(回转)" - spinDescription: "显示回转的动画效果。" + spin: "动画(旋转)" + spinDescription: "显示旋转的动画效果。" x2: "大" x2Description: "以大尺寸显示内容。" x3: "非常大" @@ -1315,6 +1319,7 @@ _widgets: jobQueue: "作业队列" serverMetric: "服务器指标" aiscript: "AiScript控制台" + aiscriptApp: "AiScript App" aichan: "小蓝" userList: "用户列表" _userList: @@ -1420,6 +1425,21 @@ _timelines: local: "本地" social: "社交" global: "全局" +_play: + new: "创建Play" + edit: "编辑Play" + created: "创建了一个Play" + updated: "更新了Play" + deleted: "删除了Play" + pageSetting: "Play设置" + editThisPage: "编辑此Play" + viewSource: "查看源代码" + my: "我的Play" + liked: "点赞的Play" + featured: "热门" + title: "标题" + script: "脚本" + summary: "描述" _pages: newPage: "创建页面" editPage: "编辑页面" diff --git a/locales/zh-TW.yml b/locales/zh-TW.yml index 8f8a8865ac..661325d506 100644 --- a/locales/zh-TW.yml +++ b/locales/zh-TW.yml @@ -916,6 +916,9 @@ loggedInAsBot: "以機器人帳號登入中" tools: "工具" cannotLoad: "無法載入" numberOfProfileView: "個人檔案檢視次數" +like: "讚" +unlike: "收回讚" +show: "檢視" _sensitiveMediaDetection: description: "您可以使用機器學習自動檢測敏感媒體並將其用於審核。 伺服器的負荷會稍微增加。" sensitivity: "檢測敏感度" @@ -1420,6 +1423,12 @@ _timelines: local: "本地" social: "社群" global: "公開" +_play: + viewSource: "檢視原始碼" + featured: "人氣" + title: "標題" + script: "腳本" + summary: "描述" _pages: newPage: "建立頁面" editPage: "編輯頁面" From c5cb7860543d9f108c3bd71bfb0b7b4ac8de1722 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Fri, 6 Jan 2023 13:50:41 +0900 Subject: [PATCH 42/60] :art: --- packages/frontend/src/components/MkUpdated.vue | 4 +++- packages/frontend/src/scripts/confetti.ts | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/frontend/src/components/MkUpdated.vue b/packages/frontend/src/components/MkUpdated.vue index 2ed0c6ea30..1f539b154c 100644 --- a/packages/frontend/src/components/MkUpdated.vue +++ b/packages/frontend/src/components/MkUpdated.vue @@ -26,7 +26,9 @@ const whatIsNew = () => { }; onMounted(() => { - confetti(); + confetti({ + duration: 1000 * 3, + }); }); </script> diff --git a/packages/frontend/src/scripts/confetti.ts b/packages/frontend/src/scripts/confetti.ts index 2a9f393904..d1fbd0ee76 100644 --- a/packages/frontend/src/scripts/confetti.ts +++ b/packages/frontend/src/scripts/confetti.ts @@ -1,8 +1,8 @@ import _confetti from 'canvas-confetti'; import * as os from '@/os'; -export function confetti() { - const duration = 1000 * 5; +export function confetti(options: { duration?: number; } = {}) { + const duration = options.duration ?? 1000 * 5; const animationEnd = Date.now() + duration; const defaults = { startVelocity: 30, spread: 360, ticks: 60, zIndex: os.claimZIndex('high') }; From 46af9515b00466ab0d55e8909c3a690ef63a1f72 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Fri, 6 Jan 2023 13:52:05 +0900 Subject: [PATCH 43/60] fix style --- packages/frontend/src/pages/timeline.vue | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/frontend/src/pages/timeline.vue b/packages/frontend/src/pages/timeline.vue index 5f08d32532..eaeb7d686e 100644 --- a/packages/frontend/src/pages/timeline.vue +++ b/packages/frontend/src/pages/timeline.vue @@ -3,8 +3,8 @@ <template #header><MkPageHeader v-model:tab="src" :actions="headerActions" :tabs="headerTabs" :display-my-avatar="true"/></template> <MkSpacer :content-max="800"> <div ref="rootEl" v-hotkey.global="keymap" class="cmuxhskf"> - <XTutorial v-if="$store.reactiveState.tutorial.value != -1" class="tutorial"/> - <XPostForm v-if="$store.reactiveState.showFixedPostForm.value" class="post-form" fixed/> + <XTutorial v-if="$store.reactiveState.tutorial.value != -1" class="tutorial _panel" style="margin-bottom: var(--margin);"/> + <XPostForm v-if="$store.reactiveState.showFixedPostForm.value" class="post-form _panel" fixed style="margin-bottom: var(--margin);"/> <div v-if="queue > 0" class="new"><button class="_buttonPrimary" @click="top()">{{ i18n.ts.newNoteRecived }}</button></div> <div class="tl"> From a11672d0a5f652021c8934ae2808c21b1b45bb5d Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Fri, 6 Jan 2023 13:52:14 +0900 Subject: [PATCH 44/60] 13.0.0-beta.25 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 098d21e34b..4b591fef6b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "13.0.0-beta.24", + "version": "13.0.0-beta.25", "codename": "indigo", "repository": { "type": "git", From 88c64ece78db199d2f4685180511f2d6b84945d6 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Fri, 6 Jan 2023 14:07:34 +0900 Subject: [PATCH 45/60] Update ClientServerService.ts --- packages/backend/src/server/web/ClientServerService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/src/server/web/ClientServerService.ts b/packages/backend/src/server/web/ClientServerService.ts index 9cb0c4b331..461d1d489b 100644 --- a/packages/backend/src/server/web/ClientServerService.ts +++ b/packages/backend/src/server/web/ClientServerService.ts @@ -252,7 +252,7 @@ export class ClientServerService { reply.header('Content-Security-Policy', 'default-src \'none\'; style-src \'unsafe-inline\''); // || emoji.originalUrl してるのは後方互換性のため(publicUrlはstringなので??はだめ) - return await reply.redirect(301, emoji.publicUrl ?? emoji.originalUrl); + return await reply.redirect(301, emoji.publicUrl || emoji.originalUrl); /* https://github.com/misskey-dev/misskey/pull/9431#issuecomment-1373006446 const url = new URL('/proxy/emoji.webp', this.config.url); From 513cef50a2f4aed99dd7d1a01555c22a66775dba Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Fri, 6 Jan 2023 16:32:34 +0900 Subject: [PATCH 46/60] tweak federation widget --- packages/frontend/src/widgets/federation.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frontend/src/widgets/federation.vue b/packages/frontend/src/widgets/federation.vue index a701ca5673..23f7cd4411 100644 --- a/packages/frontend/src/widgets/federation.vue +++ b/packages/frontend/src/widgets/federation.vue @@ -8,7 +8,7 @@ <div v-for="(instance, i) in instances" :key="instance.id" class="instance"> <img :src="getInstanceIcon(instance)" alt=""/> <div class="body"> - <a class="a" :href="'https://' + instance.host" target="_blank" :title="instance.host">{{ instance.host }}</a> + <MkA class="a" :to="`/instance-info/${instance.host}`" behavior="window" :title="instance.host">{{ instance.host }}</MkA> <p>{{ instance.softwareName || '?' }} {{ instance.softwareVersion }}</p> </div> <MkMiniChart class="chart" :src="charts[i].requests.received"/> From 457670e7306681c3d53fa25edf3235517579abe5 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Fri, 6 Jan 2023 16:58:43 +0900 Subject: [PATCH 47/60] Update MkTime.vue --- packages/frontend/src/components/global/MkTime.vue | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/packages/frontend/src/components/global/MkTime.vue b/packages/frontend/src/components/global/MkTime.vue index 704e6d0de0..66c0bd5135 100644 --- a/packages/frontend/src/components/global/MkTime.vue +++ b/packages/frontend/src/components/global/MkTime.vue @@ -36,23 +36,21 @@ const relative = $computed(() => { i18n.ts._ago.future); }); +let tickId: number; + function tick() { now = new Date(); const ago = (now.getTime() - _time.getTime()) / 1000/*ms*/; const next = ago < 60 ? 10000 : ago < 3600 ? 60000 : 180000; - tickId = window.setTimeout(() => { - window.requestAnimationFrame(tick); - }, next); + tickId = window.setTimeout(tick, next); } -let tickId: number; - if (props.mode === 'relative' || props.mode === 'detail') { - tickId = window.requestAnimationFrame(tick); + tick(); onUnmounted(() => { - window.cancelAnimationFrame(tickId); + window.clearTimeout(tickId); }); } </script> From c14063a921d56a2ff2432bf4678f3b5554c0af74 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Fri, 6 Jan 2023 17:07:32 +0900 Subject: [PATCH 48/60] :art: --- packages/frontend/src/pages/flash/flash.vue | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/frontend/src/pages/flash/flash.vue b/packages/frontend/src/pages/flash/flash.vue index caaa192bbe..9407f92815 100644 --- a/packages/frontend/src/pages/flash/flash.vue +++ b/packages/frontend/src/pages/flash/flash.vue @@ -10,8 +10,8 @@ <MkAsUi v-if="root" :component="root" :components="components"/> </div> <div class="actions _panel"> - <MkButton v-if="flash.isLiked" v-tooltip="i18n.ts.unlike" as-like class="button" rounded primary @click="unlike()"><i class="ti ti-heart-off"></i><span v-if="flash.likedCount > 0" class="count">{{ flash.likedCount }}</span></MkButton> - <MkButton v-else v-tooltip="i18n.ts.like" as-like class="button" rounded @click="like()"><i class="ti ti-heart"></i><span v-if="flash.likedCount > 0" class="count">{{ flash.likedCount }}</span></MkButton> + <MkButton v-if="flash.isLiked" v-tooltip="i18n.ts.unlike" as-like class="button" rounded primary @click="unlike()"><i class="ti ti-heart"></i><span v-if="flash.likedCount > 0" style="margin-left: 6px;">{{ flash.likedCount }}</span></MkButton> + <MkButton v-else v-tooltip="i18n.ts.like" as-like class="button" rounded @click="like()"><i class="ti ti-heart"></i><span v-if="flash.likedCount > 0" style="margin-left: 6px;">{{ flash.likedCount }}</span></MkButton> <MkButton v-tooltip="i18n.ts.shareWithNote" class="button" rounded @click="shareWithNote"><i class="ti ti-repeat ti-fw"></i></MkButton> <MkButton v-tooltip="i18n.ts.share" class="button" rounded @click="share"><i class="ti ti-share ti-fw"></i></MkButton> </div> From 5846198eee4af8b31601b7d7701b3165c16a43f8 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Fri, 6 Jan 2023 17:19:25 +0900 Subject: [PATCH 49/60] enhance(client): provide THIS_ID and THIS_URL vars to aiscript of flash --- packages/frontend/src/pages/flash/flash.vue | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/frontend/src/pages/flash/flash.vue b/packages/frontend/src/pages/flash/flash.vue index 9407f92815..63a1e47038 100644 --- a/packages/frontend/src/pages/flash/flash.vue +++ b/packages/frontend/src/pages/flash/flash.vue @@ -147,6 +147,8 @@ async function run() { ...registerAsUiLib(components, (_root) => { root.value = _root.value; }), + THIS_ID: values.STR(flash.id), + THIS_URL: values.STR(`${url}/play/${flash.id}`), }, { in: (q) => { return new Promise(ok => { From 3e7d8b5f17836f8f482a8d773c15c8cdcdfed0fc Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Fri, 6 Jan 2023 19:59:31 +0900 Subject: [PATCH 50/60] update deps --- package.json | 6 +- packages/backend/package.json | 20 +- packages/frontend/package.json | 20 +- yarn.lock | 418 +++++++++++++++++---------------- 4 files changed, 237 insertions(+), 227 deletions(-) diff --git a/package.json b/package.json index 4b591fef6b..d39db86136 100644 --- a/package.json +++ b/package.json @@ -53,10 +53,10 @@ "devDependencies": { "@types/gulp": "4.0.10", "@types/gulp-rename": "2.0.1", - "@typescript-eslint/eslint-plugin": "5.47.1", - "@typescript-eslint/parser": "5.47.1", + "@typescript-eslint/eslint-plugin": "5.48.0", + "@typescript-eslint/parser": "5.48.0", "cross-env": "7.0.3", - "cypress": "12.2.0", + "cypress": "12.3.0", "eslint": "^8.31.0", "start-server-and-test": "1.15.2", "typescript": "4.9.4" diff --git a/packages/backend/package.json b/packages/backend/package.json index e78b3ef70a..31dac2156d 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -21,9 +21,9 @@ "@tensorflow/tfjs-node": "4.1.0" }, "dependencies": { - "@bull-board/api": "^4.10.0", - "@bull-board/fastify": "^4.10.0", - "@bull-board/ui": "^4.10.0", + "@bull-board/api": "^4.10.1", + "@bull-board/fastify": "^4.10.1", + "@bull-board/ui": "^4.10.1", "@discordapp/twemoji": "14.0.2", "@fastify/accepts": "4.1.0", "@fastify/cookie": "^8.3.0", @@ -38,10 +38,10 @@ "@peertube/http-signature": "1.7.0", "@sinonjs/fake-timers": "10.0.2", "accepts": "^1.3.8", - "ajv": "8.11.2", + "ajv": "8.12.0", "archiver": "5.3.1", "autwh": "0.1.0", - "aws-sdk": "2.1286.0", + "aws-sdk": "2.1289.0", "bcryptjs": "2.4.3", "blurhash": "2.0.4", "bull": "4.10.2", @@ -110,8 +110,8 @@ "stringz": "2.1.0", "summaly": "2.7.0", "syslog-pro": "git+https://github.com/misskey-dev/SyslogPro#0.2.9-misskey.2", - "systeminformation": "5.16.9", - "tinycolor2": "1.5.1", + "systeminformation": "5.17.1", + "tinycolor2": "1.5.2", "tmp": "0.2.1", "tsc-alias": "1.8.2", "tsconfig-paths": "4.1.2", @@ -128,7 +128,7 @@ }, "devDependencies": { "@redocly/openapi-core": "1.0.0-beta.117", - "@swc/core": "1.3.24", + "@swc/core": "1.3.25", "@swc/jest": "0.2.24", "@types/accepts": "1.3.5", "@types/archiver": "5.3.1", @@ -172,8 +172,8 @@ "@types/web-push": "3.3.2", "@types/websocket": "1.0.5", "@types/ws": "8.5.4", - "@typescript-eslint/eslint-plugin": "5.47.1", - "@typescript-eslint/parser": "5.47.1", + "@typescript-eslint/eslint-plugin": "5.48.0", + "@typescript-eslint/parser": "5.48.0", "cross-env": "7.0.3", "eslint": "8.31.0", "eslint-plugin-import": "2.26.0", diff --git a/packages/frontend/package.json b/packages/frontend/package.json index 98230d6ceb..0dec916fa0 100644 --- a/packages/frontend/package.json +++ b/packages/frontend/package.json @@ -21,7 +21,7 @@ "broadcast-channel": "4.19.1", "browser-image-resizer": "git+https://github.com/misskey-dev/browser-image-resizer#v2.2.1-misskey.3", "canvas-confetti": "^1.6.0", - "chart.js": "4.1.1", + "chart.js": "4.1.2", "chartjs-adapter-date-fns": "3.0.0", "chartjs-chart-matrix": "^1.3.0", "chartjs-plugin-gradient": "0.6.1", @@ -36,7 +36,7 @@ "insert-text-at-cursor": "0.3.0", "is-file-animated": "1.0.2", "json5": "2.2.3", - "katex": "0.15.6", + "katex": "0.16.4", "matter-js": "0.18.0", "mfm-js": "0.23.0", "misskey-js": "0.0.14", @@ -45,7 +45,7 @@ "punycode": "2.1.1", "querystring": "0.2.1", "rndstr": "1.0.0", - "rollup": "3.9.0", + "rollup": "3.9.1", "s-age": "1.1.2", "sanitize-html": "^2.8.1", "sass": "1.57.1", @@ -56,14 +56,14 @@ "textarea-caret": "3.1.0", "three": "0.148.0", "throttle-debounce": "5.0.0", - "tinycolor2": "1.5.1", + "tinycolor2": "1.5.2", "tsc-alias": "1.8.2", "tsconfig-paths": "4.1.2", "twemoji-parser": "14.0.0", "typescript": "4.9.4", "uuid": "9.0.0", "vanilla-tilt": "1.8.0", - "vite": "4.0.3", + "vite": "4.0.4", "vue": "3.2.45", "vue-prism-editor": "2.0.0-alpha.2", "vuedraggable": "next" @@ -73,7 +73,7 @@ "@types/glob": "8.0.0", "@types/gulp": "4.0.10", "@types/gulp-rename": "2.0.1", - "@types/katex": "0.14.0", + "@types/katex": "0.16.0", "@types/matter-js": "0.18.2", "@types/punycode": "2.1.0", "@types/sanitize-html": "^2.8.0", @@ -83,16 +83,16 @@ "@types/uuid": "9.0.0", "@types/websocket": "1.0.5", "@types/ws": "8.5.4", - "@typescript-eslint/eslint-plugin": "5.47.1", - "@typescript-eslint/parser": "5.47.1", + "@typescript-eslint/eslint-plugin": "5.48.0", + "@typescript-eslint/parser": "5.48.0", "@vue/runtime-core": "3.2.45", "cross-env": "7.0.3", - "cypress": "12.2.0", + "cypress": "12.3.0", "eslint": "8.31.0", "eslint-plugin-import": "2.26.0", "eslint-plugin-vue": "9.8.0", "start-server-and-test": "1.15.2", "vue-eslint-parser": "^9.1.0", - "vue-tsc": "^1.0.19" + "vue-tsc": "^1.0.22" } } diff --git a/yarn.lock b/yarn.lock index 49ae07dd00..8ebee03676 100644 --- a/yarn.lock +++ b/yarn.lock @@ -417,34 +417,34 @@ __metadata: languageName: node linkType: hard -"@bull-board/api@npm:4.10.0, @bull-board/api@npm:^4.10.0": - version: 4.10.0 - resolution: "@bull-board/api@npm:4.10.0" +"@bull-board/api@npm:4.10.1, @bull-board/api@npm:^4.10.1": + version: 4.10.1 + resolution: "@bull-board/api@npm:4.10.1" dependencies: redis-info: ^3.0.8 - checksum: 886ee7955283056fd40b75bfa63bd174c06bedd8086a3a54804548015ebfc1ba9ff8ec5e76ed2f0a3beeda4b5b5ab32531f35cd77cd6ab5ed7e4978fe426ae84 + checksum: 32c9e88df3dd73f513f8d917df3896a58f182417d75bf43d3446c00cb3c0d17a27ef26a6da40d2304350a614ed1be8d1de30002184a03c6c334c6f84faa85a83 languageName: node linkType: hard -"@bull-board/fastify@npm:^4.10.0": - version: 4.10.0 - resolution: "@bull-board/fastify@npm:4.10.0" +"@bull-board/fastify@npm:^4.10.1": + version: 4.10.1 + resolution: "@bull-board/fastify@npm:4.10.1" dependencies: - "@bull-board/api": 4.10.0 - "@bull-board/ui": 4.10.0 + "@bull-board/api": 4.10.1 + "@bull-board/ui": 4.10.1 "@fastify/static": ^6.4.0 "@fastify/view": ^7.0.0 ejs: ^3.1.8 - checksum: c71d52a49bae6025a1ef4bd3b76bf6a15fb12d8e52f89128c6a9bbbd0e71195d55b3e9b7ccb4608476ac99befe334c6d7fbbea9a7227662047ad7e30ea9174a1 + checksum: e6a0597fc682ff89a31a09d61861f0ec84941538a1ca10f55ffaef1f53b8989c16923725515b1e74112ea169a74fb357765a64e7e75d78d1628edb586958bc0f languageName: node linkType: hard -"@bull-board/ui@npm:4.10.0, @bull-board/ui@npm:^4.10.0": - version: 4.10.0 - resolution: "@bull-board/ui@npm:4.10.0" +"@bull-board/ui@npm:4.10.1, @bull-board/ui@npm:^4.10.1": + version: 4.10.1 + resolution: "@bull-board/ui@npm:4.10.1" dependencies: - "@bull-board/api": 4.10.0 - checksum: 0322523c486ebeae7c31af26e286ce253ed01f09808b4370bde12ba19ac1b0cc888ecc099dc6dba74a63e65b1b14664dd216f1f9880dc893396b24a7a4b1ea90 + "@bull-board/api": 4.10.1 + checksum: b221444ccd836d26a66501b5cd1702710f65ab7a5f0f6d92a0168b91ffde11a75a41ef334ea77a7f0c2b1caa7b5e136fe489242028eca1a5c7ed11ace24365d9 languageName: node linkType: hard @@ -1850,90 +1850,90 @@ __metadata: languageName: node linkType: hard -"@swc/core-darwin-arm64@npm:1.3.24": - version: 1.3.24 - resolution: "@swc/core-darwin-arm64@npm:1.3.24" +"@swc/core-darwin-arm64@npm:1.3.25": + version: 1.3.25 + resolution: "@swc/core-darwin-arm64@npm:1.3.25" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@swc/core-darwin-x64@npm:1.3.24": - version: 1.3.24 - resolution: "@swc/core-darwin-x64@npm:1.3.24" +"@swc/core-darwin-x64@npm:1.3.25": + version: 1.3.25 + resolution: "@swc/core-darwin-x64@npm:1.3.25" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@swc/core-linux-arm-gnueabihf@npm:1.3.24": - version: 1.3.24 - resolution: "@swc/core-linux-arm-gnueabihf@npm:1.3.24" +"@swc/core-linux-arm-gnueabihf@npm:1.3.25": + version: 1.3.25 + resolution: "@swc/core-linux-arm-gnueabihf@npm:1.3.25" conditions: os=linux & cpu=arm languageName: node linkType: hard -"@swc/core-linux-arm64-gnu@npm:1.3.24": - version: 1.3.24 - resolution: "@swc/core-linux-arm64-gnu@npm:1.3.24" +"@swc/core-linux-arm64-gnu@npm:1.3.25": + version: 1.3.25 + resolution: "@swc/core-linux-arm64-gnu@npm:1.3.25" conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"@swc/core-linux-arm64-musl@npm:1.3.24": - version: 1.3.24 - resolution: "@swc/core-linux-arm64-musl@npm:1.3.24" +"@swc/core-linux-arm64-musl@npm:1.3.25": + version: 1.3.25 + resolution: "@swc/core-linux-arm64-musl@npm:1.3.25" conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard -"@swc/core-linux-x64-gnu@npm:1.3.24": - version: 1.3.24 - resolution: "@swc/core-linux-x64-gnu@npm:1.3.24" +"@swc/core-linux-x64-gnu@npm:1.3.25": + version: 1.3.25 + resolution: "@swc/core-linux-x64-gnu@npm:1.3.25" conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard -"@swc/core-linux-x64-musl@npm:1.3.24": - version: 1.3.24 - resolution: "@swc/core-linux-x64-musl@npm:1.3.24" +"@swc/core-linux-x64-musl@npm:1.3.25": + version: 1.3.25 + resolution: "@swc/core-linux-x64-musl@npm:1.3.25" conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard -"@swc/core-win32-arm64-msvc@npm:1.3.24": - version: 1.3.24 - resolution: "@swc/core-win32-arm64-msvc@npm:1.3.24" +"@swc/core-win32-arm64-msvc@npm:1.3.25": + version: 1.3.25 + resolution: "@swc/core-win32-arm64-msvc@npm:1.3.25" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@swc/core-win32-ia32-msvc@npm:1.3.24": - version: 1.3.24 - resolution: "@swc/core-win32-ia32-msvc@npm:1.3.24" +"@swc/core-win32-ia32-msvc@npm:1.3.25": + version: 1.3.25 + resolution: "@swc/core-win32-ia32-msvc@npm:1.3.25" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"@swc/core-win32-x64-msvc@npm:1.3.24": - version: 1.3.24 - resolution: "@swc/core-win32-x64-msvc@npm:1.3.24" +"@swc/core-win32-x64-msvc@npm:1.3.25": + version: 1.3.25 + resolution: "@swc/core-win32-x64-msvc@npm:1.3.25" conditions: os=win32 & cpu=x64 languageName: node linkType: hard -"@swc/core@npm:1.3.24": - version: 1.3.24 - resolution: "@swc/core@npm:1.3.24" +"@swc/core@npm:1.3.25": + version: 1.3.25 + resolution: "@swc/core@npm:1.3.25" dependencies: - "@swc/core-darwin-arm64": 1.3.24 - "@swc/core-darwin-x64": 1.3.24 - "@swc/core-linux-arm-gnueabihf": 1.3.24 - "@swc/core-linux-arm64-gnu": 1.3.24 - "@swc/core-linux-arm64-musl": 1.3.24 - "@swc/core-linux-x64-gnu": 1.3.24 - "@swc/core-linux-x64-musl": 1.3.24 - "@swc/core-win32-arm64-msvc": 1.3.24 - "@swc/core-win32-ia32-msvc": 1.3.24 - "@swc/core-win32-x64-msvc": 1.3.24 + "@swc/core-darwin-arm64": 1.3.25 + "@swc/core-darwin-x64": 1.3.25 + "@swc/core-linux-arm-gnueabihf": 1.3.25 + "@swc/core-linux-arm64-gnu": 1.3.25 + "@swc/core-linux-arm64-musl": 1.3.25 + "@swc/core-linux-x64-gnu": 1.3.25 + "@swc/core-linux-x64-musl": 1.3.25 + "@swc/core-win32-arm64-msvc": 1.3.25 + "@swc/core-win32-ia32-msvc": 1.3.25 + "@swc/core-win32-x64-msvc": 1.3.25 dependenciesMeta: "@swc/core-darwin-arm64": optional: true @@ -1955,9 +1955,7 @@ __metadata: optional: true "@swc/core-win32-x64-msvc": optional: true - bin: - swcx: run_swcx.js - checksum: a27b842be129b83c116f804e63deaa51dbd5d9b77d6260888d549f6408df1dd05aeef20046ceacc9fd7458e6afda6723545249bd77f77086b98bd9bf84738c19 + checksum: de45a7dd871cc9497ad998d6a320d3c95cb9c74fdcb70590ff1f631e75001820d021bbfd5c463e9172afcb5ee47bffaa8fb893230e1329538c9f7afbd5ed45cf languageName: node linkType: hard @@ -2447,10 +2445,10 @@ __metadata: languageName: node linkType: hard -"@types/katex@npm:0.14.0": - version: 0.14.0 - resolution: "@types/katex@npm:0.14.0" - checksum: 330e0d0337ba48c87f5b793965fbad673653789bf6e50dfe8d726a7b0cbefd37195055e31503aae629814aa79447e4f23a4b87ad1ac565c0d9a9d9978836f39b +"@types/katex@npm:0.16.0": + version: 0.16.0 + resolution: "@types/katex@npm:0.16.0" + checksum: f93ceb2496621d18a28252264c0b7f5b0bdf125f9dc92d1adfbd9bf00942cd2918de336fae628d3929e615aaf84b7adb1781711c4e4605664be0827b1013ec14 languageName: node linkType: hard @@ -2886,13 +2884,13 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/eslint-plugin@npm:5.47.1": - version: 5.47.1 - resolution: "@typescript-eslint/eslint-plugin@npm:5.47.1" +"@typescript-eslint/eslint-plugin@npm:5.48.0": + version: 5.48.0 + resolution: "@typescript-eslint/eslint-plugin@npm:5.48.0" dependencies: - "@typescript-eslint/scope-manager": 5.47.1 - "@typescript-eslint/type-utils": 5.47.1 - "@typescript-eslint/utils": 5.47.1 + "@typescript-eslint/scope-manager": 5.48.0 + "@typescript-eslint/type-utils": 5.48.0 + "@typescript-eslint/utils": 5.48.0 debug: ^4.3.4 ignore: ^5.2.0 natural-compare-lite: ^1.4.0 @@ -2905,24 +2903,24 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 59fe719a8fbad14c37b8ce0dd292f6b8066bba370090f5e40eeab03033b97a12df1f1d0963c7070ac8cf4f7f319974fa6747e70932660055d222fa993c239b6a + checksum: cb9cd62fd56670414795e30d30c9fa11ec7ad3a8b0abda48dd17625053a1c26ba1767184b096149bdd0ccb457bec6392306f22211b75f802f4b27366398d16eb languageName: node linkType: hard -"@typescript-eslint/parser@npm:5.47.1": - version: 5.47.1 - resolution: "@typescript-eslint/parser@npm:5.47.1" +"@typescript-eslint/parser@npm:5.48.0": + version: 5.48.0 + resolution: "@typescript-eslint/parser@npm:5.48.0" dependencies: - "@typescript-eslint/scope-manager": 5.47.1 - "@typescript-eslint/types": 5.47.1 - "@typescript-eslint/typescript-estree": 5.47.1 + "@typescript-eslint/scope-manager": 5.48.0 + "@typescript-eslint/types": 5.48.0 + "@typescript-eslint/typescript-estree": 5.48.0 debug: ^4.3.4 peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 peerDependenciesMeta: typescript: optional: true - checksum: 36806686a2c5cc60558c09b13e885861aa21ec6250539d8d3d3c8abb90b321662e57dacec44915c87726a5a0d74187b58a65880a0613024eaeeb7ad0197a345d + checksum: 41d5ce5c8742d286fb083523295a4f186e57bbe4e3da63b6b2de1edbafbcbf6d5225ed3405da2c56e2b0fe1d52bb72babc37508d2ee9b86f6fadad3c4a7950d0 languageName: node linkType: hard @@ -2953,22 +2951,22 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/scope-manager@npm:5.47.1": - version: 5.47.1 - resolution: "@typescript-eslint/scope-manager@npm:5.47.1" +"@typescript-eslint/scope-manager@npm:5.48.0": + version: 5.48.0 + resolution: "@typescript-eslint/scope-manager@npm:5.48.0" dependencies: - "@typescript-eslint/types": 5.47.1 - "@typescript-eslint/visitor-keys": 5.47.1 - checksum: 73e2e2949b6e0122d89cfd44e1d24eda38d774899b834746700a4f1eb096effd1432c953f8be743a3ea3c7fc8fbf6e0882b11ee0f39b7ced6d8abf6a8665f1c8 + "@typescript-eslint/types": 5.48.0 + "@typescript-eslint/visitor-keys": 5.48.0 + checksum: 96c0ce33d613490690ae6f34e4152f05dbddf3196a6dec89afba4a63cd2d828ae23a98262920b521fe461e7655d38f3a01e9e43588c12392a27bf8cb4f8ae201 languageName: node linkType: hard -"@typescript-eslint/type-utils@npm:5.47.1": - version: 5.47.1 - resolution: "@typescript-eslint/type-utils@npm:5.47.1" +"@typescript-eslint/type-utils@npm:5.48.0": + version: 5.48.0 + resolution: "@typescript-eslint/type-utils@npm:5.48.0" dependencies: - "@typescript-eslint/typescript-estree": 5.47.1 - "@typescript-eslint/utils": 5.47.1 + "@typescript-eslint/typescript-estree": 5.48.0 + "@typescript-eslint/utils": 5.48.0 debug: ^4.3.4 tsutils: ^3.21.0 peerDependencies: @@ -2976,7 +2974,7 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 84a1e6c8fd47d419dc66430e31b818774d4c0329a5f355a5a9e8af94378be4c0c24a89916d5cc1380fdbb640693527b906c2e6adee486a2e6786cb5e08bd9eb3 + checksum: 0d57e3bbcaa46e29b588b86b2271341b264f063e71ff5b6d4d35f50f2fe11bd6cdc3c4c95d78493fd17673ecdbd712992b84da1600947ed3bf6ae09de7b99464 languageName: node linkType: hard @@ -2987,10 +2985,10 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/types@npm:5.47.1": - version: 5.47.1 - resolution: "@typescript-eslint/types@npm:5.47.1" - checksum: 9b3df8661862a8927ec29d21d6b5826cae7dd8b4797b5b54d66289d8abcf46081453a5cbaf9cc0a5b6c8249ca381dda61c2623da2a704e47f9d86175639a8cea +"@typescript-eslint/types@npm:5.48.0": + version: 5.48.0 + resolution: "@typescript-eslint/types@npm:5.48.0" + checksum: fa27bd9ec7ec5f256b79a371bb05cfbc26902b6a395f38b0cff0e281633ebd76775ad18e41be1bb156868859287295f6833a2a671da57c6347ac7c6bc08a553b languageName: node linkType: hard @@ -3012,12 +3010,12 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/typescript-estree@npm:5.47.1": - version: 5.47.1 - resolution: "@typescript-eslint/typescript-estree@npm:5.47.1" +"@typescript-eslint/typescript-estree@npm:5.48.0": + version: 5.48.0 + resolution: "@typescript-eslint/typescript-estree@npm:5.48.0" dependencies: - "@typescript-eslint/types": 5.47.1 - "@typescript-eslint/visitor-keys": 5.47.1 + "@typescript-eslint/types": 5.48.0 + "@typescript-eslint/visitor-keys": 5.48.0 debug: ^4.3.4 globby: ^11.1.0 is-glob: ^4.0.3 @@ -3026,25 +3024,25 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 803214a53fd4faf19b6b325dd4e8ddaa5bb1ebb9b52358d26ebeaeb86b431cea5bc09f3b43ca8abfdd3a72fdea667467a1abfda50cbad866696ec5739afae2ac + checksum: 2444632243111e51bc83b56140514cb5978bef4d7151fede0dfcff8808afc1ad335b0c60ca86c2811bcc82273b87e59e2e0360bf1b8c014825ff818a1731d127 languageName: node linkType: hard -"@typescript-eslint/utils@npm:5.47.1": - version: 5.47.1 - resolution: "@typescript-eslint/utils@npm:5.47.1" +"@typescript-eslint/utils@npm:5.48.0": + version: 5.48.0 + resolution: "@typescript-eslint/utils@npm:5.48.0" dependencies: "@types/json-schema": ^7.0.9 "@types/semver": ^7.3.12 - "@typescript-eslint/scope-manager": 5.47.1 - "@typescript-eslint/types": 5.47.1 - "@typescript-eslint/typescript-estree": 5.47.1 + "@typescript-eslint/scope-manager": 5.48.0 + "@typescript-eslint/types": 5.48.0 + "@typescript-eslint/typescript-estree": 5.48.0 eslint-scope: ^5.1.1 eslint-utils: ^3.0.0 semver: ^7.3.7 peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - checksum: 5167d26b8d5579de4d9aae36e18f980b33e01006ecc87cff59b761e15f69234092ac555bcf64a9f18d7c3e68a971df2a37b3912fc523c2586c2ba3f4544cc3d3 + checksum: 53f512ae61f72c2b29f2daf8adbc1f37c400cc71156557f69f0745b62c1265d99917a168245e2ee3d88ae458144818d1bf41ced4a764d7d9534b466b29d362fd languageName: node linkType: hard @@ -3058,13 +3056,13 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/visitor-keys@npm:5.47.1": - version: 5.47.1 - resolution: "@typescript-eslint/visitor-keys@npm:5.47.1" +"@typescript-eslint/visitor-keys@npm:5.48.0": + version: 5.48.0 + resolution: "@typescript-eslint/visitor-keys@npm:5.48.0" dependencies: - "@typescript-eslint/types": 5.47.1 + "@typescript-eslint/types": 5.48.0 eslint-visitor-keys: ^3.3.0 - checksum: b4d1f4daa67e962d22c41325d9dcb6b2efde1caf354a2edb5bf682b92ab8c6205435d0b12f39ce9771955250e26f2a6f03adabb37e62e5aac8225691a59ef153 + checksum: 8d41fb7c93b79df415b43c31da7c9007074d78ab6f16c2d318c23e7974b578ce510f466a9584bd67c526367666974091cb5cfbf6670d29e36fb4ab2e57137515 languageName: node linkType: hard @@ -3085,57 +3083,57 @@ __metadata: languageName: node linkType: hard -"@volar/language-core@npm:1.0.19": - version: 1.0.19 - resolution: "@volar/language-core@npm:1.0.19" +"@volar/language-core@npm:1.0.22": + version: 1.0.22 + resolution: "@volar/language-core@npm:1.0.22" dependencies: - "@volar/source-map": 1.0.19 + "@volar/source-map": 1.0.22 muggle-string: ^0.1.0 - checksum: f49f235e07a2393337c679afaa232d9fd70bf6b6eb0752d574cb99f75c5996af70d3344fe324f3553117b7650c8b3e31331c81fabf8fde6289f2a5f97459e686 + checksum: 809b697a3349ed2ee735c684ee796492b6dab6c7bfe0a82fce2b4c3139b7bb32e7ad9339a1aaf1ce3aa15ccc0826eae27ac77d02dfe73d61a6ce3d20d45fbf92 languageName: node linkType: hard -"@volar/source-map@npm:1.0.19": - version: 1.0.19 - resolution: "@volar/source-map@npm:1.0.19" +"@volar/source-map@npm:1.0.22": + version: 1.0.22 + resolution: "@volar/source-map@npm:1.0.22" dependencies: muggle-string: ^0.1.0 - checksum: a80596333f7ebba33caa2c007ab1b442e5c3ce1d9990364aa10da8e46ebdc736d939b562b5095f268151a02fc45cd09671d8ca8f377b615f3d9d8b876cbcd407 + checksum: 40b33138fd6fe7189a5782c69283d4320e9ab7aa68e9546ce245e4b73035c6d284bb4aa0736080e7c5c7e1532966585cb674fa4c8adba941a6c68f0cbb3905f3 languageName: node linkType: hard -"@volar/typescript@npm:1.0.19": - version: 1.0.19 - resolution: "@volar/typescript@npm:1.0.19" +"@volar/typescript@npm:1.0.22": + version: 1.0.22 + resolution: "@volar/typescript@npm:1.0.22" dependencies: - "@volar/language-core": 1.0.19 - checksum: 1dcce9a6386a205c8708308e664ebb76b846a076a0726a833f6e9416af323e666b6061b28a5a0351e56021be83374af07cb93cb5990e26b6e31a4808a4821847 + "@volar/language-core": 1.0.22 + checksum: d88cb63b32e22ca924853964f2989db894ef4325ee66591be0a5c5e9f2546a8d964b63ee9481d6d6f2e1d2b6d4492a09e9d1e400ef7637583b2f49601087f3e5 languageName: node linkType: hard -"@volar/vue-language-core@npm:1.0.19": - version: 1.0.19 - resolution: "@volar/vue-language-core@npm:1.0.19" +"@volar/vue-language-core@npm:1.0.22": + version: 1.0.22 + resolution: "@volar/vue-language-core@npm:1.0.22" dependencies: - "@volar/language-core": 1.0.19 - "@volar/source-map": 1.0.19 + "@volar/language-core": 1.0.22 + "@volar/source-map": 1.0.22 "@vue/compiler-dom": ^3.2.45 "@vue/compiler-sfc": ^3.2.45 "@vue/reactivity": ^3.2.45 "@vue/shared": ^3.2.45 minimatch: ^5.1.1 vue-template-compiler: ^2.7.14 - checksum: a3efd990c2cfe661b21c3d0b2536dae504a98358d29cc8ae77a93c37468feafc47b326f20e9cb6ac936fb8dc0d9fc602a1ab25d1f8783791090d76fa3e073828 + checksum: e0eeb347af5f99a5c93bd4cd18b3e5f984ecaec04a21e59f319f466c344feecb79bcc64b3389b8c5e338ac6b57aff2dc02dfaba67505910f09775930f815f37e languageName: node linkType: hard -"@volar/vue-typescript@npm:1.0.19": - version: 1.0.19 - resolution: "@volar/vue-typescript@npm:1.0.19" +"@volar/vue-typescript@npm:1.0.22": + version: 1.0.22 + resolution: "@volar/vue-typescript@npm:1.0.22" dependencies: - "@volar/typescript": 1.0.19 - "@volar/vue-language-core": 1.0.19 - checksum: 01ab7eab3e4c237beca10280c1c79b93579791db37f14874c78c9129f6a8e34f8ed794828129e0114d087f59826e34ae0066c1efc4548e1094eb5b2bdbb5c09f + "@volar/typescript": 1.0.22 + "@volar/vue-language-core": 1.0.22 + checksum: fc63ba3d579305e0aa8642b76494b12b56c10f63dfd3a225e4d1d094769f0e7de62494a3a9c2443724ccfd8de49df651a866f305f9953de6ccb9f96f2cf5e51c languageName: node linkType: hard @@ -3411,15 +3409,15 @@ __metadata: languageName: node linkType: hard -"ajv@npm:8.11.2, ajv@npm:^8.0.0, ajv@npm:^8.10.0, ajv@npm:^8.11.0": - version: 8.11.2 - resolution: "ajv@npm:8.11.2" +"ajv@npm:8.12.0": + version: 8.12.0 + resolution: "ajv@npm:8.12.0" dependencies: fast-deep-equal: ^3.1.1 json-schema-traverse: ^1.0.0 require-from-string: ^2.0.2 uri-js: ^4.2.2 - checksum: 53435bf79ee7d1eabba8085962dba4c08d08593334b304db7772887f0b7beebc1b3d957432f7437ed4b60e53b5d966a57b439869890209c50fed610459999e3e + checksum: 4dc13714e316e67537c8b31bc063f99a1d9d9a497eb4bbd55191ac0dcd5e4985bbb71570352ad6f1e76684fb6d790928f96ba3b2d4fd6e10024be9612fe3f001 languageName: node linkType: hard @@ -3435,6 +3433,18 @@ __metadata: languageName: node linkType: hard +"ajv@npm:^8.0.0, ajv@npm:^8.10.0, ajv@npm:^8.11.0": + version: 8.11.2 + resolution: "ajv@npm:8.11.2" + dependencies: + fast-deep-equal: ^3.1.1 + json-schema-traverse: ^1.0.0 + require-from-string: ^2.0.2 + uri-js: ^4.2.2 + checksum: 53435bf79ee7d1eabba8085962dba4c08d08593334b304db7772887f0b7beebc1b3d957432f7437ed4b60e53b5d966a57b439869890209c50fed610459999e3e + languageName: node + linkType: hard + "alphanum-sort@npm:^1.0.1, alphanum-sort@npm:^1.0.2": version: 1.0.2 resolution: "alphanum-sort@npm:1.0.2" @@ -3960,9 +3970,9 @@ __metadata: languageName: node linkType: hard -"aws-sdk@npm:2.1286.0": - version: 2.1286.0 - resolution: "aws-sdk@npm:2.1286.0" +"aws-sdk@npm:2.1289.0": + version: 2.1289.0 + resolution: "aws-sdk@npm:2.1289.0" dependencies: buffer: 4.9.2 events: 1.1.1 @@ -3974,7 +3984,7 @@ __metadata: util: ^0.12.4 uuid: 8.0.0 xml2js: 0.4.19 - checksum: 589494899ee008c2acb5dde44c036501dcfe51bfc537eddb4dcbf6c8a937fb5bd29fb4f2433d75ef080bef79c4ead486d230ba558ab89f54e2cc92b88a500de7 + checksum: e4985b3bf4d06b56711012ed2a05949e51ebc82e3316039268ef54ff774e7b8ba8fa6780a6ca0be222b11b5329b92ca08f27bdc6d410177d9675a45e53a62eb3 languageName: node linkType: hard @@ -4116,9 +4126,9 @@ __metadata: version: 0.0.0-use.local resolution: "backend@workspace:packages/backend" dependencies: - "@bull-board/api": ^4.10.0 - "@bull-board/fastify": ^4.10.0 - "@bull-board/ui": ^4.10.0 + "@bull-board/api": ^4.10.1 + "@bull-board/fastify": ^4.10.1 + "@bull-board/ui": ^4.10.1 "@discordapp/twemoji": 14.0.2 "@fastify/accepts": 4.1.0 "@fastify/cookie": ^8.3.0 @@ -4133,7 +4143,7 @@ __metadata: "@peertube/http-signature": 1.7.0 "@redocly/openapi-core": 1.0.0-beta.117 "@sinonjs/fake-timers": 10.0.2 - "@swc/core": 1.3.24 + "@swc/core": 1.3.25 "@swc/jest": 0.2.24 "@tensorflow/tfjs": ^4.1.0 "@tensorflow/tfjs-node": 4.1.0 @@ -4179,13 +4189,13 @@ __metadata: "@types/web-push": 3.3.2 "@types/websocket": 1.0.5 "@types/ws": 8.5.4 - "@typescript-eslint/eslint-plugin": 5.47.1 - "@typescript-eslint/parser": 5.47.1 + "@typescript-eslint/eslint-plugin": 5.48.0 + "@typescript-eslint/parser": 5.48.0 accepts: ^1.3.8 - ajv: 8.11.2 + ajv: 8.12.0 archiver: 5.3.1 autwh: 0.1.0 - aws-sdk: 2.1286.0 + aws-sdk: 2.1289.0 bcryptjs: 2.4.3 blurhash: 2.0.4 bull: 4.10.2 @@ -4260,8 +4270,8 @@ __metadata: stringz: 2.1.0 summaly: 2.7.0 syslog-pro: "git+https://github.com/misskey-dev/SyslogPro#0.2.9-misskey.2" - systeminformation: 5.16.9 - tinycolor2: 1.5.1 + systeminformation: 5.17.1 + tinycolor2: 1.5.2 tmp: 0.2.1 tsc-alias: 1.8.2 tsconfig-paths: 4.1.2 @@ -4931,12 +4941,12 @@ __metadata: languageName: node linkType: hard -"chart.js@npm:4.1.1": - version: 4.1.1 - resolution: "chart.js@npm:4.1.1" +"chart.js@npm:4.1.2": + version: 4.1.2 + resolution: "chart.js@npm:4.1.2" dependencies: "@kurkle/color": ^0.3.0 - checksum: e195abc7b1271a0d4f616b90ac56465b639bffef8e965d26bf1fd65050011bf359fdc25e43dac1576420c1173dc1430c576fd0f953ae8b62bf257ec571424882 + checksum: 938ac88baf04eb8d784a8d89064696375bc12545421186f5ec9b69ba9b6f7b7da960754f351b59b1df2b1c314dfa848aab44267076888e664d5253bc6a4059dd languageName: node linkType: hard @@ -5788,9 +5798,9 @@ __metadata: languageName: node linkType: hard -"cypress@npm:12.2.0": - version: 12.2.0 - resolution: "cypress@npm:12.2.0" +"cypress@npm:12.3.0": + version: 12.3.0 + resolution: "cypress@npm:12.3.0" dependencies: "@cypress/request": ^2.88.10 "@cypress/xvfb": ^1.2.4 @@ -5836,7 +5846,7 @@ __metadata: yauzl: ^2.10.0 bin: cypress: bin/cypress - checksum: fae721114f2001705cdd59a031d5d41d93092e4bc66561021e7890cd01004ac8e037dbd22e22fe0fac325e8e4420a52e1d75254f4e8b7f7a63e84d3fdb86626c + checksum: 00658996bcca918254348eb42bc03079ccf2d583e5c9c04190267edcbc542d4a22835d7399711c99f7aa7334412104b23cc5a1aa7f02b8b541c12298bf3f63f0 languageName: node linkType: hard @@ -8039,7 +8049,7 @@ __metadata: "@types/glob": 8.0.0 "@types/gulp": 4.0.10 "@types/gulp-rename": 2.0.1 - "@types/katex": 0.14.0 + "@types/katex": 0.16.0 "@types/matter-js": 0.18.2 "@types/punycode": 2.1.0 "@types/sanitize-html": ^2.8.0 @@ -8049,8 +8059,8 @@ __metadata: "@types/uuid": 9.0.0 "@types/websocket": 1.0.5 "@types/ws": 8.5.4 - "@typescript-eslint/eslint-plugin": 5.47.1 - "@typescript-eslint/parser": 5.47.1 + "@typescript-eslint/eslint-plugin": 5.48.0 + "@typescript-eslint/parser": 5.48.0 "@vitejs/plugin-vue": 4.0.0 "@vue/compiler-sfc": 3.2.45 "@vue/runtime-core": 3.2.45 @@ -8060,7 +8070,7 @@ __metadata: broadcast-channel: 4.19.1 browser-image-resizer: "git+https://github.com/misskey-dev/browser-image-resizer#v2.2.1-misskey.3" canvas-confetti: ^1.6.0 - chart.js: 4.1.1 + chart.js: 4.1.2 chartjs-adapter-date-fns: 3.0.0 chartjs-chart-matrix: ^1.3.0 chartjs-plugin-gradient: 0.6.1 @@ -8068,7 +8078,7 @@ __metadata: compare-versions: 5.0.1 cropperjs: 2.0.0-beta.2 cross-env: 7.0.3 - cypress: 12.2.0 + cypress: 12.3.0 date-fns: 2.29.3 escape-regexp: 0.0.1 eslint: 8.31.0 @@ -8080,7 +8090,7 @@ __metadata: insert-text-at-cursor: 0.3.0 is-file-animated: 1.0.2 json5: 2.2.3 - katex: 0.15.6 + katex: 0.16.4 matter-js: 0.18.0 mfm-js: 0.23.0 misskey-js: 0.0.14 @@ -8089,7 +8099,7 @@ __metadata: punycode: 2.1.1 querystring: 0.2.1 rndstr: 1.0.0 - rollup: 3.9.0 + rollup: 3.9.1 s-age: 1.1.2 sanitize-html: ^2.8.1 sass: 1.57.1 @@ -8101,18 +8111,18 @@ __metadata: textarea-caret: 3.1.0 three: 0.148.0 throttle-debounce: 5.0.0 - tinycolor2: 1.5.1 + tinycolor2: 1.5.2 tsc-alias: 1.8.2 tsconfig-paths: 4.1.2 twemoji-parser: 14.0.0 typescript: 4.9.4 uuid: 9.0.0 vanilla-tilt: 1.8.0 - vite: 4.0.3 + vite: 4.0.4 vue: 3.2.45 vue-eslint-parser: ^9.1.0 vue-prism-editor: 2.0.0-alpha.2 - vue-tsc: ^1.0.19 + vue-tsc: ^1.0.22 vuedraggable: next languageName: unknown linkType: soft @@ -10911,14 +10921,14 @@ __metadata: languageName: node linkType: hard -"katex@npm:0.15.6": - version: 0.15.6 - resolution: "katex@npm:0.15.6" +"katex@npm:0.16.4": + version: 0.16.4 + resolution: "katex@npm:0.16.4" dependencies: commander: ^8.0.0 bin: katex: cli.js - checksum: 2da808bbd1d3be27715006cd86767dd3fcce3e317fb3bbd64d407328d2d90de17b5d83062b2cfd0e0d0de32e340efbac214862bc96892a5d1492462e553728d4 + checksum: 94eaf1fbd8365792308527695c09baa6d2d84e2d0170e4af44fb12be3ed403fb3430caff2410117f2f1a9dfdb329f61ab9611d97e645d9c89ee60940698a45cc languageName: node linkType: hard @@ -11829,10 +11839,10 @@ __metadata: dependencies: "@types/gulp": 4.0.10 "@types/gulp-rename": 2.0.1 - "@typescript-eslint/eslint-plugin": 5.47.1 - "@typescript-eslint/parser": 5.47.1 + "@typescript-eslint/eslint-plugin": 5.48.0 + "@typescript-eslint/parser": 5.48.0 cross-env: 7.0.3 - cypress: 12.2.0 + cypress: 12.3.0 eslint: ^8.31.0 execa: 5.1.1 gulp: 4.0.2 @@ -14797,9 +14807,9 @@ __metadata: languageName: node linkType: hard -"rollup@npm:3.9.0": - version: 3.9.0 - resolution: "rollup@npm:3.9.0" +"rollup@npm:3.9.1": + version: 3.9.1 + resolution: "rollup@npm:3.9.1" dependencies: fsevents: ~2.3.2 dependenciesMeta: @@ -14807,7 +14817,7 @@ __metadata: optional: true bin: rollup: dist/bin/rollup - checksum: b0ce4baa8db8ee77ab096a4e066b20fb0719efb9cbd84f230838517d35bf159311487112852cfa687126896b58084c8e6cb9ab222f7559c4b6138ca693d63439 + checksum: 929cfab6b8bb2e20c28d7a4c3909b53729f4a63d8cc14f3b1a217d5f8e550737ee0903124ba58a1f2e7efd45c596e044a968aa379411731d0e76c910621d7d3f languageName: node linkType: hard @@ -15960,12 +15970,12 @@ __metadata: languageName: node linkType: hard -"systeminformation@npm:5.16.9": - version: 5.16.9 - resolution: "systeminformation@npm:5.16.9" +"systeminformation@npm:5.17.1": + version: 5.17.1 + resolution: "systeminformation@npm:5.17.1" bin: systeminformation: lib/cli.js - checksum: e590134391ba727b4988ae828483344ae90ee03551ffb578defd3d6d489a8faa519aee563df18fea5f98c2b9e74ca55d29f1ad096d3a70933672338b9a78f03c + checksum: f13052ef52be86431a045d545f2b641a80e1becd46a0610dacd3f5773ebca0866b0087bf7706c1539e61945007354c4da344479f09a69b6dffe7a4f42d712a90 conditions: (os=darwin | os=linux | os=win32 | os=freebsd | os=openbsd | os=netbsd | os=sunos | os=android) languageName: node linkType: hard @@ -16189,10 +16199,10 @@ __metadata: languageName: node linkType: hard -"tinycolor2@npm:1.5.1": - version: 1.5.1 - resolution: "tinycolor2@npm:1.5.1" - checksum: 0fffbe217217f819e0ef79524fda8813c20dac89b647bfc0f1bc0c7d5a80884dad0d74a414b865d2ce867d76d2303f251f7527eaebfe2838251e195fc4b0287c +"tinycolor2@npm:1.5.2": + version: 1.5.2 + resolution: "tinycolor2@npm:1.5.2" + checksum: 9df1ea9a986b03f1aebb1c1ac17fc561e358493f61b56d73ef2d7207fe7bd74eb71cf745b70487b2b5bb1ce33c9e8af7101088bb0b5fc532eaa1f9d1eda4ef31 languageName: node linkType: hard @@ -17079,9 +17089,9 @@ __metadata: languageName: node linkType: hard -"vite@npm:4.0.3": - version: 4.0.3 - resolution: "vite@npm:4.0.3" +"vite@npm:4.0.4": + version: 4.0.4 + resolution: "vite@npm:4.0.4" dependencies: esbuild: ^0.16.3 fsevents: ~2.3.2 @@ -17113,7 +17123,7 @@ __metadata: optional: true bin: vite: bin/vite.js - checksum: 7df71d955f78cbe0dd8e1eb0851fc75070346a0426b8e3e913bf2e05d1053ca8a50619d550fab4f1ed52c68dfcc2921e6421504e9669fc5ed77497a77f84e33e + checksum: eb86c8cdfe8dcb6644005486b31cb60bc596f2aa683cb194abb5c0afca7c2a5dfdb02bbc7f83f419ad170227ac9c3b898f4406a6d1433105fb61d79d78e47d52 languageName: node linkType: hard @@ -17160,17 +17170,17 @@ __metadata: languageName: node linkType: hard -"vue-tsc@npm:^1.0.19": - version: 1.0.19 - resolution: "vue-tsc@npm:1.0.19" +"vue-tsc@npm:^1.0.22": + version: 1.0.22 + resolution: "vue-tsc@npm:1.0.22" dependencies: - "@volar/vue-language-core": 1.0.19 - "@volar/vue-typescript": 1.0.19 + "@volar/vue-language-core": 1.0.22 + "@volar/vue-typescript": 1.0.22 peerDependencies: typescript: "*" bin: vue-tsc: bin/vue-tsc.js - checksum: 7fe6287d7a12b906f8b7a111cc5b88e3df09f919bbfc04dff1d5c46edcdd925b7650e51fc2de819fbc9ae7bc081807ca27d8f86bd3141886ee9f3fff6cdf417a + checksum: 630a372fef2cd6ce830c540fe81b29c657f1b8caaffb181a84af5c10c9daec9c977860ab30cdfb7c80166c5211d3cbfa912f9212f87a763b20d3e91887b1318c languageName: node linkType: hard From e36e5df635d59dbd238eaa63c787f0ff8c864827 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Fri, 6 Jan 2023 20:00:06 +0900 Subject: [PATCH 51/60] chore(client): tweak charts --- packages/frontend/src/components/MkChart.vue | 10 ++++------ packages/frontend/src/components/MkHeatmap.vue | 5 +++-- .../frontend/src/components/MkRetentionHeatmap.vue | 1 - .../frontend/src/pages/admin/overview.active-users.vue | 6 ------ .../frontend/src/pages/admin/overview.ap-requests.vue | 6 ------ packages/frontend/src/pages/user/activity.heatmap.vue | 5 +++-- packages/frontend/src/pages/user/activity.pv.vue | 10 ++++------ 7 files changed, 14 insertions(+), 29 deletions(-) diff --git a/packages/frontend/src/components/MkChart.vue b/packages/frontend/src/components/MkChart.vue index ea28cfa794..57efda44b1 100644 --- a/packages/frontend/src/components/MkChart.vue +++ b/packages/frontend/src/components/MkChart.vue @@ -16,7 +16,6 @@ */ import { onMounted, ref, shallowRef, watch, PropType, onUnmounted } from 'vue'; import { Chart } from 'chart.js'; -import { enUS } from 'date-fns/locale'; import gradient from 'chartjs-plugin-gradient'; import * as os from '@/os'; import { defaultStore } from '@/store'; @@ -186,6 +185,10 @@ const render = () => { time: { stepSize: 1, unit: props.span === 'day' ? 'month' : 'day', + displayFormats: { + day: 'M/d', + month: 'Y/M', + }, }, grid: { }, @@ -194,11 +197,6 @@ const render = () => { maxRotation: 0, autoSkipPadding: 16, }, - adapters: { - date: { - locale: enUS, - }, - }, min: getDate(props.limit).getTime(), }, y: { diff --git a/packages/frontend/src/components/MkHeatmap.vue b/packages/frontend/src/components/MkHeatmap.vue index ec7f49beee..f222fca9a1 100644 --- a/packages/frontend/src/components/MkHeatmap.vue +++ b/packages/frontend/src/components/MkHeatmap.vue @@ -10,7 +10,6 @@ <script lang="ts" setup> import { markRaw, version as vueVersion, onMounted, onBeforeUnmount, nextTick, watch } from 'vue'; import { Chart } from 'chart.js'; -import { enUS } from 'date-fns/locale'; import tinycolor from 'tinycolor2'; import { MatrixController, MatrixElement } from 'chartjs-chart-matrix'; import * as os from '@/os'; @@ -149,7 +148,9 @@ async function renderChart() { round: 'week', isoWeekday: 0, displayFormats: { - week: 'MMM dd', + day: 'M/d', + month: 'Y/M', + week: 'M/d', }, }, grid: { diff --git a/packages/frontend/src/components/MkRetentionHeatmap.vue b/packages/frontend/src/components/MkRetentionHeatmap.vue index e91b58a4a8..b5b1d5f103 100644 --- a/packages/frontend/src/components/MkRetentionHeatmap.vue +++ b/packages/frontend/src/components/MkRetentionHeatmap.vue @@ -10,7 +10,6 @@ <script lang="ts" setup> import { markRaw, version as vueVersion, onMounted, onBeforeUnmount, nextTick } from 'vue'; import { Chart } from 'chart.js'; -import { enUS } from 'date-fns/locale'; import tinycolor from 'tinycolor2'; import { MatrixController, MatrixElement } from 'chartjs-chart-matrix'; import * as os from '@/os'; diff --git a/packages/frontend/src/pages/admin/overview.active-users.vue b/packages/frontend/src/pages/admin/overview.active-users.vue index 14b09f34e9..e20b8221cf 100644 --- a/packages/frontend/src/pages/admin/overview.active-users.vue +++ b/packages/frontend/src/pages/admin/overview.active-users.vue @@ -10,7 +10,6 @@ <script lang="ts" setup> import { markRaw, version as vueVersion, onMounted, onBeforeUnmount, nextTick } from 'vue'; import { Chart } from 'chart.js'; -import { enUS } from 'date-fns/locale'; import tinycolor from 'tinycolor2'; import gradient from 'chartjs-plugin-gradient'; import * as os from '@/os'; @@ -114,11 +113,6 @@ async function renderChart() { maxRotation: 0, autoSkipPadding: 8, }, - adapters: { - date: { - locale: enUS, - }, - }, }, y: { position: 'left', diff --git a/packages/frontend/src/pages/admin/overview.ap-requests.vue b/packages/frontend/src/pages/admin/overview.ap-requests.vue index 61a0667080..9bc08ef6b6 100644 --- a/packages/frontend/src/pages/admin/overview.ap-requests.vue +++ b/packages/frontend/src/pages/admin/overview.ap-requests.vue @@ -18,7 +18,6 @@ import { onMounted, onUnmounted, ref } from 'vue'; import { Chart } from 'chart.js'; import gradient from 'chartjs-plugin-gradient'; -import { enUS } from 'date-fns/locale'; import tinycolor from 'tinycolor2'; import MkMiniChart from '@/components/MkMiniChart.vue'; import * as os from '@/os'; @@ -135,11 +134,6 @@ onMounted(async () => { maxRotation: 0, autoSkipPadding: 16, }, - adapters: { - date: { - locale: enUS, - }, - }, min: getDate(chartLimit).getTime(), }, y: { diff --git a/packages/frontend/src/pages/user/activity.heatmap.vue b/packages/frontend/src/pages/user/activity.heatmap.vue index 252985d113..7b4c92c020 100644 --- a/packages/frontend/src/pages/user/activity.heatmap.vue +++ b/packages/frontend/src/pages/user/activity.heatmap.vue @@ -10,7 +10,6 @@ <script lang="ts" setup> import { markRaw, version as vueVersion, onMounted, onBeforeUnmount, nextTick, watch } from 'vue'; import { Chart } from 'chart.js'; -import { enUS } from 'date-fns/locale'; import tinycolor from 'tinycolor2'; import * as misskey from 'misskey-js'; import * as os from '@/os'; @@ -138,7 +137,9 @@ async function renderChart() { round: 'week', isoWeekday: 0, displayFormats: { - week: 'MMM dd', + day: 'M/d', + month: 'Y/M', + week: 'M/d', }, }, grid: { diff --git a/packages/frontend/src/pages/user/activity.pv.vue b/packages/frontend/src/pages/user/activity.pv.vue index 7715b66673..1f355cbcb1 100644 --- a/packages/frontend/src/pages/user/activity.pv.vue +++ b/packages/frontend/src/pages/user/activity.pv.vue @@ -11,7 +11,6 @@ <script lang="ts" setup> import { markRaw, version as vueVersion, onMounted, onBeforeUnmount, nextTick } from 'vue'; import { Chart } from 'chart.js'; -import { enUS } from 'date-fns/locale'; import tinycolor from 'tinycolor2'; import * as misskey from 'misskey-js'; import gradient from 'chartjs-plugin-gradient'; @@ -113,6 +112,10 @@ async function renderChart() { time: { stepSize: 1, unit: 'day', + displayFormats: { + day: 'M/d', + month: 'Y/M', + }, }, grid: { display: false, @@ -122,11 +125,6 @@ async function renderChart() { maxRotation: 0, autoSkipPadding: 8, }, - adapters: { - date: { - locale: enUS, - }, - }, }, y: { position: 'left', From 3551ac328eed8928c788301a1056592a7f033800 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Fri, 6 Jan 2023 20:01:32 +0900 Subject: [PATCH 52/60] tweak retention heatmap --- packages/frontend/src/components/MkRetentionHeatmap.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frontend/src/components/MkRetentionHeatmap.vue b/packages/frontend/src/components/MkRetentionHeatmap.vue index b5b1d5f103..b7886d1dc2 100644 --- a/packages/frontend/src/components/MkRetentionHeatmap.vue +++ b/packages/frontend/src/components/MkRetentionHeatmap.vue @@ -39,7 +39,7 @@ async function renderChart() { const wide = rootEl.offsetWidth > 600; const narrow = rootEl.offsetWidth < 400; - const maxDays = wide ? 20 : narrow ? 7 : 14; + const maxDays = wide ? 15 : narrow ? 5 : 10; const raw = await os.api('retention', { }); From 70805e00eb2b2b3af7d7460b374f270a7de627e7 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Fri, 6 Jan 2023 20:19:27 +0900 Subject: [PATCH 53/60] :art: --- .../frontend/src/components/MkChartLegend.vue | 7 ++++ .../src/pages/user/activity.heatmap.vue | 8 ++++- .../frontend/src/pages/user/activity.pv.vue | 36 +++++++++++++++++-- packages/frontend/src/pages/user/activity.vue | 18 +++++----- 4 files changed, 57 insertions(+), 12 deletions(-) diff --git a/packages/frontend/src/components/MkChartLegend.vue b/packages/frontend/src/components/MkChartLegend.vue index 8d2a2be8e8..b950f2836e 100644 --- a/packages/frontend/src/components/MkChartLegend.vue +++ b/packages/frontend/src/components/MkChartLegend.vue @@ -72,4 +72,11 @@ defineExpose({ } } } + +@container (max-width: 500px) { + .root { + font-size: 90%; + gap: 6px; + } +} </style> diff --git a/packages/frontend/src/pages/user/activity.heatmap.vue b/packages/frontend/src/pages/user/activity.heatmap.vue index 7b4c92c020..202201afb5 100644 --- a/packages/frontend/src/pages/user/activity.heatmap.vue +++ b/packages/frontend/src/pages/user/activity.heatmap.vue @@ -1,7 +1,7 @@ <template> <div ref="rootEl"> <MkLoading v-if="fetching"/> - <div v-else> + <div v-else :class="$style.root" class="_panel"> <canvas ref="chartEl"></canvas> </div> </div> @@ -205,3 +205,9 @@ onMounted(async () => { renderChart(); }); </script> + +<style lang="scss" module> +.root { + padding: 20px; +} +</style> diff --git a/packages/frontend/src/pages/user/activity.pv.vue b/packages/frontend/src/pages/user/activity.pv.vue index 1f355cbcb1..d74b641dac 100644 --- a/packages/frontend/src/pages/user/activity.pv.vue +++ b/packages/frontend/src/pages/user/activity.pv.vue @@ -64,6 +64,8 @@ async function renderChart() { const colorUser = '#3498db'; const colorVisitor = '#2ecc71'; + const colorUser2 = '#3498db88'; + const colorVisitor2 = '#2ecc7188'; chartInstance = new Chart(chartEl, { type: 'bar', @@ -78,8 +80,9 @@ async function renderChart() { borderRadius: 4, backgroundColor: colorUser, barPercentage: 0.7, - categoryPercentage: 1, + categoryPercentage: 0.7, fill: true, + stack: 'u', }, { parsing: false, label: 'UPV (visitor)', @@ -90,8 +93,35 @@ async function renderChart() { borderRadius: 4, backgroundColor: colorVisitor, barPercentage: 0.7, - categoryPercentage: 1, + categoryPercentage: 0.7, fill: true, + stack: 'u', + }, { + parsing: false, + label: 'NPV (user)', + data: format(raw.pv.user).slice().reverse(), + pointRadius: 0, + borderWidth: 0, + borderJoinStyle: 'round', + borderRadius: 4, + backgroundColor: colorUser2, + barPercentage: 0.7, + categoryPercentage: 0.7, + fill: true, + stack: 'n', + }, { + parsing: false, + label: 'NPV (visitor)', + data: format(raw.pv.visitor).slice().reverse(), + pointRadius: 0, + borderWidth: 0, + borderJoinStyle: 'round', + borderRadius: 4, + backgroundColor: colorVisitor2, + barPercentage: 0.7, + categoryPercentage: 0.7, + fill: true, + stack: 'n', }], }, options: { @@ -146,7 +176,7 @@ async function renderChart() { plugins: { title: { display: true, - text: 'Unique PV', + text: 'Unique/Natural PV', padding: { left: 0, right: 0, diff --git a/packages/frontend/src/pages/user/activity.vue b/packages/frontend/src/pages/user/activity.vue index f9dce3a9e8..3def414674 100644 --- a/packages/frontend/src/pages/user/activity.vue +++ b/packages/frontend/src/pages/user/activity.vue @@ -1,13 +1,15 @@ <template> <MkSpacer :content-max="700"> - <MkFolder class="item"> - <template #header>Heatmap</template> - <XHeatmap :user="user" :src="'notes'"/> - </MkFolder> - <MkFolder class="item"> - <template #header>PV</template> - <XPv :user="user"/> - </MkFolder> + <div class="_gaps"> + <MkFolder class="item"> + <template #header>Heatmap</template> + <XHeatmap :user="user" :src="'notes'"/> + </MkFolder> + <MkFolder class="item"> + <template #header>PV</template> + <XPv :user="user"/> + </MkFolder> + </div> </MkSpacer> </template> From 4b181a30da9aa53c68cbc151fed42027c40a18b4 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Fri, 6 Jan 2023 20:25:08 +0900 Subject: [PATCH 54/60] tweak confetti --- packages/frontend/src/pages/user/home.vue | 4 +++- packages/frontend/src/scripts/confetti.ts | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/frontend/src/pages/user/home.vue b/packages/frontend/src/pages/user/home.vue index 8c5c4c309a..53ae6a2c53 100644 --- a/packages/frontend/src/pages/user/home.vue +++ b/packages/frontend/src/pages/user/home.vue @@ -188,7 +188,9 @@ onMounted(() => { const bm = parseInt(props.user.birthday.split('-')[1]); const bd = parseInt(props.user.birthday.split('-')[2]); if (m === bm && d === bd) { - confetti(); + confetti({ + duration: 1000 * 4 + }); } } }); diff --git a/packages/frontend/src/scripts/confetti.ts b/packages/frontend/src/scripts/confetti.ts index d1fbd0ee76..9e03acbf8d 100644 --- a/packages/frontend/src/scripts/confetti.ts +++ b/packages/frontend/src/scripts/confetti.ts @@ -2,7 +2,7 @@ import _confetti from 'canvas-confetti'; import * as os from '@/os'; export function confetti(options: { duration?: number; } = {}) { - const duration = options.duration ?? 1000 * 5; + const duration = options.duration ?? 1000 * 4; const animationEnd = Date.now() + duration; const defaults = { startVelocity: 30, spread: 360, ticks: 60, zIndex: os.claimZIndex('high') }; From a42b03c1542d362ec11b7bd01bb6e458ef7160e1 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Fri, 6 Jan 2023 20:29:48 +0900 Subject: [PATCH 55/60] 13.0.0-beta.26 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d39db86136..1ee91c9fc1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "13.0.0-beta.25", + "version": "13.0.0-beta.26", "codename": "indigo", "repository": { "type": "git", From 5ec89ea0c3674a3b0b34f73cd576ee011530c9f0 Mon Sep 17 00:00:00 2001 From: tamaina <tamaina@hotmail.co.jp> Date: Fri, 6 Jan 2023 22:34:50 +0900 Subject: [PATCH 56/60] =?UTF-8?q?=E3=82=AB=E3=82=B9=E3=82=BF=E3=83=A0?= =?UTF-8?q?=E7=B5=B5=E6=96=87=E5=AD=97=E3=81=AB=E3=83=97=E3=83=AD=E3=82=AD?= =?UTF-8?q?=E3=82=B7=E3=82=92=E5=BE=A9=E6=B4=BB=20(#9481)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * wip * Revert "Update ClientServerService.ts" This reverts commit 88c64ece78db199d2f4685180511f2d6b84945d6. * Revert "disable custom emoji proxy temporary" This reverts commit 495d513efdb8b2a996287fd54ae5014832352616. * :v: --- packages/backend/src/misc/is-mime-image.ts | 1 + .../src/server/MediaProxyServerService.ts | 21 +++++++++++++------ .../src/server/web/ClientServerService.ts | 5 ----- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/packages/backend/src/misc/is-mime-image.ts b/packages/backend/src/misc/is-mime-image.ts index 168a9a7af6..acf5c1ede3 100644 --- a/packages/backend/src/misc/is-mime-image.ts +++ b/packages/backend/src/misc/is-mime-image.ts @@ -3,6 +3,7 @@ import { FILE_TYPE_BROWSERSAFE } from '@/const.js'; const dictionary = { 'safe-file': FILE_TYPE_BROWSERSAFE, 'sharp-convertible-image': ['image/jpeg', 'image/png', 'image/gif', 'image/apng', 'image/vnd.mozilla.apng', 'image/webp', 'image/avif', 'image/svg+xml'], + 'sharp-animation-convertible-image': ['image/jpeg', 'image/png', 'image/gif', 'image/webp', 'image/avif', 'image/svg+xml'], }; export const isMimeImage = (mime: string, type: keyof typeof dictionary): boolean => dictionary[type].includes(mime); diff --git a/packages/backend/src/server/MediaProxyServerService.ts b/packages/backend/src/server/MediaProxyServerService.ts index 4491a17545..5b76f15020 100644 --- a/packages/backend/src/server/MediaProxyServerService.ts +++ b/packages/backend/src/server/MediaProxyServerService.ts @@ -79,10 +79,18 @@ export class MediaProxyServerService { const { mime, ext } = await this.fileInfoService.detectType(path); const isConvertibleImage = isMimeImage(mime, 'sharp-convertible-image'); + const isAnimationConvertibleImage = isMimeImage(mime, 'sharp-animation-convertible-image'); let image: IImage; if ('emoji' in request.query && isConvertibleImage) { - const data = await sharp(path, { animated: !('static' in request.query) }) + if (!isAnimationConvertibleImage && !('static' in request.query)) { + image = { + data: fs.readFileSync(path), + ext, + type: mime, + }; + } else { + const data = await sharp(path, { animated: !('static' in request.query) }) .resize({ height: 128, withoutEnlargement: true, @@ -90,11 +98,12 @@ export class MediaProxyServerService { .webp(webpDefault) .toBuffer(); - image = { - data, - ext: 'webp', - type: 'image/webp', - }; + image = { + data, + ext: 'webp', + type: 'image/webp', + }; + } } else if ('static' in request.query && isConvertibleImage) { image = await this.imageProcessingService.convertToWebp(path, 498, 280); } else if ('preview' in request.query && isConvertibleImage) { diff --git a/packages/backend/src/server/web/ClientServerService.ts b/packages/backend/src/server/web/ClientServerService.ts index 461d1d489b..263b15cc5e 100644 --- a/packages/backend/src/server/web/ClientServerService.ts +++ b/packages/backend/src/server/web/ClientServerService.ts @@ -251,10 +251,6 @@ export class ClientServerService { reply.header('Content-Security-Policy', 'default-src \'none\'; style-src \'unsafe-inline\''); - // || emoji.originalUrl してるのは後方互換性のため(publicUrlはstringなので??はだめ) - return await reply.redirect(301, emoji.publicUrl || emoji.originalUrl); - - /* https://github.com/misskey-dev/misskey/pull/9431#issuecomment-1373006446 const url = new URL('/proxy/emoji.webp', this.config.url); // || emoji.originalUrl してるのは後方互換性のため(publicUrlはstringなので??はだめ) url.searchParams.set('url', emoji.publicUrl || emoji.originalUrl); @@ -265,7 +261,6 @@ export class ClientServerService { 301, url.toString(), ); - */ }); fastify.get<{ Params: { path: string } }>('/fluent-emoji/:path(.*)', async (request, reply) => { From 0f3399753d4f06e37fdbe1dcfb3fe8f29bd0dc36 Mon Sep 17 00:00:00 2001 From: tamaina <tamaina@hotmail.co.jp> Date: Fri, 6 Jan 2023 14:00:54 +0000 Subject: [PATCH 57/60] chore: remove ` Search` from the name of OpenSearch --- packages/backend/src/server/web/ClientServerService.ts | 2 +- packages/backend/src/server/web/views/base.pug | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/backend/src/server/web/ClientServerService.ts b/packages/backend/src/server/web/ClientServerService.ts index 461d1d489b..326d409953 100644 --- a/packages/backend/src/server/web/ClientServerService.ts +++ b/packages/backend/src/server/web/ClientServerService.ts @@ -362,7 +362,7 @@ export class ClientServerService { const name = meta.name || 'Misskey'; let content = ''; content += '<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/" xmlns:moz="http://www.mozilla.org/2006/browser/search/">'; - content += `<ShortName>${name} Search</ShortName>`; + content += `<ShortName>${name}</ShortName>`; content += `<Description>${name} Search</Description>`; content += '<InputEncoding>UTF-8</InputEncoding>'; content += `<Image width="16" height="16" type="image/x-icon">${this.config.url}/favicon.ico</Image>`; diff --git a/packages/backend/src/server/web/views/base.pug b/packages/backend/src/server/web/views/base.pug index b472cff899..b27bbcbce0 100644 --- a/packages/backend/src/server/web/views/base.pug +++ b/packages/backend/src/server/web/views/base.pug @@ -31,7 +31,7 @@ html link(rel='icon' href= icon || '/favicon.ico') link(rel='apple-touch-icon' href= icon || '/apple-touch-icon.png') link(rel='manifest' href='/manifest.json') - link(rel='search' type='application/opensearchdescription+xml' title=((title || "Misskey") + " Search") href=`${url}/opensearch.xml`) + link(rel='search' type='application/opensearchdescription+xml' title=(title || "Misskey") href=`${url}/opensearch.xml`) link(rel='prefetch' href='https://xn--931a.moe/assets/info.jpg') link(rel='prefetch' href='https://xn--931a.moe/assets/not-found.jpg') link(rel='prefetch' href='https://xn--931a.moe/assets/error.jpg') From 91503405b4e58cb9d39c0441d86ff540a27931e6 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sat, 7 Jan 2023 10:13:02 +0900 Subject: [PATCH 58/60] refactor(client): typed localStorage --- packages/frontend/src/account.ts | 9 +++--- .../src/components/MkAutocomplete.vue | 3 +- packages/frontend/src/components/MkFolder.vue | 7 +++-- .../frontend/src/components/MkPostForm.vue | 17 +++++----- packages/frontend/src/config.ts | 10 +++--- packages/frontend/src/init.ts | 24 ++++++++------ packages/frontend/src/instance.ts | 5 +-- packages/frontend/src/local-storage.ts | 31 +++++++++++++++++++ packages/frontend/src/navbar.ts | 7 +++-- packages/frontend/src/pages/_error_.vue | 3 +- .../pages/messaging/messaging-room.form.vue | 11 ++++--- packages/frontend/src/pages/scratchpad.vue | 5 +-- .../src/pages/settings/custom-css.vue | 5 +-- .../frontend/src/pages/settings/general.vue | 19 ++++++------ .../frontend/src/pages/settings/index.vue | 5 +-- .../pages/settings/preferences-backups.vue | 19 ++++++------ .../frontend/src/pages/settings/theme.vue | 7 +++-- packages/frontend/src/scripts/aiscript/api.ts | 5 +-- .../frontend/src/scripts/get-note-menu.ts | 3 +- packages/frontend/src/scripts/idb-proxy.ts | 6 ++-- packages/frontend/src/scripts/theme.ts | 5 +-- packages/frontend/src/store.ts | 17 +++++++--- packages/frontend/src/theme-store.ts | 14 ++++++--- packages/frontend/src/ui/classic.vue | 5 +-- packages/frontend/src/ui/universal.vue | 3 +- 25 files changed, 157 insertions(+), 88 deletions(-) create mode 100644 packages/frontend/src/local-storage.ts diff --git a/packages/frontend/src/account.ts b/packages/frontend/src/account.ts index 0e991cdfb5..93916ccf2f 100644 --- a/packages/frontend/src/account.ts +++ b/packages/frontend/src/account.ts @@ -6,12 +6,13 @@ import { del, get, set } from '@/scripts/idb-proxy'; import { apiUrl } from '@/config'; import { waiting, api, popup, popupMenu, success, alert } from '@/os'; import { unisonReload, reloadChannel } from '@/scripts/unison-reload'; +import { miLocalStorage } from './local-storage'; // TODO: 他のタブと永続化されたstateを同期 type Account = misskey.entities.MeDetailed; -const accountData = localStorage.getItem('account'); +const accountData = miLocalStorage.getItem('account'); // TODO: 外部からはreadonlyに export const $i = accountData ? reactive(JSON.parse(accountData) as Account) : null; @@ -21,7 +22,7 @@ export const iAmAdmin = $i != null && $i.isAdmin; export async function signout() { waiting(); - localStorage.removeItem('account'); + miLocalStorage.removeItem('account'); await removeAccount($i.id); @@ -119,7 +120,7 @@ export function updateAccount(accountData) { for (const [key, value] of Object.entries(accountData)) { $i[key] = value; } - localStorage.setItem('account', JSON.stringify($i)); + miLocalStorage.setItem('account', JSON.stringify($i)); } export function refreshAccount() { @@ -130,7 +131,7 @@ export async function login(token: Account['token'], redirect?: string) { waiting(); if (_DEV_) console.log('logging as token ', token); const me = await fetchAccount(token); - localStorage.setItem('account', JSON.stringify(me)); + miLocalStorage.setItem('account', JSON.stringify(me)); document.cookie = `token=${token}; path=/; max-age=31536000`; // bull dashboardの認証とかで使う await addAccount(me.id, token); diff --git a/packages/frontend/src/components/MkAutocomplete.vue b/packages/frontend/src/components/MkAutocomplete.vue index 08e2c29de2..8ed60bc5dc 100644 --- a/packages/frontend/src/components/MkAutocomplete.vue +++ b/packages/frontend/src/components/MkAutocomplete.vue @@ -46,6 +46,7 @@ import { defaultStore } from '@/store'; import { emojilist } from '@/scripts/emojilist'; import { instance } from '@/instance'; import { i18n } from '@/i18n'; +import { miLocalStorage } from '@/local-storage'; type EmojiDef = { emoji: string; @@ -208,7 +209,7 @@ function exec() { } } else if (props.type === 'hashtag') { if (!props.q || props.q === '') { - hashtags.value = JSON.parse(localStorage.getItem('hashtags') || '[]'); + hashtags.value = JSON.parse(miLocalStorage.getItem('hashtags') || '[]'); fetching.value = false; } else { const cacheKey = `autocomplete:hashtag:${props.q}`; diff --git a/packages/frontend/src/components/MkFolder.vue b/packages/frontend/src/components/MkFolder.vue index 5a406c8635..dc10c7d3f3 100644 --- a/packages/frontend/src/components/MkFolder.vue +++ b/packages/frontend/src/components/MkFolder.vue @@ -25,8 +25,9 @@ <script lang="ts"> import { defineComponent } from 'vue'; import tinycolor from 'tinycolor2'; +import { miLocalStorage } from '@/local-storage'; -const localStoragePrefix = 'ui:folder:'; +const miLocalStoragePrefix = 'ui:folder:' as const; export default defineComponent({ props: { @@ -44,13 +45,13 @@ export default defineComponent({ data() { return { bg: null, - showBody: (this.persistKey && localStorage.getItem(localStoragePrefix + this.persistKey)) ? localStorage.getItem(localStoragePrefix + this.persistKey) === 't' : this.expanded, + showBody: (this.persistKey && miLocalStorage.getItem(`${miLocalStoragePrefix}${this.persistKey}`)) ? (miLocalStorage.getItem(`${miLocalStoragePrefix}${this.persistKey}`) === 't') : this.expanded, }; }, watch: { showBody() { if (this.persistKey) { - localStorage.setItem(localStoragePrefix + this.persistKey, this.showBody ? 't' : 'f'); + miLocalStorage.setItem(`${miLocalStoragePrefix}${this.persistKey}`, this.showBody ? 't' : 'f'); } }, }, diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue index 883ad9f14f..ff3b7ec1f5 100644 --- a/packages/frontend/src/components/MkPostForm.vue +++ b/packages/frontend/src/components/MkPostForm.vue @@ -98,6 +98,7 @@ import { $i, getAccounts, openAccountMenu as openAccountMenu_ } from '@/account' import { uploadFile } from '@/scripts/upload'; import { deepClone } from '@/scripts/clone'; import MkRippleEffect from '@/components/MkRippleEffect.vue'; +import { miLocalStorage } from '@/local-storage'; const modal = inject('modal'); @@ -156,7 +157,7 @@ let autocomplete = $ref(null); let draghover = $ref(false); let quoteId = $ref(null); let hasNotSpecifiedMentions = $ref(false); -let recentHashtags = $ref(JSON.parse(localStorage.getItem('hashtags') || '[]')); +let recentHashtags = $ref(JSON.parse(miLocalStorage.getItem('hashtags') || '[]')); let imeText = $ref(''); const typing = throttle(3000, () => { @@ -543,7 +544,7 @@ function onDrop(ev): void { } function saveDraft() { - const draftData = JSON.parse(localStorage.getItem('drafts') || '{}'); + const draftData = JSON.parse(miLocalStorage.getItem('drafts') || '{}'); draftData[draftKey] = { updatedAt: new Date(), @@ -558,15 +559,15 @@ function saveDraft() { }, }; - localStorage.setItem('drafts', JSON.stringify(draftData)); + miLocalStorage.setItem('drafts', JSON.stringify(draftData)); } function deleteDraft() { - const draftData = JSON.parse(localStorage.getItem('drafts') ?? '{}'); + const draftData = JSON.parse(miLocalStorage.getItem('drafts') ?? '{}'); delete draftData[draftKey]; - localStorage.setItem('drafts', JSON.stringify(draftData)); + miLocalStorage.setItem('drafts', JSON.stringify(draftData)); } async function post(ev?: MouseEvent) { @@ -622,8 +623,8 @@ async function post(ev?: MouseEvent) { emit('posted'); if (postData.text && postData.text !== '') { const hashtags_ = mfm.parse(postData.text).filter(x => x.type === 'hashtag').map(x => x.props.hashtag); - const history = JSON.parse(localStorage.getItem('hashtags') || '[]') as string[]; - localStorage.setItem('hashtags', JSON.stringify(unique(hashtags_.concat(history)))); + const history = JSON.parse(miLocalStorage.getItem('hashtags') || '[]') as string[]; + miLocalStorage.setItem('hashtags', JSON.stringify(unique(hashtags_.concat(history)))); } posting = false; postAccount = null; @@ -698,7 +699,7 @@ onMounted(() => { nextTick(() => { // 書きかけの投稿を復元 if (!props.instant && !props.mention && !props.specified) { - const draft = JSON.parse(localStorage.getItem('drafts') || '{}')[draftKey]; + const draft = JSON.parse(miLocalStorage.getItem('drafts') || '{}')[draftKey]; if (draft) { text = draft.data.text; useCw = draft.data.useCw; diff --git a/packages/frontend/src/config.ts b/packages/frontend/src/config.ts index f2022b0f02..4b084d365b 100644 --- a/packages/frontend/src/config.ts +++ b/packages/frontend/src/config.ts @@ -1,3 +1,5 @@ +import { miLocalStorage } from "./local-storage"; + const address = new URL(location.href); const siteName = (document.querySelector('meta[property="og:site_name"]') as HTMLMetaElement)?.content; @@ -6,10 +8,10 @@ export const hostname = address.hostname; export const url = address.origin; export const apiUrl = url + '/api'; export const wsUrl = url.replace('http://', 'ws://').replace('https://', 'wss://') + '/streaming'; -export const lang = localStorage.getItem('lang'); +export const lang = miLocalStorage.getItem('lang'); export const langs = _LANGS_; -export const locale = JSON.parse(localStorage.getItem('locale')); +export const locale = JSON.parse(miLocalStorage.getItem('locale')); export const version = _VERSION_; export const instanceName = siteName === 'Misskey' ? host : siteName; -export const ui = localStorage.getItem('ui'); -export const debug = localStorage.getItem('debug') === 'true'; +export const ui = miLocalStorage.getItem('ui'); +export const debug = miLocalStorage.getItem('debug') === 'true'; diff --git a/packages/frontend/src/init.ts b/packages/frontend/src/init.ts index 45ade64127..bd515f47ea 100644 --- a/packages/frontend/src/init.ts +++ b/packages/frontend/src/init.ts @@ -9,9 +9,12 @@ import '@/style.scss'; //#region account indexedDB migration import { set } from '@/scripts/idb-proxy'; -if (localStorage.getItem('accounts') != null) { - set('accounts', JSON.parse(localStorage.getItem('accounts'))); - localStorage.removeItem('accounts'); +{ + const accounts = miLocalStorage.getItem('accounts'); + if (accounts) { + set('accounts', JSON.parse(accounts)); + miLocalStorage.removeItem('accounts'); + } } //#endregion @@ -40,6 +43,7 @@ import { reloadChannel } from '@/scripts/unison-reload'; import { reactionPicker } from '@/scripts/reaction-picker'; import { getUrlWithoutLoginId } from '@/scripts/login-id'; import { getAccountFromId } from '@/scripts/get-account-from-id'; +import { miLocalStorage } from './local-storage'; (async () => { console.info(`Misskey v${version}`); @@ -154,7 +158,7 @@ import { getAccountFromId } from '@/scripts/get-account-from-id'; const fetchInstanceMetaPromise = fetchInstance(); fetchInstanceMetaPromise.then(() => { - localStorage.setItem('v', instance.version); + miLocalStorage.setItem('v', instance.version); // Init service worker initializeSw(); @@ -223,12 +227,12 @@ import { getAccountFromId } from '@/scripts/get-account-from-id'; } // クライアントが更新されたか? - const lastVersion = localStorage.getItem('lastVersion'); + const lastVersion = miLocalStorage.getItem('lastVersion'); if (lastVersion !== version) { - localStorage.setItem('lastVersion', version); + miLocalStorage.setItem('lastVersion', version); // テーマリビルドするため - localStorage.removeItem('theme'); + miLocalStorage.removeItem('theme'); try { // 変なバージョン文字列来るとcompareVersionsでエラーになるため if (lastVersion != null && compareVersions(version, lastVersion) === 1) { @@ -244,7 +248,7 @@ import { getAccountFromId } from '@/scripts/get-account-from-id'; // NOTE: この処理は必ず↑のクライアント更新時処理より後に来ること(テーマ再構築のため) watch(defaultStore.reactiveState.darkMode, (darkMode) => { applyTheme(darkMode ? ColdDeviceStorage.get('darkTheme') : ColdDeviceStorage.get('lightTheme')); - }, { immediate: localStorage.theme == null }); + }, { immediate: miLocalStorage.getItem('theme') == null }); const darkTheme = computed(ColdDeviceStorage.makeGetterSetter('darkTheme')); const lightTheme = computed(ColdDeviceStorage.makeGetterSetter('lightTheme')); @@ -341,7 +345,7 @@ import { getAccountFromId } from '@/scripts/get-account-from-id'; }); } - const lastUsed = localStorage.getItem('lastUsed'); + const lastUsed = miLocalStorage.getItem('lastUsed'); if (lastUsed) { const lastUsedDate = parseInt(lastUsed, 10); // 二時間以上前なら @@ -351,7 +355,7 @@ import { getAccountFromId } from '@/scripts/get-account-from-id'; })); } } - localStorage.setItem('lastUsed', Date.now().toString()); + miLocalStorage.setItem('lastUsed', Date.now().toString()); if ('Notification' in window) { // 許可を得ていなかったらリクエスト diff --git a/packages/frontend/src/instance.ts b/packages/frontend/src/instance.ts index 51464f32fb..82d3e7aea2 100644 --- a/packages/frontend/src/instance.ts +++ b/packages/frontend/src/instance.ts @@ -1,10 +1,11 @@ import { computed, reactive } from 'vue'; import * as Misskey from 'misskey-js'; import { api } from './os'; +import { miLocalStorage } from './local-storage'; // TODO: 他のタブと永続化されたstateを同期 -const instanceData = localStorage.getItem('instance'); +const instanceData = miLocalStorage.getItem('instance'); // TODO: instanceをリアクティブにするかは再考の余地あり @@ -21,7 +22,7 @@ export async function fetchInstance() { instance[k] = v; } - localStorage.setItem('instance', JSON.stringify(instance)); + miLocalStorage.setItem('instance', JSON.stringify(instance)); } export const emojiCategories = computed(() => { diff --git a/packages/frontend/src/local-storage.ts b/packages/frontend/src/local-storage.ts new file mode 100644 index 0000000000..5286a6f3a7 --- /dev/null +++ b/packages/frontend/src/local-storage.ts @@ -0,0 +1,31 @@ +type Keys = + 'v' | + 'lastVersion' | + 'instance' | + 'account' | + 'accounts' | + 'lastUsed' | + 'lang' | + 'drafts' | + 'hashtags' | + 'wallpaper' | + 'theme' | + 'colorSchema' | + 'useSystemFont' | + 'fontSize' | + 'ui' | + 'locale' | + 'theme' | + 'customCss' | + 'message_drafts' | + 'scratchpad' | + `miux:${string}` | + `ui:folder:${string}` | + `themes:${string}` | + `aiscript:${string}`; + +export const miLocalStorage = { + getItem: (key: Keys) => window.localStorage.getItem(key), + setItem: (key: Keys, value: string) => window.localStorage.setItem(key, value), + removeItem: (key: Keys) => window.localStorage.removeItem(key), +}; diff --git a/packages/frontend/src/navbar.ts b/packages/frontend/src/navbar.ts index efc0abfc6e..9ee78741dc 100644 --- a/packages/frontend/src/navbar.ts +++ b/packages/frontend/src/navbar.ts @@ -5,6 +5,7 @@ import * as os from '@/os'; import { i18n } from '@/i18n'; import { ui } from '@/config'; import { unisonReload } from '@/scripts/unison-reload'; +import { miLocalStorage } from './local-storage'; export const navbarItemDef = reactive({ notifications: { @@ -110,21 +111,21 @@ export const navbarItemDef = reactive({ text: i18n.ts.default, active: ui === 'default' || ui === null, action: () => { - localStorage.setItem('ui', 'default'); + miLocalStorage.setItem('ui', 'default'); unisonReload(); }, }, { text: i18n.ts.deck, active: ui === 'deck', action: () => { - localStorage.setItem('ui', 'deck'); + miLocalStorage.setItem('ui', 'deck'); unisonReload(); }, }, { text: i18n.ts.classic, active: ui === 'classic', action: () => { - localStorage.setItem('ui', 'classic'); + miLocalStorage.setItem('ui', 'classic'); unisonReload(); }, }], ev.currentTarget ?? ev.target); diff --git a/packages/frontend/src/pages/_error_.vue b/packages/frontend/src/pages/_error_.vue index da2889ba27..5001b5a8b4 100644 --- a/packages/frontend/src/pages/_error_.vue +++ b/packages/frontend/src/pages/_error_.vue @@ -26,6 +26,7 @@ import * as os from '@/os'; import { unisonReload } from '@/scripts/unison-reload'; import { i18n } from '@/i18n'; import { definePageMetadata } from '@/scripts/page-metadata'; +import { miLocalStorage } from '@/local-storage'; const props = withDefaults(defineProps<{ error?: Error; @@ -42,7 +43,7 @@ os.api('meta', { loaded = true; serverIsDead = false; meta = res; - localStorage.setItem('v', res.version); + miLocalStorage.setItem('v', res.version); }, () => { loaded = true; serverIsDead = true; diff --git a/packages/frontend/src/pages/messaging/messaging-room.form.vue b/packages/frontend/src/pages/messaging/messaging-room.form.vue index a0fa2677d0..e880129033 100644 --- a/packages/frontend/src/pages/messaging/messaging-room.form.vue +++ b/packages/frontend/src/pages/messaging/messaging-room.form.vue @@ -40,6 +40,7 @@ import { defaultStore } from '@/store'; import { i18n } from '@/i18n'; //import { Autocomplete } from '@/scripts/autocomplete'; import { uploadFile } from '@/scripts/upload'; +import { miLocalStorage } from '@/local-storage'; const props = defineProps<{ user?: Misskey.entities.UserDetailed | null; @@ -188,7 +189,7 @@ function clear() { } function saveDraft() { - const drafts = JSON.parse(localStorage.getItem('message_drafts') || '{}'); + const drafts = JSON.parse(miLocalStorage.getItem('message_drafts') || '{}'); drafts[draftKey] = { updatedAt: new Date(), @@ -199,15 +200,15 @@ function saveDraft() { }, }; - localStorage.setItem('message_drafts', JSON.stringify(drafts)); + miLocalStorage.setItem('message_drafts', JSON.stringify(drafts)); } function deleteDraft() { - const drafts = JSON.parse(localStorage.getItem('message_drafts') || '{}'); + const drafts = JSON.parse(miLocalStorage.getItem('message_drafts') || '{}'); delete drafts[draftKey]; - localStorage.setItem('message_drafts', JSON.stringify(drafts)); + miLocalStorage.setItem('message_drafts', JSON.stringify(drafts)); } async function insertEmoji(ev: MouseEvent) { @@ -222,7 +223,7 @@ onMounted(() => { //new Autocomplete(textEl, this, { model: 'text' }); // 書きかけの投稿を復元 - const draft = JSON.parse(localStorage.getItem('message_drafts') || '{}')[draftKey]; + const draft = JSON.parse(miLocalStorage.getItem('message_drafts') || '{}')[draftKey]; if (draft) { text = draft.data.text; file = draft.data.file; diff --git a/packages/frontend/src/pages/scratchpad.vue b/packages/frontend/src/pages/scratchpad.vue index 316133b968..ff5f06c8da 100644 --- a/packages/frontend/src/pages/scratchpad.vue +++ b/packages/frontend/src/pages/scratchpad.vue @@ -46,6 +46,7 @@ import { i18n } from '@/i18n'; import { definePageMetadata } from '@/scripts/page-metadata'; import { AsUiComponent, AsUiRoot, patch, registerAsUiLib, render } from '@/scripts/aiscript/ui'; import MkAsUi from '@/components/MkAsUi.vue'; +import { miLocalStorage } from '@/local-storage'; const parser = new Parser(); let aiscript: Interpreter; @@ -55,13 +56,13 @@ const root = ref<AsUiRoot>(); let components: Ref<AsUiComponent>[] = []; let uiKey = $ref(0); -const saved = localStorage.getItem('scratchpad'); +const saved = miLocalStorage.getItem('scratchpad'); if (saved) { code.value = saved; } watch(code, () => { - localStorage.setItem('scratchpad', code.value); + miLocalStorage.setItem('scratchpad', code.value); }); async function run() { diff --git a/packages/frontend/src/pages/settings/custom-css.vue b/packages/frontend/src/pages/settings/custom-css.vue index 9fa9bdd658..be2ec32ac2 100644 --- a/packages/frontend/src/pages/settings/custom-css.vue +++ b/packages/frontend/src/pages/settings/custom-css.vue @@ -16,11 +16,12 @@ import * as os from '@/os'; import { unisonReload } from '@/scripts/unison-reload'; import { i18n } from '@/i18n'; import { definePageMetadata } from '@/scripts/page-metadata'; +import { miLocalStorage } from '@/local-storage'; -const localCustomCss = ref(localStorage.getItem('customCss') ?? ''); +const localCustomCss = ref(miLocalStorage.getItem('customCss') ?? ''); async function apply() { - localStorage.setItem('customCss', localCustomCss.value); + miLocalStorage.setItem('customCss', localCustomCss.value); const { canceled } = await os.confirm({ type: 'info', diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue index b90dc3da0e..580c38149a 100644 --- a/packages/frontend/src/pages/settings/general.vue +++ b/packages/frontend/src/pages/settings/general.vue @@ -120,10 +120,11 @@ import * as os from '@/os'; import { unisonReload } from '@/scripts/unison-reload'; import { i18n } from '@/i18n'; import { definePageMetadata } from '@/scripts/page-metadata'; +import { miLocalStorage } from '@/local-storage'; -const lang = ref(localStorage.getItem('lang')); -const fontSize = ref(localStorage.getItem('fontSize')); -const useSystemFont = ref(localStorage.getItem('useSystemFont') != null); +const lang = ref(miLocalStorage.getItem('lang')); +const fontSize = ref(miLocalStorage.getItem('fontSize')); +const useSystemFont = ref(miLocalStorage.getItem('useSystemFont') != null); async function reloadAsk() { const { canceled } = await os.confirm({ @@ -157,23 +158,23 @@ const squareAvatars = computed(defaultStore.makeGetterSetter('squareAvatars')); const aiChanMode = computed(defaultStore.makeGetterSetter('aiChanMode')); watch(lang, () => { - localStorage.setItem('lang', lang.value as string); - localStorage.removeItem('locale'); + miLocalStorage.setItem('lang', lang.value as string); + miLocalStorage.removeItem('locale'); }); watch(fontSize, () => { if (fontSize.value == null) { - localStorage.removeItem('fontSize'); + miLocalStorage.removeItem('fontSize'); } else { - localStorage.setItem('fontSize', fontSize.value); + miLocalStorage.setItem('fontSize', fontSize.value); } }); watch(useSystemFont, () => { if (useSystemFont.value) { - localStorage.setItem('useSystemFont', 't'); + miLocalStorage.setItem('useSystemFont', 't'); } else { - localStorage.removeItem('useSystemFont'); + miLocalStorage.removeItem('useSystemFont'); } }); diff --git a/packages/frontend/src/pages/settings/index.vue b/packages/frontend/src/pages/settings/index.vue index 119a75b650..3468d44e00 100644 --- a/packages/frontend/src/pages/settings/index.vue +++ b/packages/frontend/src/pages/settings/index.vue @@ -33,6 +33,7 @@ import { instance } from '@/instance'; import { useRouter } from '@/router'; import { definePageMetadata, provideMetadataReceiver, setPageMetadata } from '@/scripts/page-metadata'; import * as os from '@/os'; +import { miLocalStorage } from '@/local-storage'; const indexInfo = { title: i18n.ts.settings, @@ -180,8 +181,8 @@ const menuDef = computed(() => [{ icon: 'ti ti-trash', text: i18n.ts.clearCache, action: () => { - localStorage.removeItem('locale'); - localStorage.removeItem('theme'); + miLocalStorage.removeItem('locale'); + miLocalStorage.removeItem('theme'); unisonReload(); }, }, { diff --git a/packages/frontend/src/pages/settings/preferences-backups.vue b/packages/frontend/src/pages/settings/preferences-backups.vue index 0c32676c89..87a08612fc 100644 --- a/packages/frontend/src/pages/settings/preferences-backups.vue +++ b/packages/frontend/src/pages/settings/preferences-backups.vue @@ -45,6 +45,7 @@ import { $i } from '@/account'; import { i18n } from '@/i18n'; import { version, host } from '@/config'; import { definePageMetadata } from '@/scripts/page-metadata'; +import { miLocalStorage } from '@/local-storage'; const { t, ts } = i18n; useCssModule(); @@ -170,9 +171,9 @@ function getSettings(): Profile['settings'] { return { hot, cold, - fontSize: localStorage.getItem('fontSize'), - useSystemFont: localStorage.getItem('useSystemFont') as 't' | null, - wallpaper: localStorage.getItem('wallpaper'), + fontSize: miLocalStorage.getItem('fontSize'), + useSystemFont: miLocalStorage.getItem('useSystemFont') as 't' | null, + wallpaper: miLocalStorage.getItem('wallpaper'), }; } @@ -279,23 +280,23 @@ async function applyProfile(id: string): Promise<void> { // fontSize if (settings.fontSize) { - localStorage.setItem('fontSize', settings.fontSize); + miLocalStorage.setItem('fontSize', settings.fontSize); } else { - localStorage.removeItem('fontSize'); + miLocalStorage.removeItem('fontSize'); } // useSystemFont if (settings.useSystemFont) { - localStorage.setItem('useSystemFont', settings.useSystemFont); + miLocalStorage.setItem('useSystemFont', settings.useSystemFont); } else { - localStorage.removeItem('useSystemFont'); + miLocalStorage.removeItem('useSystemFont'); } // wallpaper if (settings.wallpaper != null) { - localStorage.setItem('wallpaper', settings.wallpaper); + miLocalStorage.setItem('wallpaper', settings.wallpaper); } else { - localStorage.removeItem('wallpaper'); + miLocalStorage.removeItem('wallpaper'); } const { canceled: cancel2 } = await os.confirm({ diff --git a/packages/frontend/src/pages/settings/theme.vue b/packages/frontend/src/pages/settings/theme.vue index 60aa80647d..a2dc9bc95f 100644 --- a/packages/frontend/src/pages/settings/theme.vue +++ b/packages/frontend/src/pages/settings/theme.vue @@ -82,6 +82,7 @@ import { instance } from '@/instance'; import { uniqueBy } from '@/scripts/array'; import { fetchThemes, getThemes } from '@/theme-store'; import { definePageMetadata } from '@/scripts/page-metadata'; +import { miLocalStorage } from '@/local-storage'; const installedThemes = ref(getThemes()); const builtinThemes = getBuiltinThemesRef(); @@ -120,7 +121,7 @@ const lightThemeId = computed({ }); const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); const syncDeviceDarkMode = computed(ColdDeviceStorage.makeGetterSetter('syncDeviceDarkMode')); -const wallpaper = ref(localStorage.getItem('wallpaper')); +const wallpaper = ref(miLocalStorage.getItem('wallpaper')); const themesCount = installedThemes.value.length; watch(syncDeviceDarkMode, () => { @@ -131,9 +132,9 @@ watch(syncDeviceDarkMode, () => { watch(wallpaper, () => { if (wallpaper.value == null) { - localStorage.removeItem('wallpaper'); + miLocalStorage.removeItem('wallpaper'); } else { - localStorage.setItem('wallpaper', wallpaper.value); + miLocalStorage.setItem('wallpaper', wallpaper.value); } location.reload(); }); diff --git a/packages/frontend/src/scripts/aiscript/api.ts b/packages/frontend/src/scripts/aiscript/api.ts index 6debcb8a13..2a44223080 100644 --- a/packages/frontend/src/scripts/aiscript/api.ts +++ b/packages/frontend/src/scripts/aiscript/api.ts @@ -1,6 +1,7 @@ import { utils, values } from '@syuilo/aiscript'; import * as os from '@/os'; import { $i } from '@/account'; +import { miLocalStorage } from '@/local-storage'; export function createAiScriptEnv(opts) { let apiRequests = 0; @@ -32,12 +33,12 @@ export function createAiScriptEnv(opts) { }), 'Mk:save': values.FN_NATIVE(([key, value]) => { utils.assertString(key); - localStorage.setItem('aiscript:' + opts.storageKey + ':' + key.value, JSON.stringify(utils.valToJs(value))); + miLocalStorage.setItem(`aiscript:${opts.storageKey}:${key.value}`, JSON.stringify(utils.valToJs(value))); return values.NULL; }), 'Mk:load': values.FN_NATIVE(([key]) => { utils.assertString(key); - return utils.jsToVal(JSON.parse(localStorage.getItem('aiscript:' + opts.storageKey + ':' + key.value))); + return utils.jsToVal(JSON.parse(miLocalStorage.getItem(`aiscript:${opts.storageKey}:${key.value}`))); }), }; } diff --git a/packages/frontend/src/scripts/get-note-menu.ts b/packages/frontend/src/scripts/get-note-menu.ts index 7656770894..1b723220ee 100644 --- a/packages/frontend/src/scripts/get-note-menu.ts +++ b/packages/frontend/src/scripts/get-note-menu.ts @@ -9,6 +9,7 @@ import copyToClipboard from '@/scripts/copy-to-clipboard'; import { url } from '@/config'; import { noteActions } from '@/store'; import { notePage } from '@/filters/note'; +import { miLocalStorage } from '@/local-storage'; export function getNoteMenu(props: { note: misskey.entities.Note; @@ -181,7 +182,7 @@ export function getNoteMenu(props: { props.translating.value = true; const res = await os.api('notes/translate', { noteId: appearNote.id, - targetLang: localStorage.getItem('lang') || navigator.language, + targetLang: miLocalStorage.getItem('lang') || navigator.language, }); props.translating.value = false; props.translation.value = res; diff --git a/packages/frontend/src/scripts/idb-proxy.ts b/packages/frontend/src/scripts/idb-proxy.ts index 77bb84463c..218682bb56 100644 --- a/packages/frontend/src/scripts/idb-proxy.ts +++ b/packages/frontend/src/scripts/idb-proxy.ts @@ -22,15 +22,15 @@ if (idbAvailable) { export async function get(key: string) { if (idbAvailable) return iget(key); - return JSON.parse(localStorage.getItem(fallbackName(key))); + return JSON.parse(window.localStorage.getItem(fallbackName(key))); } export async function set(key: string, val: any) { if (idbAvailable) return iset(key, val); - return localStorage.setItem(fallbackName(key), JSON.stringify(val)); + return window.localStorage.setItem(fallbackName(key), JSON.stringify(val)); } export async function del(key: string) { if (idbAvailable) return idel(key); - return localStorage.removeItem(fallbackName(key)); + return window.localStorage.removeItem(fallbackName(key)); } diff --git a/packages/frontend/src/scripts/theme.ts b/packages/frontend/src/scripts/theme.ts index 62a2b9459a..42cb00265d 100644 --- a/packages/frontend/src/scripts/theme.ts +++ b/packages/frontend/src/scripts/theme.ts @@ -14,6 +14,7 @@ export type Theme = { import lightTheme from '@/themes/_light.json5'; import darkTheme from '@/themes/_dark.json5'; import { deepClone } from './clone'; +import { miLocalStorage } from '@/local-storage'; export const themeProps = Object.keys(lightTheme.props).filter(key => !key.startsWith('X')); @@ -84,8 +85,8 @@ export function applyTheme(theme: Theme, persist = true) { document.documentElement.style.setProperty('color-schema', colorSchema); if (persist) { - localStorage.setItem('theme', JSON.stringify(props)); - localStorage.setItem('colorSchema', colorSchema); + miLocalStorage.setItem('theme', JSON.stringify(props)); + miLocalStorage.setItem('colorSchema', colorSchema); } // 色計算など再度行えるようにクライアント全体に通知 diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts index 4b1f47c2bc..97b6ebc188 100644 --- a/packages/frontend/src/store.ts +++ b/packages/frontend/src/store.ts @@ -86,6 +86,14 @@ export const defaultStore = markRaw(new Storage('base', { where: 'account', default: [] as string[], }, + latestDonateDialogShowAt: { + where: 'account', + default: null, + }, + neverShowDonateDialog: { + where: 'account', + default: false, + }, menu: { where: 'deviceAccount', @@ -274,7 +282,7 @@ export const defaultStore = markRaw(new Storage('base', { // TODO: 他のタブと永続化されたstateを同期 -const PREFIX = 'miux:'; +const PREFIX = 'miux:' as const; type Plugin = { id: string; @@ -296,6 +304,7 @@ interface Watcher { import lightTheme from '@/themes/l-light.json5'; import darkTheme from '@/themes/d-green-lime.json5'; import { Note, UserDetailed } from 'misskey-js/built/entities'; +import { miLocalStorage } from './local-storage'; export class ColdDeviceStorage { public static default = { @@ -320,7 +329,7 @@ export class ColdDeviceStorage { // TODO: indexedDBにする // ただしその際はnullチェックではなくキー存在チェックにしないとダメ // (indexedDBはnullを保存できるため、ユーザーが意図してnullを格納した可能性がある) - const value = localStorage.getItem(PREFIX + key); + const value = miLocalStorage.getItem(`${PREFIX}${key}`); if (value == null) { return ColdDeviceStorage.default[key]; } else { @@ -330,14 +339,14 @@ export class ColdDeviceStorage { public static set<T extends keyof typeof ColdDeviceStorage.default>(key: T, value: typeof ColdDeviceStorage.default[T]): void { // 呼び出し側のバグ等で undefined が来ることがある - // undefined を文字列として localStorage に入れると参照する際の JSON.parse でコケて不具合の元になるため無視 + // undefined を文字列として miLocalStorage に入れると参照する際の JSON.parse でコケて不具合の元になるため無視 // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (value === undefined) { console.error(`attempt to store undefined value for key '${key}'`); return; } - localStorage.setItem(PREFIX + key, JSON.stringify(value)); + miLocalStorage.setItem(`${PREFIX}${key}`, JSON.stringify(value)); for (const watcher of this.watchers) { if (watcher.key === key) watcher.callback(value); diff --git a/packages/frontend/src/theme-store.ts b/packages/frontend/src/theme-store.ts index fdc92ed793..aa1244665b 100644 --- a/packages/frontend/src/theme-store.ts +++ b/packages/frontend/src/theme-store.ts @@ -1,11 +1,13 @@ import { api } from '@/os'; import { $i } from '@/account'; import { Theme } from './scripts/theme'; +import { miLocalStorage } from './local-storage'; -const lsCacheKey = $i ? `themes:${$i.id}` : ''; +const lsCacheKey = $i ? `themes:${$i.id}` as const : null; export function getThemes(): Theme[] { - return JSON.parse(localStorage.getItem(lsCacheKey) || '[]'); + if ($i == null) return []; + return JSON.parse(miLocalStorage.getItem(lsCacheKey!) || '[]'); } export async function fetchThemes(): Promise<void> { @@ -13,7 +15,7 @@ export async function fetchThemes(): Promise<void> { try { const themes = await api('i/registry/get', { scope: ['client'], key: 'themes' }); - localStorage.setItem(lsCacheKey, JSON.stringify(themes)); + miLocalStorage.setItem(lsCacheKey!, JSON.stringify(themes)); } catch (err) { if (err.code === 'NO_SUCH_KEY') return; throw err; @@ -21,14 +23,16 @@ export async function fetchThemes(): Promise<void> { } export async function addTheme(theme: Theme): Promise<void> { + if ($i == null) return; await fetchThemes(); const themes = getThemes().concat(theme); await api('i/registry/set', { scope: ['client'], key: 'themes', value: themes }); - localStorage.setItem(lsCacheKey, JSON.stringify(themes)); + miLocalStorage.setItem(lsCacheKey!, JSON.stringify(themes)); } export async function removeTheme(theme: Theme): Promise<void> { + if ($i == null) return; const themes = getThemes().filter(t => t.id !== theme.id); await api('i/registry/set', { scope: ['client'], key: 'themes', value: themes }); - localStorage.setItem(lsCacheKey, JSON.stringify(themes)); + miLocalStorage.setItem(lsCacheKey!, JSON.stringify(themes)); } diff --git a/packages/frontend/src/ui/classic.vue b/packages/frontend/src/ui/classic.vue index 280e69e7dd..f220501ee2 100644 --- a/packages/frontend/src/ui/classic.vue +++ b/packages/frontend/src/ui/classic.vue @@ -51,6 +51,7 @@ import { mainRouter } from '@/router'; import { PageMetadata, provideMetadataReceiver, setPageMetadata } from '@/scripts/page-metadata'; import { defaultStore } from '@/store'; import { i18n } from '@/i18n'; +import { miLocalStorage } from '@/local-storage'; const XHeaderMenu = defineAsyncComponent(() => import('./classic.header.vue')); const XWidgets = defineAsyncComponent(() => import('./universal.widgets.vue')); @@ -62,7 +63,7 @@ let pageMetadata = $ref<null | ComputedRef<PageMetadata>>(); let widgetsShowing = $ref(false); let fullView = $ref(false); let globalHeaderHeight = $ref(0); -const wallpaper = localStorage.getItem('wallpaper') != null; +const wallpaper = miLocalStorage.getItem('wallpaper') != null; const showMenuOnTop = $computed(() => defaultStore.state.menuDisplay === 'top'); let live2d = $shallowRef<HTMLIFrameElement>(); let widgetsLeft = $ref(); @@ -123,7 +124,7 @@ function onAiClick(ev) { } if (window.innerWidth < 1024) { - localStorage.setItem('ui', 'default'); + miLocalStorage.setItem('ui', 'default'); location.reload(); } diff --git a/packages/frontend/src/ui/universal.vue b/packages/frontend/src/ui/universal.vue index 9e1fee5b6b..06129ffc87 100644 --- a/packages/frontend/src/ui/universal.vue +++ b/packages/frontend/src/ui/universal.vue @@ -71,6 +71,7 @@ import { Router } from '@/nirax'; import { mainRouter } from '@/router'; import { PageMetadata, provideMetadataReceiver, setPageMetadata } from '@/scripts/page-metadata'; import { deviceKind } from '@/scripts/device-kind'; +import { miLocalStorage } from '@/local-storage'; const XWidgets = defineAsyncComponent(() => import('./universal.widgets.vue')); const XSidebar = defineAsyncComponent(() => import('@/ui/_common_/navbar.vue')); const XStatusBars = defineAsyncComponent(() => import('@/ui/_common_/statusbars.vue')); @@ -170,7 +171,7 @@ function top() { window.scroll({ top: 0, behavior: 'smooth' }); } -const wallpaper = localStorage.getItem('wallpaper') != null; +const wallpaper = miLocalStorage.getItem('wallpaper') != null; </script> <style lang="scss" scoped> From d99be6697e06ee87b4e00ab6421a2bdb1a9e1609 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sat, 7 Jan 2023 11:49:00 +0900 Subject: [PATCH 59/60] enhance(client): donation dialog --- locales/ja-JP.yml | 4 + .../frontend/src/components/MkDonation.vue | 109 ++++++++++++++++++ packages/frontend/src/init.ts | 8 ++ packages/frontend/src/local-storage.ts | 2 + packages/frontend/src/store.ts | 8 -- 5 files changed, 123 insertions(+), 8 deletions(-) create mode 100644 packages/frontend/src/components/MkDonation.vue diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 32bafcd661..b49d872a0b 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -920,6 +920,10 @@ like: "いいね!" unlike: "いいねを解除" numberOfLikes: "いいね数" show: "表示" +neverShow: "今後表示しない" +remindMeLater: "また後で" +didYouLikeMisskey: "Misskeyを気に入っていただけましたか?" +pleaseDonate: "Misskeyは{host}が使用している無料のソフトウェアです。これからも開発を続けられるように、ぜひ寄付をお願いします!" _sensitiveMediaDetection: description: "機械学習を使って自動でセンシティブなメディアを検出し、モデレーションに役立てることができます。サーバーの負荷が少し増えます。" diff --git a/packages/frontend/src/components/MkDonation.vue b/packages/frontend/src/components/MkDonation.vue new file mode 100644 index 0000000000..707444abc9 --- /dev/null +++ b/packages/frontend/src/components/MkDonation.vue @@ -0,0 +1,109 @@ +<template> +<div class="_panel _shadow" :class="$style.root"> + <!-- TODO: インスタンス運営者が任意のテキストとリンクを設定できるようにする --> + <div :class="$style.icon"> + <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-pig-money" width="40" height="40" viewBox="0 0 24 24" stroke-width="1" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"> + <path stroke="none" d="M0 0h24v24H0z" fill="none"></path> + <path d="M15 11v.01"></path> + <path d="M5.173 8.378a3 3 0 1 1 4.656 -1.377"></path> + <path d="M16 4v3.803a6.019 6.019 0 0 1 2.658 3.197h1.341a1 1 0 0 1 1 1v2a1 1 0 0 1 -1 1h-1.342c-.336 .95 -.907 1.8 -1.658 2.473v2.027a1.5 1.5 0 0 1 -3 0v-.583a6.04 6.04 0 0 1 -1 .083h-4a6.04 6.04 0 0 1 -1 -.083v.583a1.5 1.5 0 0 1 -3 0v-2l.001 -.027a6 6 0 0 1 3.999 -10.473h2.5l4.5 -3h.001z"></path> + </svg> + </div> + <div :class="$style.main"> + <div :class="$style.title">{{ i18n.ts.didYouLikeMisskey }}</div> + <div :class="$style.text"> + <I18n :src="i18n.ts.pleaseDonate" tag="span"> + <template #host> + {{ $instance.name ?? host }} + </template> + </I18n> + <div style="margin-top: 0.2em;"> + <MkLink target="_blank" url="https://misskey-hub.net/docs/donate.html">{{ i18n.ts.learnMore }}</MkLink> + </div> + </div> + <div class="_buttons"> + <MkButton @click="close">{{ i18n.ts.remindMeLater }}</MkButton> + <MkButton @click="neverShow">{{ i18n.ts.neverShow }}</MkButton> + </div> + </div> + <button class="_button" :class="$style.close" @click="close"><i class="ti ti-x"></i></button> +</div> +</template> + +<script lang="ts" setup> +import { onMounted, shallowRef } from 'vue'; +import MkButton from '@/components/MkButton.vue'; +import MkLink from '@/components/MkLink.vue'; +import { host } from '@/config'; +import { i18n } from '@/i18n'; +import * as os from '@/os'; +import { miLocalStorage } from '@/local-storage'; + +const emit = defineEmits<{ + (ev: 'closed'): void; +}>(); + +const zIndex = os.claimZIndex('low'); + +function close() { + miLocalStorage.setItem('latestDonationInfoShownAt', Date.now().toString()); + emit('closed'); +} + +function neverShow() { + miLocalStorage.setItem('neverShowDonationInfo', 'true') + close(); +} +</script> + +<style lang="scss" module> +.root { + position: fixed; + z-index: v-bind(zIndex); + bottom: var(--margin); + left: 0; + right: 0; + margin: auto; + box-sizing: border-box; + width: calc(100% - (var(--margin) * 2)); + max-width: 500px; + display: flex; +} + +.icon { + text-align: center; + padding-top: 25px; + width: 100px; + color: var(--accent); +} +@media (max-width: 500px) { + .icon { + width: 80px; + } +} +@media (max-width: 450px) { + .icon { + width: 70px; + } +} + +.main { + padding: 25px 25px 25px 0; + flex: 1; +} + +.close { + position: absolute; + top: 8px; + right: 8px; + padding: 8px; +} + +.title { + font-weight: bold; +} + +.text { + margin: 0.7em 0 1em 0; +} +</style> diff --git a/packages/frontend/src/init.ts b/packages/frontend/src/init.ts index bd515f47ea..e10315e1ad 100644 --- a/packages/frontend/src/init.ts +++ b/packages/frontend/src/init.ts @@ -357,6 +357,14 @@ import { miLocalStorage } from './local-storage'; } miLocalStorage.setItem('lastUsed', Date.now().toString()); + const latestDonationInfoShownAt = miLocalStorage.getItem('latestDonationInfoShownAt'); + const neverShowDonationInfo = miLocalStorage.getItem('neverShowDonationInfo'); + if (neverShowDonationInfo !== 'true' && (new Date($i.createdAt).getTime() < (Date.now() - (1000 * 60 * 60 * 24 * 3)))) { + if (latestDonationInfoShownAt == null || (new Date(latestDonationInfoShownAt).getTime() < (Date.now() - (1000 * 60 * 60 * 24 * 30)))) { + popup(defineAsyncComponent(() => import('@/components/MkDonation.vue')), {}, {}, 'closed'); + } + } + if ('Notification' in window) { // 許可を得ていなかったらリクエスト if (Notification.permission === 'default') { diff --git a/packages/frontend/src/local-storage.ts b/packages/frontend/src/local-storage.ts index 5286a6f3a7..50e28d621f 100644 --- a/packages/frontend/src/local-storage.ts +++ b/packages/frontend/src/local-storage.ts @@ -4,6 +4,8 @@ type Keys = 'instance' | 'account' | 'accounts' | + 'latestDonationInfoShownAt' | + 'neverShowDonationInfo' | 'lastUsed' | 'lang' | 'drafts' | diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts index 97b6ebc188..b2bf8db646 100644 --- a/packages/frontend/src/store.ts +++ b/packages/frontend/src/store.ts @@ -86,14 +86,6 @@ export const defaultStore = markRaw(new Storage('base', { where: 'account', default: [] as string[], }, - latestDonateDialogShowAt: { - where: 'account', - default: null, - }, - neverShowDonateDialog: { - where: 'account', - default: false, - }, menu: { where: 'deviceAccount', From ecd6fc1db8a10d2279b04f4bcf43f283c19b620d Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sat, 7 Jan 2023 11:49:04 +0900 Subject: [PATCH 60/60] :art: --- packages/frontend/src/components/global/MkSpacer.vue | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/frontend/src/components/global/MkSpacer.vue b/packages/frontend/src/components/global/MkSpacer.vue index 1ddb230bd6..78e9a1a9c2 100644 --- a/packages/frontend/src/components/global/MkSpacer.vue +++ b/packages/frontend/src/components/global/MkSpacer.vue @@ -38,13 +38,13 @@ const forceSpacerMin = inject('forceSpacerMin', false) || deviceKind === 'smartp container-type: inline-size; } -@container (max-width: 360px) { +@container (max-width: 450px) { .root { padding: v-bind('props.marginMin + "px"'); } } -@container (min-width: 361px) { +@container (min-width: 451px) { .root { padding: v-bind('props.marginMax + "px"'); }