From c548ec9906947c72743e611254a6557e8e8d057c Mon Sep 17 00:00:00 2001 From: かっこかり <67428053+kakkokari-gtyih@users.noreply.github.com> Date: Wed, 5 Feb 2025 19:01:44 +0900 Subject: refactor(frontend): verbatimModuleSyntaxを有効化 (#15323) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * wip * wip * wip * wip * revert unnecessary changes * wip * refactor(frontend): enforce verbatimModuleSyntax * fix * refactor(frontend-shared): enforce verbatimModuleSyntax * wip * refactor(frontend-embed): enforce verbatimModuleSyntax * enforce consistent-type-imports * fix lint config * attemt to fix ci * fix lint * fix * fix * fix --- packages/frontend-shared/eslint.config.js | 1 + packages/frontend-shared/tsconfig.json | 1 + 2 files changed, 2 insertions(+) (limited to 'packages/frontend-shared') diff --git a/packages/frontend-shared/eslint.config.js b/packages/frontend-shared/eslint.config.js index cd4641a270..a3ab438336 100644 --- a/packages/frontend-shared/eslint.config.js +++ b/packages/frontend-shared/eslint.config.js @@ -52,6 +52,7 @@ export default [ '@typescript-eslint/no-empty-interface': ['error', { allowSingleExtends: true, }], + 'import/consistent-type-specifier-style': ['error', 'prefer-top-level'], // window の禁止理由: グローバルスコープと衝突し、予期せぬ結果を招くため // e の禁止理由: error や event など、複数のキーワードの頭文字であり分かりにくいため 'id-denylist': ['error', 'window', 'e'], diff --git a/packages/frontend-shared/tsconfig.json b/packages/frontend-shared/tsconfig.json index 09a8ff76aa..12f00eb503 100644 --- a/packages/frontend-shared/tsconfig.json +++ b/packages/frontend-shared/tsconfig.json @@ -16,6 +16,7 @@ "experimentalDecorators": true, "noImplicitReturns": true, "esModuleInterop": true, + "verbatimModuleSyntax": true, "baseUrl": ".", "paths": { "@/*": ["./*"], -- cgit v1.2.3-freya From cfb61289a99a2f6c4e0d8214a11046a93db1180d Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Wed, 5 Feb 2025 20:17:48 +0900 Subject: refactor(frontend): remove X theme properties (#15376) * refactor(frontend): remove X theme properties * Update MkAutocomplete.vue * Update WidgetCalendar.vue --- packages/frontend-shared/themes/_dark.json5 | 8 -------- packages/frontend-shared/themes/_light.json5 | 8 -------- packages/frontend-shared/themes/d-astro.json5 | 8 -------- packages/frontend-shared/themes/d-u0.json5 | 8 -------- packages/frontend-shared/themes/l-u0.json5 | 8 -------- packages/frontend-shared/themes/l-vivid.json5 | 8 -------- packages/frontend/src/components/MkAutocomplete.vue | 6 +++--- packages/frontend/src/components/MkCustomEmojiDetailedDialog.vue | 4 ++-- packages/frontend/src/components/MkEmojiPicker.vue | 6 +++--- packages/frontend/src/components/MkPostForm.vue | 6 +++--- packages/frontend/src/components/MkUserSelectDialog.vue | 4 ++-- packages/frontend/src/pages/admin/server-rules.vue | 2 +- packages/frontend/src/pages/page-editor/page-editor.container.vue | 4 ++-- packages/frontend/src/widgets/WidgetCalendar.vue | 2 +- 14 files changed, 17 insertions(+), 65 deletions(-) (limited to 'packages/frontend-shared') diff --git a/packages/frontend-shared/themes/_dark.json5 b/packages/frontend-shared/themes/_dark.json5 index 5271785e62..f2d8a7aed8 100644 --- a/packages/frontend-shared/themes/_dark.json5 +++ b/packages/frontend-shared/themes/_dark.json5 @@ -79,14 +79,6 @@ codeBoolean: '#c59eff', deckBg: '#000', htmlThemeColor: '@bg', - X3: 'rgba(255, 255, 255, 0.05)', - X4: 'rgba(255, 255, 255, 0.1)', - X5: 'rgba(255, 255, 255, 0.05)', - X6: 'rgba(255, 255, 255, 0.15)', - X7: 'rgba(255, 255, 255, 0.05)', - X11: 'rgba(0, 0, 0, 0.3)', - X12: 'rgba(255, 255, 255, 0.1)', - X13: 'rgba(255, 255, 255, 0.15)', }, codeHighlighter: { diff --git a/packages/frontend-shared/themes/_light.json5 b/packages/frontend-shared/themes/_light.json5 index be331ce58f..22893bf4b3 100644 --- a/packages/frontend-shared/themes/_light.json5 +++ b/packages/frontend-shared/themes/_light.json5 @@ -79,14 +79,6 @@ codeBoolean: '#62b70c', deckBg: ':darken<3<@bg', htmlThemeColor: '@bg', - X3: 'rgba(0, 0, 0, 0.05)', - X4: 'rgba(0, 0, 0, 0.1)', - X5: 'rgba(0, 0, 0, 0.05)', - X6: 'rgba(0, 0, 0, 0.25)', - X7: 'rgba(0, 0, 0, 0.05)', - X11: 'rgba(0, 0, 0, 0.1)', - X12: 'rgba(0, 0, 0, 0.1)', - X13: 'rgba(0, 0, 0, 0.15)', }, codeHighlighter: { diff --git a/packages/frontend-shared/themes/d-astro.json5 b/packages/frontend-shared/themes/d-astro.json5 index 4422526a33..e8864df336 100644 --- a/packages/frontend-shared/themes/d-astro.json5 +++ b/packages/frontend-shared/themes/d-astro.json5 @@ -54,13 +54,5 @@ wallpaperOverlay: 'rgba(0, 0, 0, 0.5)', panelHeaderDivider: 'rgba(0, 0, 0, 0)', scrollbarHandleHover: 'rgba(255, 255, 255, 0.4)', - X3: 'rgba(255, 255, 255, 0.05)', - X4: 'rgba(255, 255, 255, 0.1)', - X5: 'rgba(255, 255, 255, 0.05)', - X6: 'rgba(255, 255, 255, 0.15)', - X7: 'rgba(255, 255, 255, 0.05)', - X11: 'rgba(0, 0, 0, 0.3)', - X12: 'rgba(255, 255, 255, 0.1)', - X13: 'rgba(255, 255, 255, 0.15)', }, } diff --git a/packages/frontend-shared/themes/d-u0.json5 b/packages/frontend-shared/themes/d-u0.json5 index fb707c74c3..0223b1fb5c 100644 --- a/packages/frontend-shared/themes/d-u0.json5 +++ b/packages/frontend-shared/themes/d-u0.json5 @@ -3,17 +3,9 @@ base: 'dark', name: 'Mi U0 Dark', props: { - X3: 'rgba(255, 255, 255, 0.05)', - X4: 'rgba(255, 255, 255, 0.1)', - X5: 'rgba(255, 255, 255, 0.05)', - X6: 'rgba(255, 255, 255, 0.15)', - X7: 'rgba(255, 255, 255, 0.05)', bg: '#172426', fg: '#dadada', X10: ':alpha<0.4<@accent', - X11: 'rgba(0, 0, 0, 0.3)', - X12: 'rgba(255, 255, 255, 0.1)', - X13: 'rgba(255, 255, 255, 0.15)', X14: ':alpha<0.5<@navBg', X15: ':alpha<0<@panel', X16: ':alpha<0.7<@panel', diff --git a/packages/frontend-shared/themes/l-u0.json5 b/packages/frontend-shared/themes/l-u0.json5 index 7062e7fe5b..f6023af819 100644 --- a/packages/frontend-shared/themes/l-u0.json5 +++ b/packages/frontend-shared/themes/l-u0.json5 @@ -3,17 +3,9 @@ base: 'light', name: 'Mi U0 Light', props: { - X3: 'rgba(255, 255, 255, 0.05)', - X4: 'rgba(255, 255, 255, 0.1)', - X5: 'rgba(255, 255, 255, 0.05)', - X6: 'rgba(255, 255, 255, 0.15)', - X7: 'rgba(255, 255, 255, 0.05)', bg: '#e7e7eb', fg: '#5f5f5f', X10: ':alpha<0.4<@accent', - X11: 'rgba(0, 0, 0, 0.3)', - X12: 'rgba(255, 255, 255, 0.1)', - X13: 'rgba(255, 255, 255, 0.15)', X14: ':alpha<0.5<@navBg', X15: ':alpha<0<@panel', X16: ':alpha<0.7<@panel', diff --git a/packages/frontend-shared/themes/l-vivid.json5 b/packages/frontend-shared/themes/l-vivid.json5 index 39768d4ac6..058c9c32e5 100644 --- a/packages/frontend-shared/themes/l-vivid.json5 +++ b/packages/frontend-shared/themes/l-vivid.json5 @@ -57,13 +57,5 @@ fgTransparentWeak: ':alpha<0.75<@fg', panelHeaderDivider: '@divider', scrollbarHandleHover: 'rgba(0, 0, 0, 0.4)', - X3: 'rgba(0, 0, 0, 0.05)', - X4: 'rgba(0, 0, 0, 0.1)', - X5: 'rgba(0, 0, 0, 0.05)', - X6: 'rgba(0, 0, 0, 0.25)', - X7: 'rgba(0, 0, 0, 0.05)', - X11: 'rgba(0, 0, 0, 0.1)', - X12: 'rgba(0, 0, 0, 0.1)', - X13: 'rgba(0, 0, 0, 0.15)', }, } diff --git a/packages/frontend/src/components/MkAutocomplete.vue b/packages/frontend/src/components/MkAutocomplete.vue index e7372da6f8..685dcb86e4 100644 --- a/packages/frontend/src/components/MkAutocomplete.vue +++ b/packages/frontend/src/components/MkAutocomplete.vue @@ -47,8 +47,9 @@ SPDX-License-Identifier: AGPL-3.0-only import { markRaw, ref, shallowRef, computed, onUpdated, onMounted, onBeforeUnmount, nextTick, watch } from 'vue'; import sanitizeHtml from 'sanitize-html'; import { emojilist, getEmojiName } from '@@/js/emojilist.js'; -import contains from '@/scripts/contains.js'; import { char2twemojiFilePath, char2fluentEmojiFilePath } from '@@/js/emoji-base.js'; +import { MFM_TAGS, MFM_PARAMS } from '@@/js/const.js'; +import contains from '@/scripts/contains.js'; import { acct } from '@/filters/user.js'; import * as os from '@/os.js'; import { misskeyApi } from '@/scripts/misskey-api.js'; @@ -56,7 +57,6 @@ import { defaultStore } from '@/store.js'; import { i18n } from '@/i18n.js'; import { miLocalStorage } from '@/local-storage.js'; import { customEmojis } from '@/custom-emojis.js'; -import { MFM_TAGS, MFM_PARAMS } from '@@/js/const.js'; import { searchEmoji } from '@/scripts/search-emoji.js'; import type { EmojiDef } from '@/scripts/search-emoji.js'; @@ -408,7 +408,7 @@ onBeforeUnmount(() => { text-overflow: ellipsis; &:hover { - background: var(--MI_THEME-X3); + background: light-dark(rgba(0, 0, 0, 0.05), rgba(255, 255, 255, 0.05)); } &[data-selected='true'] { diff --git a/packages/frontend/src/components/MkCustomEmojiDetailedDialog.vue b/packages/frontend/src/components/MkCustomEmojiDetailedDialog.vue index e6ab17417d..86d6269c69 100644 --- a/packages/frontend/src/components/MkCustomEmojiDetailedDialog.vue +++ b/packages/frontend/src/components/MkCustomEmojiDetailedDialog.vue @@ -85,7 +85,7 @@ function cancel() { .emojiImgWrapper { max-width: 100%; height: 40cqh; - background-image: repeating-linear-gradient(45deg, transparent, transparent 8px, var(--MI_THEME-X5) 8px, var(--MI_THEME-X5) 14px); + background-image: repeating-linear-gradient(45deg, transparent, transparent 8px, light-dark(rgba(0, 0, 0, 0.05), rgba(255, 255, 255, 0.05)) 8px, light-dark(rgba(0, 0, 0, 0.05), rgba(255, 255, 255, 0.05)) 14px); border-radius: var(--MI-radius); margin: auto; overflow-y: hidden; @@ -101,7 +101,7 @@ function cancel() { display: inline-block; word-break: break-all; padding: 3px 10px; - background-color: var(--MI_THEME-X5); + background-color: light-dark(rgba(0, 0, 0, 0.05), rgba(255, 255, 255, 0.05)); border: solid 1px var(--MI_THEME-divider); border-radius: var(--MI-radius); } diff --git a/packages/frontend/src/components/MkEmojiPicker.vue b/packages/frontend/src/components/MkEmojiPicker.vue index 7ee0d6a6ac..5da161dae8 100644 --- a/packages/frontend/src/components/MkEmojiPicker.vue +++ b/packages/frontend/src/components/MkEmojiPicker.vue @@ -582,7 +582,7 @@ defineExpose({ &:disabled { cursor: not-allowed; - background: linear-gradient(-45deg, transparent 0% 48%, var(--MI_THEME-X6) 48% 52%, transparent 52% 100%); + background: linear-gradient(-45deg, transparent 0% 48%, light-dark(rgba(0, 0, 0, 0.25), rgba(255, 255, 255, 0.15)) 48% 52%, transparent 52% 100%); opacity: 1; > .emoji { @@ -617,7 +617,7 @@ defineExpose({ &:disabled { cursor: not-allowed; - background: linear-gradient(-45deg, transparent 0% 48%, var(--MI_THEME-X6) 48% 52%, transparent 52% 100%); + background: linear-gradient(-45deg, transparent 0% 48%, light-dark(rgba(0, 0, 0, 0.25), rgba(255, 255, 255, 0.15)) 48% 52%, transparent 52% 100%); opacity: 1; > .emoji { @@ -738,7 +738,7 @@ defineExpose({ &:disabled { cursor: not-allowed; - background: linear-gradient(-45deg, transparent 0% 48%, var(--MI_THEME-X6) 48% 52%, transparent 52% 100%); + background: linear-gradient(-45deg, transparent 0% 48%, light-dark(rgba(0, 0, 0, 0.25), rgba(255, 255, 255, 0.15)) 48% 52%, transparent 52% 100%); opacity: 1; > .emoji { diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue index 500f97b2ed..4f2792ed40 100644 --- a/packages/frontend/src/components/MkPostForm.vue +++ b/packages/frontend/src/components/MkPostForm.vue @@ -1166,7 +1166,7 @@ defineExpose({ border-radius: 6px; &:hover { - background: var(--MI_THEME-X5); + background: light-dark(rgba(0, 0, 0, 0.05), rgba(255, 255, 255, 0.05)); } &:disabled { @@ -1238,7 +1238,7 @@ html[data-color-scheme=light] .preview { margin-right: 14px; padding: 8px 0 8px 8px; border-radius: 8px; - background: var(--MI_THEME-X4); + background: light-dark(rgba(0, 0, 0, 0.1), rgba(255, 255, 255, 0.1)); } .hasNotSpecifiedMentions { @@ -1349,7 +1349,7 @@ html[data-color-scheme=light] .preview { border-radius: 6px; &:hover { - background: var(--MI_THEME-X5); + background: light-dark(rgba(0, 0, 0, 0.05), rgba(255, 255, 255, 0.05)); } &.footerButtonActive { diff --git a/packages/frontend/src/components/MkUserSelectDialog.vue b/packages/frontend/src/components/MkUserSelectDialog.vue index 63af652cbc..1e93d9dbea 100644 --- a/packages/frontend/src/components/MkUserSelectDialog.vue +++ b/packages/frontend/src/components/MkUserSelectDialog.vue @@ -63,6 +63,7 @@ SPDX-License-Identifier: AGPL-3.0-only @@ -47,6 +50,10 @@ withDefaults(defineProps<{ min-height: calc(var(--fukidashi-radius) * 2); padding-top: calc(var(--fukidashi-radius) * .13); + &.accented { + --fukidashi-bg: var(--MI_THEME-accent); + } + &.shadow { filter: drop-shadow(0 4px 32px var(--MI_THEME-shadow)); } @@ -77,7 +84,7 @@ withDefaults(defineProps<{ .content { position: relative; - padding: 8px 12px; + padding: 10px 14px; } .tail { diff --git a/packages/frontend/src/components/MkMediaList.vue b/packages/frontend/src/components/MkMediaList.vue index ae15776041..4a1100c324 100644 --- a/packages/frontend/src/components/MkMediaList.vue +++ b/packages/frontend/src/components/MkMediaList.vue @@ -227,7 +227,6 @@ defineExpose({ .container { position: relative; width: 100%; - margin-top: 4px; } .medias { diff --git a/packages/frontend/src/components/MkMenu.vue b/packages/frontend/src/components/MkMenu.vue index a84bd9b256..f2f36308ca 100644 --- a/packages/frontend/src/components/MkMenu.vue +++ b/packages/frontend/src/components/MkMenu.vue @@ -11,6 +11,7 @@ SPDX-License-Identifier: AGPL-3.0-only [$style.center]: align === 'center', [$style.big]: big, [$style.asDrawer]: asDrawer, + [$style.widthSpecified]: width != null, }" @focusin.passive.stop="() => {}" > @@ -29,15 +30,19 @@ SPDX-License-Identifier: AGPL-3.0-only > + {{ i18n.ts.none }} @@ -438,6 +473,12 @@ onBeforeUnmount(() => { } } + &:not(.widthSpecified) { + > .menu { + max-width: 400px; + } + } + &.big:not(.asDrawer) { > .menu { min-width: 230px; @@ -607,10 +648,19 @@ onBeforeUnmount(() => { .item_content_text { max-width: calc(100vw - 4rem); +} + +.item_content_text_title { text-overflow: ellipsis; overflow: hidden; } +.item_content_text_caption { + text-wrap: auto; + font-size: 85%; + opacity: 0.7; +} + .switchButton { margin-left: -2px; --height: 1.35em; diff --git a/packages/frontend/src/components/MkPagination.vue b/packages/frontend/src/components/MkPagination.vue index ab8bda403b..a729619180 100644 --- a/packages/frontend/src/components/MkPagination.vue +++ b/packages/frontend/src/components/MkPagination.vue @@ -24,16 +24,16 @@ SPDX-License-Identifier: AGPL-3.0-only -
-
- +
+
+ {{ i18n.ts.loadMore }}
-
- +
+ {{ i18n.ts.loadMore }} @@ -46,7 +46,7 @@ SPDX-License-Identifier: AGPL-3.0-only import { computed, isRef, nextTick, onActivated, onBeforeMount, onBeforeUnmount, onDeactivated, ref, useTemplateRef, watch } from 'vue'; import * as Misskey from 'misskey-js'; import { useDocumentVisibility } from '@@/js/use-document-visibility.js'; -import { onScrollTop, isTopVisible, getBodyScrollHeight, getScrollContainer, onScrollBottom, scrollToBottom, scroll, isBottomVisible } from '@@/js/scroll.js'; +import { onScrollTop, isHeadVisible, getBodyScrollHeight, getScrollContainer, onScrollBottom, scrollToBottom, scroll, isTailVisible } from '@@/js/scroll.js'; import type { ComputedRef } from 'vue'; import type { MisskeyEntity } from '@/types/date-separated-list.js'; import { misskeyApi } from '@/utility/misskey-api.js'; @@ -74,8 +74,6 @@ export type Paging reversed?: boolean; offsetMode?: boolean; - - pageEl?: HTMLElement; }; type MisskeyEntityMap = Map; @@ -141,8 +139,7 @@ const { enableInfiniteScroll, } = prefer.r; -const contentEl = computed(() => props.pagination.pageEl ?? rootEl.value); -const scrollableElement = computed(() => contentEl.value ? getScrollContainer(contentEl.value) : window.document.body); +const scrollableElement = computed(() => rootEl.value ? getScrollContainer(rootEl.value) : window.document.body); const visibility = useDocumentVisibility(); @@ -173,13 +170,13 @@ watch(rootEl, () => { }); }); -watch([backed, contentEl], () => { +watch([backed, rootEl], () => { if (!backed.value) { - if (!contentEl.value) return; + if (!rootEl.value) return; scrollRemove.value = props.pagination.reversed - ? onScrollBottom(contentEl.value, executeQueue, TOLERANCE) - : onScrollTop(contentEl.value, (topVisible) => { if (topVisible) executeQueue(); }, TOLERANCE); + ? onScrollBottom(rootEl.value, executeQueue, TOLERANCE) + : onScrollTop(rootEl.value, (topVisible) => { if (topVisible) executeQueue(); }, TOLERANCE); } else { if (scrollRemove.value) scrollRemove.value(); scrollRemove.value = null; @@ -349,7 +346,7 @@ const appearFetchMoreAhead = async (): Promise => { fetchMoreAppearTimeout(); }; -const isTop = (): boolean => isBackTop.value || (props.pagination.reversed ? isBottomVisible : isTopVisible)(contentEl.value!, TOLERANCE); +const isHead = (): boolean => isBackTop.value || (props.pagination.reversed ? isTailVisible : isHeadVisible)(rootEl.value!, TOLERANCE); watch(visibility, () => { if (visibility.value === 'hidden') { @@ -364,7 +361,7 @@ watch(visibility, () => { timerForSetPause = null; } else { isPausingUpdate = false; - if (isTop()) { + if (isHead()) { executeQueue(); } } @@ -376,16 +373,18 @@ watch(visibility, () => { * ストリーミングから降ってきたアイテムはこれで追加する * @param item アイテム */ -const prepend = (item: MisskeyEntity): void => { +function prepend(item: MisskeyEntity): void { if (items.value.size === 0) { items.value.set(item.id, item); fetching.value = false; return; } - if (isTop() && !isPausingUpdate) unshiftItems([item]); + console.log(isHead(), isPausingUpdate); + + if (isHead() && !isPausingUpdate) unshiftItems([item]); else prependQueue(item); -}; +} /** * 新着アイテムをitemsの先頭に追加し、displayLimitを適用する @@ -447,7 +446,7 @@ onDeactivated(() => { }); function toBottom() { - scrollToBottom(contentEl.value!); + scrollToBottom(rootEl.value!); } onBeforeMount(() => { diff --git a/packages/frontend/src/components/MkPolkadots.vue b/packages/frontend/src/components/MkPolkadots.vue new file mode 100644 index 0000000000..285c4d0b79 --- /dev/null +++ b/packages/frontend/src/components/MkPolkadots.vue @@ -0,0 +1,40 @@ + + + + + + + diff --git a/packages/frontend/src/components/MkUrlPreview.vue b/packages/frontend/src/components/MkUrlPreview.vue index f20aee0ce3..20dab6f028 100644 --- a/packages/frontend/src/components/MkUrlPreview.vue +++ b/packages/frontend/src/components/MkUrlPreview.vue @@ -246,6 +246,7 @@ onUnmounted(() => { box-shadow: 0 0 0 1px var(--MI_THEME-divider); border-radius: 8px; overflow: clip; + text-align: left; &:hover { text-decoration: none; diff --git a/packages/frontend/src/local-storage.ts b/packages/frontend/src/local-storage.ts index 099339fbee..f6d6bbf0fb 100644 --- a/packages/frontend/src/local-storage.ts +++ b/packages/frontend/src/local-storage.ts @@ -28,7 +28,7 @@ export type Keys = ( 'theme' | 'themeId' | 'customCss' | - 'message_drafts' | + 'chatMessageDrafts' | 'scratchpad' | 'debug' | 'preferences' | diff --git a/packages/frontend/src/navbar.ts b/packages/frontend/src/navbar.ts index d478ece641..894df83721 100644 --- a/packages/frontend/src/navbar.ts +++ b/packages/frontend/src/navbar.ts @@ -4,6 +4,7 @@ */ import { computed, reactive } from 'vue'; +import { ui } from '@@/js/config.js'; import { clearCache } from './utility/clear-cache.js'; import { $i } from '@/i.js'; import { miLocalStorage } from '@/local-storage.js'; @@ -11,7 +12,6 @@ import { openInstanceMenu, openToolsMenu } from '@/ui/_common_/common.js'; import { lookup } from '@/utility/lookup.js'; import * as os from '@/os.js'; import { i18n } from '@/i18n.js'; -import { ui } from '@@/js/config.js'; import { unisonReload } from '@/utility/unison-reload.js'; export const navbarItemDef = reactive({ @@ -110,6 +110,12 @@ export const navbarItemDef = reactive({ icon: 'ti ti-device-tv', to: '/channels', }, + chat: { + title: i18n.ts.chat, + icon: 'ti ti-message', + to: '/chat', + indicated: computed(() => $i != null && $i.hasUnreadChatMessages), + }, achievements: { title: i18n.ts.achievements, icon: 'ti ti-medal', diff --git a/packages/frontend/src/pages/admin/roles.editor.vue b/packages/frontend/src/pages/admin/roles.editor.vue index 4e9f4edb70..d1e823215a 100644 --- a/packages/frontend/src/pages/admin/roles.editor.vue +++ b/packages/frontend/src/pages/admin/roles.editor.vue @@ -160,6 +160,26 @@ SPDX-License-Identifier: AGPL-3.0-only
+ + + +
+ + + + + + + + + +
+
+