summaryrefslogtreecommitdiff
path: root/packages/client/src/components/global
diff options
context:
space:
mode:
authorsyuilo <Syuilotan@yahoo.co.jp>2022-12-27 14:36:33 +0900
committersyuilo <Syuilotan@yahoo.co.jp>2022-12-27 14:36:33 +0900
commit9384f5399da39e53855beb8e7f8ded1aa56bf72e (patch)
treece5959571a981b9c4047da3c7b3fd080aa44222c /packages/client/src/components/global
parentwip: retention for dashboard (diff)
downloadsharkey-9384f5399da39e53855beb8e7f8ded1aa56bf72e.tar.gz
sharkey-9384f5399da39e53855beb8e7f8ded1aa56bf72e.tar.bz2
sharkey-9384f5399da39e53855beb8e7f8ded1aa56bf72e.zip
rename: client -> frontend
Diffstat (limited to 'packages/client/src/components/global')
-rw-r--r--packages/client/src/components/global/MkA.vue102
-rw-r--r--packages/client/src/components/global/MkAcct.vue27
-rw-r--r--packages/client/src/components/global/MkAd.vue186
-rw-r--r--packages/client/src/components/global/MkAvatar.vue143
-rw-r--r--packages/client/src/components/global/MkEllipsis.vue34
-rw-r--r--packages/client/src/components/global/MkEmoji.vue81
-rw-r--r--packages/client/src/components/global/MkError.vue36
-rw-r--r--packages/client/src/components/global/MkLoading.vue101
-rw-r--r--packages/client/src/components/global/MkMisskeyFlavoredMarkdown.vue191
-rw-r--r--packages/client/src/components/global/MkPageHeader.vue368
-rw-r--r--packages/client/src/components/global/MkSpacer.vue96
-rw-r--r--packages/client/src/components/global/MkStickyContainer.vue66
-rw-r--r--packages/client/src/components/global/MkTime.vue56
-rw-r--r--packages/client/src/components/global/MkUrl.vue89
-rw-r--r--packages/client/src/components/global/MkUserName.vue15
-rw-r--r--packages/client/src/components/global/RouterView.vue61
-rw-r--r--packages/client/src/components/global/i18n.ts42
17 files changed, 0 insertions, 1694 deletions
diff --git a/packages/client/src/components/global/MkA.vue b/packages/client/src/components/global/MkA.vue
deleted file mode 100644
index 5a0ba0d8d3..0000000000
--- a/packages/client/src/components/global/MkA.vue
+++ /dev/null
@@ -1,102 +0,0 @@
-<template>
-<a :href="to" :class="active ? activeClass : null" @click.prevent="nav" @contextmenu.prevent.stop="onContextmenu">
- <slot></slot>
-</a>
-</template>
-
-<script lang="ts" setup>
-import { inject } from 'vue';
-import * as os from '@/os';
-import copyToClipboard from '@/scripts/copy-to-clipboard';
-import { url } from '@/config';
-import { popout as popout_ } from '@/scripts/popout';
-import { i18n } from '@/i18n';
-import { useRouter } from '@/router';
-
-const props = withDefaults(defineProps<{
- to: string;
- activeClass?: null | string;
- behavior?: null | 'window' | 'browser' | 'modalWindow';
-}>(), {
- activeClass: null,
- behavior: null,
-});
-
-const router = useRouter();
-
-const active = $computed(() => {
- if (props.activeClass == null) return false;
- const resolved = router.resolve(props.to);
- if (resolved == null) return false;
- if (resolved.route.path === router.currentRoute.value.path) return true;
- if (resolved.route.name == null) return false;
- if (router.currentRoute.value.name == null) return false;
- return resolved.route.name === router.currentRoute.value.name;
-});
-
-function onContextmenu(ev) {
- const selection = window.getSelection();
- if (selection && selection.toString() !== '') return;
- os.contextMenu([{
- type: 'label',
- text: props.to,
- }, {
- icon: 'ti ti-app-window',
- text: i18n.ts.openInWindow,
- action: () => {
- os.pageWindow(props.to);
- },
- }, {
- icon: 'ti ti-player-eject',
- text: i18n.ts.showInPage,
- action: () => {
- router.push(props.to, 'forcePage');
- },
- }, null, {
- icon: 'ti ti-external-link',
- text: i18n.ts.openInNewTab,
- action: () => {
- window.open(props.to, '_blank');
- },
- }, {
- icon: 'ti ti-link',
- text: i18n.ts.copyLink,
- action: () => {
- copyToClipboard(`${url}${props.to}`);
- },
- }], ev);
-}
-
-function openWindow() {
- os.pageWindow(props.to);
-}
-
-function modalWindow() {
- os.modalPageWindow(props.to);
-}
-
-function popout() {
- popout_(props.to);
-}
-
-function nav(ev: MouseEvent) {
- if (props.behavior === 'browser') {
- location.href = props.to;
- return;
- }
-
- if (props.behavior) {
- if (props.behavior === 'window') {
- return openWindow();
- } else if (props.behavior === 'modalWindow') {
- return modalWindow();
- }
- }
-
- if (ev.shiftKey) {
- return openWindow();
- }
-
- router.push(props.to, ev.ctrlKey ? 'forcePage' : null);
-}
-</script>
diff --git a/packages/client/src/components/global/MkAcct.vue b/packages/client/src/components/global/MkAcct.vue
deleted file mode 100644
index c3e806b5fb..0000000000
--- a/packages/client/src/components/global/MkAcct.vue
+++ /dev/null
@@ -1,27 +0,0 @@
-<template>
-<span class="mk-acct">
- <span class="name">@{{ user.username }}</span>
- <span v-if="user.host || detail || $store.state.showFullAcct" class="host">@{{ user.host || host }}</span>
-</span>
-</template>
-
-<script lang="ts" setup>
-import * as misskey from 'misskey-js';
-import { toUnicode } from 'punycode/';
-import { host as hostRaw } from '@/config';
-
-defineProps<{
- user: misskey.entities.UserDetailed;
- detail?: boolean;
-}>();
-
-const host = toUnicode(hostRaw);
-</script>
-
-<style lang="scss" scoped>
-.mk-acct {
- > .host {
- opacity: 0.5;
- }
-}
-</style>
diff --git a/packages/client/src/components/global/MkAd.vue b/packages/client/src/components/global/MkAd.vue
deleted file mode 100644
index a80efb142c..0000000000
--- a/packages/client/src/components/global/MkAd.vue
+++ /dev/null
@@ -1,186 +0,0 @@
-<template>
-<div v-if="chosen" class="qiivuoyo">
- <div v-if="!showMenu" class="main" :class="chosen.place">
- <a :href="chosen.url" target="_blank">
- <img :src="chosen.imageUrl">
- <button class="_button menu" @click.prevent.stop="toggleMenu"><span class="ti ti-info-circle info-circle"></span></button>
- </a>
- </div>
- <div v-else class="menu">
- <div class="body">
- <div>Ads by {{ host }}</div>
- <!--<MkButton class="button" primary>{{ $ts._ad.like }}</MkButton>-->
- <MkButton v-if="chosen.ratio !== 0" class="button" @click="reduceFrequency">{{ $ts._ad.reduceFrequencyOfThisAd }}</MkButton>
- <button class="_textButton" @click="toggleMenu">{{ $ts._ad.back }}</button>
- </div>
- </div>
-</div>
-<div v-else></div>
-</template>
-
-<script lang="ts" setup>
-import { ref } from 'vue';
-import { instance } from '@/instance';
-import { host } from '@/config';
-import MkButton from '@/components/MkButton.vue';
-import { defaultStore } from '@/store';
-import * as os from '@/os';
-
-type Ad = (typeof instance)['ads'][number];
-
-const props = defineProps<{
- prefer: string[];
- specify?: Ad;
-}>();
-
-const showMenu = ref(false);
-const toggleMenu = (): void => {
- showMenu.value = !showMenu.value;
-};
-
-const choseAd = (): Ad | null => {
- if (props.specify) {
- return props.specify;
- }
-
- const allAds = instance.ads.map(ad => defaultStore.state.mutedAds.includes(ad.id) ? {
- ...ad,
- ratio: 0,
- } : ad);
-
- let ads = allAds.filter(ad => props.prefer.includes(ad.place));
-
- if (ads.length === 0) {
- ads = allAds.filter(ad => ad.place === 'square');
- }
-
- const lowPriorityAds = ads.filter(ad => ad.ratio === 0);
- ads = ads.filter(ad => ad.ratio !== 0);
-
- if (ads.length === 0) {
- if (lowPriorityAds.length !== 0) {
- return lowPriorityAds[Math.floor(Math.random() * lowPriorityAds.length)];
- } else {
- return null;
- }
- }
-
- const totalFactor = ads.reduce((a, b) => a + b.ratio, 0);
- const r = Math.random() * totalFactor;
-
- let stackedFactor = 0;
- for (const ad of ads) {
- if (r >= stackedFactor && r <= stackedFactor + ad.ratio) {
- return ad;
- } else {
- stackedFactor += ad.ratio;
- }
- }
-
- return null;
-};
-
-const chosen = ref(choseAd());
-
-function reduceFrequency(): void {
- if (chosen.value == null) return;
- if (defaultStore.state.mutedAds.includes(chosen.value.id)) return;
- defaultStore.push('mutedAds', chosen.value.id);
- os.success();
- chosen.value = choseAd();
- showMenu.value = false;
-}
-</script>
-
-<style lang="scss" scoped>
-.qiivuoyo {
- background-size: auto auto;
- background-image: repeating-linear-gradient(45deg, transparent, transparent 8px, var(--ad) 8px, var(--ad) 14px );
-
- > .main {
- text-align: center;
-
- > a {
- display: inline-block;
- position: relative;
- vertical-align: bottom;
-
- &:hover {
- > img {
- filter: contrast(120%);
- }
- }
-
- > img {
- display: block;
- object-fit: contain;
- margin: auto;
- border-radius: 5px;
- }
-
- > .menu {
- position: absolute;
- top: 1px;
- right: 1px;
-
- > .info-circle {
- border: 3px solid var(--panel);
- border-radius: 50%;
- background: var(--panel);
- }
- }
- }
-
- &.square {
- > a ,
- > a > img {
- max-width: min(300px, 100%);
- max-height: 300px;
- }
- }
-
- &.horizontal {
- padding: 8px;
-
- > a ,
- > a > img {
- max-width: min(600px, 100%);
- max-height: 80px;
- }
- }
-
- &.horizontal-big {
- padding: 8px;
-
- > a ,
- > a > img {
- max-width: min(600px, 100%);
- max-height: 250px;
- }
- }
-
- &.vertical {
- > a ,
- > a > img {
- max-width: min(100px, 100%);
- }
- }
- }
-
- > .menu {
- padding: 8px;
- text-align: center;
-
- > .body {
- padding: 8px;
- margin: 0 auto;
- max-width: 400px;
- border: solid 1px var(--divider);
-
- > .button {
- margin: 8px auto;
- }
- }
- }
-}
-</style>
diff --git a/packages/client/src/components/global/MkAvatar.vue b/packages/client/src/components/global/MkAvatar.vue
deleted file mode 100644
index 5f3e3c176d..0000000000
--- a/packages/client/src/components/global/MkAvatar.vue
+++ /dev/null
@@ -1,143 +0,0 @@
-<template>
-<span v-if="disableLink" v-user-preview="disablePreview ? undefined : user.id" class="eiwwqkts _noSelect" :class="{ cat: user.isCat, square: $store.state.squareAvatars }" :style="{ color }" :title="acct(user)" @click="onClick">
- <img class="inner" :src="url" decoding="async"/>
- <MkUserOnlineIndicator v-if="showIndicator" class="indicator" :user="user"/>
-</span>
-<MkA v-else v-user-preview="disablePreview ? undefined : user.id" class="eiwwqkts _noSelect" :class="{ cat: user.isCat, square: $store.state.squareAvatars }" :style="{ color }" :to="userPage(user)" :title="acct(user)" :target="target">
- <img class="inner" :src="url" decoding="async"/>
- <MkUserOnlineIndicator v-if="showIndicator" class="indicator" :user="user"/>
-</MkA>
-</template>
-
-<script lang="ts" setup>
-import { onMounted, watch } from 'vue';
-import * as misskey from 'misskey-js';
-import { getStaticImageUrl } from '@/scripts/get-static-image-url';
-import { extractAvgColorFromBlurhash } from '@/scripts/extract-avg-color-from-blurhash';
-import { acct, userPage } from '@/filters/user';
-import MkUserOnlineIndicator from '@/components/MkUserOnlineIndicator.vue';
-import { defaultStore } from '@/store';
-
-const props = withDefaults(defineProps<{
- user: misskey.entities.User;
- target?: string | null;
- disableLink?: boolean;
- disablePreview?: boolean;
- showIndicator?: boolean;
-}>(), {
- target: null,
- disableLink: false,
- disablePreview: false,
- showIndicator: false,
-});
-
-const emit = defineEmits<{
- (ev: 'click', v: MouseEvent): void;
-}>();
-
-const url = $computed(() => defaultStore.state.disableShowingAnimatedImages
- ? getStaticImageUrl(props.user.avatarUrl)
- : props.user.avatarUrl);
-
-function onClick(ev: MouseEvent) {
- emit('click', ev);
-}
-
-let color = $ref();
-
-watch(() => props.user.avatarBlurhash, () => {
- color = extractAvgColorFromBlurhash(props.user.avatarBlurhash);
-}, {
- immediate: true,
-});
-</script>
-
-<style lang="scss" scoped>
-@keyframes earwiggleleft {
- from { transform: rotate(37.6deg) skew(30deg); }
- 25% { transform: rotate(10deg) skew(30deg); }
- 50% { transform: rotate(20deg) skew(30deg); }
- 75% { transform: rotate(0deg) skew(30deg); }
- to { transform: rotate(37.6deg) skew(30deg); }
-}
-
-@keyframes earwiggleright {
- from { transform: rotate(-37.6deg) skew(-30deg); }
- 30% { transform: rotate(-10deg) skew(-30deg); }
- 55% { transform: rotate(-20deg) skew(-30deg); }
- 75% { transform: rotate(0deg) skew(-30deg); }
- to { transform: rotate(-37.6deg) skew(-30deg); }
-}
-
-.eiwwqkts {
- position: relative;
- display: inline-block;
- vertical-align: bottom;
- flex-shrink: 0;
- border-radius: 100%;
- line-height: 16px;
-
- > .inner {
- position: absolute;
- bottom: 0;
- left: 0;
- right: 0;
- top: 0;
- border-radius: 100%;
- z-index: 1;
- overflow: hidden;
- object-fit: cover;
- width: 100%;
- height: 100%;
- }
-
- > .indicator {
- position: absolute;
- z-index: 1;
- bottom: 0;
- left: 0;
- width: 20%;
- height: 20%;
- }
-
- &.square {
- border-radius: 20%;
-
- > .inner {
- border-radius: 20%;
- }
- }
-
- &.cat {
- &:before, &:after {
- background: #df548f;
- border: solid 4px currentColor;
- box-sizing: border-box;
- content: '';
- display: inline-block;
- height: 50%;
- width: 50%;
- }
-
- &:before {
- border-radius: 0 75% 75%;
- transform: rotate(37.5deg) skew(30deg);
- }
-
- &:after {
- border-radius: 75% 0 75% 75%;
- transform: rotate(-37.5deg) skew(-30deg);
- }
-
- &:hover {
- &:before {
- animation: earwiggleleft 1s infinite;
- }
-
- &:after {
- animation: earwiggleright 1s infinite;
- }
- }
- }
-}
-</style>
diff --git a/packages/client/src/components/global/MkEllipsis.vue b/packages/client/src/components/global/MkEllipsis.vue
deleted file mode 100644
index 0a46f486d6..0000000000
--- a/packages/client/src/components/global/MkEllipsis.vue
+++ /dev/null
@@ -1,34 +0,0 @@
-<template>
- <span class="mk-ellipsis">
- <span>.</span><span>.</span><span>.</span>
- </span>
-</template>
-
-<style lang="scss" scoped>
-.mk-ellipsis {
- > span {
- animation: ellipsis 1.4s infinite ease-in-out both;
-
- &:nth-child(1) {
- animation-delay: 0s;
- }
-
- &:nth-child(2) {
- animation-delay: 0.16s;
- }
-
- &:nth-child(3) {
- animation-delay: 0.32s;
- }
- }
-}
-
-@keyframes ellipsis {
- 0%, 80%, 100% {
- opacity: 1;
- }
- 40% {
- opacity: 0;
- }
-}
-</style>
diff --git a/packages/client/src/components/global/MkEmoji.vue b/packages/client/src/components/global/MkEmoji.vue
deleted file mode 100644
index ce1299a39f..0000000000
--- a/packages/client/src/components/global/MkEmoji.vue
+++ /dev/null
@@ -1,81 +0,0 @@
-<template>
-<img v-if="customEmoji" class="mk-emoji custom" :class="{ normal, noStyle }" :src="url" :alt="alt" :title="alt" decoding="async"/>
-<img v-else-if="char && !useOsNativeEmojis" class="mk-emoji" :src="url" :alt="alt" decoding="async" @pointerenter="computeTitle"/>
-<span v-else-if="char && useOsNativeEmojis" :alt="alt" @pointerenter="computeTitle">{{ char }}</span>
-<span v-else>{{ emoji }}</span>
-</template>
-
-<script lang="ts" setup>
-import { computed } from 'vue';
-import { CustomEmoji } from 'misskey-js/built/entities';
-import { getStaticImageUrl } from '@/scripts/get-static-image-url';
-import { char2twemojiFilePath, char2fluentEmojiFilePath } from '@/scripts/emoji-base';
-import { defaultStore } from '@/store';
-import { instance } from '@/instance';
-import { getEmojiName } from '@/scripts/emojilist';
-
-const props = defineProps<{
- emoji: string;
- normal?: boolean;
- noStyle?: boolean;
- customEmojis?: CustomEmoji[];
- isReaction?: boolean;
-}>();
-
-const char2path = defaultStore.state.emojiStyle === 'twemoji' ? char2twemojiFilePath : char2fluentEmojiFilePath;
-
-const isCustom = computed(() => props.emoji.startsWith(':'));
-const char = computed(() => isCustom.value ? undefined : props.emoji);
-const useOsNativeEmojis = computed(() => defaultStore.state.emojiStyle === 'native' && !props.isReaction);
-const ce = computed(() => props.customEmojis ?? instance.emojis ?? []);
-const customEmoji = computed(() => isCustom.value ? ce.value.find(x => x.name === props.emoji.substr(1, props.emoji.length - 2)) : undefined);
-const url = computed(() => {
- if (char.value) {
- return char2path(char.value);
- } else {
- const rawUrl = (customEmoji.value as CustomEmoji).url;
- return defaultStore.state.disableShowingAnimatedImages
- ? getStaticImageUrl(rawUrl)
- : rawUrl;
- }
-});
-const alt = computed(() => customEmoji.value ? `:${customEmoji.value.name}:` : char.value);
-
-// Searching from an array with 2000 items for every emoji felt like too energy-consuming, so I decided to do it lazily on pointerenter
-function computeTitle(event: PointerEvent): void {
- const title = customEmoji.value
- ? `:${customEmoji.value.name}:`
- : (getEmojiName(char.value as string) ?? char.value as string);
- (event.target as HTMLElement).title = title;
-}
-</script>
-
-<style lang="scss" scoped>
-.mk-emoji {
- height: 1.25em;
- vertical-align: -0.25em;
-
- &.custom {
- height: 2.5em;
- vertical-align: middle;
- transition: transform 0.2s ease;
-
- &:hover {
- transform: scale(1.2);
- }
-
- &.normal {
- height: 1.25em;
- vertical-align: -0.25em;
-
- &:hover {
- transform: none;
- }
- }
- }
-
- &.noStyle {
- height: auto !important;
- }
-}
-</style>
diff --git a/packages/client/src/components/global/MkError.vue b/packages/client/src/components/global/MkError.vue
deleted file mode 100644
index e135d4184b..0000000000
--- a/packages/client/src/components/global/MkError.vue
+++ /dev/null
@@ -1,36 +0,0 @@
-<template>
-<transition :name="$store.state.animation ? 'zoom' : ''" appear>
- <div class="mjndxjcg">
- <img src="https://xn--931a.moe/assets/error.jpg" class="_ghost"/>
- <p><i class="ti ti-alert-triangle"></i> {{ i18n.ts.somethingHappened }}</p>
- <MkButton class="button" @click="() => $emit('retry')">{{ i18n.ts.retry }}</MkButton>
- </div>
-</transition>
-</template>
-
-<script lang="ts" setup>
-import MkButton from '@/components/MkButton.vue';
-import { i18n } from '@/i18n';
-</script>
-
-<style lang="scss" scoped>
-.mjndxjcg {
- padding: 32px;
- text-align: center;
-
- > p {
- margin: 0 0 8px 0;
- }
-
- > .button {
- margin: 0 auto;
- }
-
- > img {
- vertical-align: bottom;
- height: 128px;
- margin-bottom: 16px;
- border-radius: 16px;
- }
-}
-</style>
diff --git a/packages/client/src/components/global/MkLoading.vue b/packages/client/src/components/global/MkLoading.vue
deleted file mode 100644
index 64e12e3b44..0000000000
--- a/packages/client/src/components/global/MkLoading.vue
+++ /dev/null
@@ -1,101 +0,0 @@
-<template>
-<div :class="[$style.root, { [$style.inline]: inline, [$style.colored]: colored, [$style.mini]: mini, [$style.em]: em }]">
- <div :class="$style.container">
- <svg :class="[$style.spinner, $style.bg]" viewBox="0 0 168 168" xmlns="http://www.w3.org/2000/svg">
- <g transform="matrix(1.125,0,0,1.125,12,12)">
- <circle cx="64" cy="64" r="64" style="fill:none;stroke:currentColor;stroke-width:21.33px;"/>
- </g>
- </svg>
- <svg :class="[$style.spinner, $style.fg]" viewBox="0 0 168 168" xmlns="http://www.w3.org/2000/svg">
- <g transform="matrix(1.125,0,0,1.125,12,12)">
- <path d="M128,64C128,28.654 99.346,0 64,0C99.346,0 128,28.654 128,64Z" style="fill:none;stroke:currentColor;stroke-width:21.33px;"/>
- </g>
- </svg>
- </div>
-</div>
-</template>
-
-<script lang="ts" setup>
-import { } from 'vue';
-
-const props = withDefaults(defineProps<{
- inline?: boolean;
- colored?: boolean;
- mini?: boolean;
- em?: boolean;
-}>(), {
- inline: false,
- colored: true,
- mini: false,
- em: false,
-});
-</script>
-
-<style lang="scss" module>
-@keyframes spinner {
- 0% {
- transform: rotate(0deg);
- }
- 100% {
- transform: rotate(360deg);
- }
-}
-
-.root {
- padding: 32px;
- text-align: center;
- cursor: wait;
-
- --size: 38px;
-
- &.colored {
- color: var(--accent);
- }
-
- &.inline {
- display: inline;
- padding: 0;
- --size: 32px;
- }
-
- &.mini {
- padding: 16px;
- --size: 32px;
- }
-
- &.em {
- display: inline-block;
- vertical-align: middle;
- padding: 0;
- --size: 1em;
- }
-}
-
-.container {
- position: relative;
- width: var(--size);
- height: var(--size);
- margin: 0 auto;
-}
-
-.spinner {
- position: absolute;
- top: 0;
- left: 0;
- width: var(--size);
- height: var(--size);
- fill-rule: evenodd;
- clip-rule: evenodd;
- stroke-linecap: round;
- stroke-linejoin: round;
- stroke-miterlimit: 1.5;
-}
-
-.bg {
- opacity: 0.275;
-}
-
-.fg {
- animation: spinner 0.5s linear infinite;
-}
-</style>
diff --git a/packages/client/src/components/global/MkMisskeyFlavoredMarkdown.vue b/packages/client/src/components/global/MkMisskeyFlavoredMarkdown.vue
deleted file mode 100644
index 70d0108e9f..0000000000
--- a/packages/client/src/components/global/MkMisskeyFlavoredMarkdown.vue
+++ /dev/null
@@ -1,191 +0,0 @@
-<template>
-<MfmCore :text="text" :plain="plain" :nowrap="nowrap" :author="author" :customEmojis="customEmojis" :isNote="isNote" class="havbbuyv" :class="{ nowrap }"/>
-</template>
-
-<script lang="ts" setup>
-import { } from 'vue';
-import MfmCore from '@/components/mfm';
-
-const props = withDefaults(defineProps<{
- text: string;
- plain?: boolean;
- nowrap?: boolean;
- author?: any;
- customEmojis?: any;
- isNote?: boolean;
-}>(), {
- plain: false,
- nowrap: false,
- author: null,
- isNote: true,
-});
-</script>
-
-<style lang="scss">
-._mfm_blur_ {
- filter: blur(6px);
- transition: filter 0.3s;
-
- &:hover {
- filter: blur(0px);
- }
-}
-
-.mfm-x2 {
- --mfm-zoom-size: 200%;
-}
-
-.mfm-x3 {
- --mfm-zoom-size: 400%;
-}
-
-.mfm-x4 {
- --mfm-zoom-size: 600%;
-}
-
-.mfm-x2, .mfm-x3, .mfm-x4 {
- font-size: var(--mfm-zoom-size);
-
- .mfm-x2, .mfm-x3, .mfm-x4 {
- /* only half effective */
- font-size: calc(var(--mfm-zoom-size) / 2 + 50%);
-
- .mfm-x2, .mfm-x3, .mfm-x4 {
- /* disabled */
- font-size: 100%;
- }
- }
-}
-
-@keyframes mfm-spin {
- 0% { transform: rotate(0deg); }
- 100% { transform: rotate(360deg); }
-}
-
-@keyframes mfm-spinX {
- 0% { transform: perspective(128px) rotateX(0deg); }
- 100% { transform: perspective(128px) rotateX(360deg); }
-}
-
-@keyframes mfm-spinY {
- 0% { transform: perspective(128px) rotateY(0deg); }
- 100% { transform: perspective(128px) rotateY(360deg); }
-}
-
-@keyframes mfm-jump {
- 0% { transform: translateY(0); }
- 25% { transform: translateY(-16px); }
- 50% { transform: translateY(0); }
- 75% { transform: translateY(-8px); }
- 100% { transform: translateY(0); }
-}
-
-@keyframes mfm-bounce {
- 0% { transform: translateY(0) scale(1, 1); }
- 25% { transform: translateY(-16px) scale(1, 1); }
- 50% { transform: translateY(0) scale(1, 1); }
- 75% { transform: translateY(0) scale(1.5, 0.75); }
- 100% { transform: translateY(0) scale(1, 1); }
-}
-
-// const val = () => `translate(${Math.floor(Math.random() * 20) - 10}px, ${Math.floor(Math.random() * 20) - 10}px)`;
-// let css = '';
-// for (let i = 0; i <= 100; i += 5) { css += `${i}% { transform: ${val()} }\n`; }
-@keyframes mfm-twitch {
- 0% { transform: translate(7px, -2px) }
- 5% { transform: translate(-3px, 1px) }
- 10% { transform: translate(-7px, -1px) }
- 15% { transform: translate(0px, -1px) }
- 20% { transform: translate(-8px, 6px) }
- 25% { transform: translate(-4px, -3px) }
- 30% { transform: translate(-4px, -6px) }
- 35% { transform: translate(-8px, -8px) }
- 40% { transform: translate(4px, 6px) }
- 45% { transform: translate(-3px, 1px) }
- 50% { transform: translate(2px, -10px) }
- 55% { transform: translate(-7px, 0px) }
- 60% { transform: translate(-2px, 4px) }
- 65% { transform: translate(3px, -8px) }
- 70% { transform: translate(6px, 7px) }
- 75% { transform: translate(-7px, -2px) }
- 80% { transform: translate(-7px, -8px) }
- 85% { transform: translate(9px, 3px) }
- 90% { transform: translate(-3px, -2px) }
- 95% { transform: translate(-10px, 2px) }
- 100% { transform: translate(-2px, -6px) }
-}
-
-// const val = () => `translate(${Math.floor(Math.random() * 6) - 3}px, ${Math.floor(Math.random() * 6) - 3}px) rotate(${Math.floor(Math.random() * 24) - 12}deg)`;
-// let css = '';
-// for (let i = 0; i <= 100; i += 5) { css += `${i}% { transform: ${val()} }\n`; }
-@keyframes mfm-shake {
- 0% { transform: translate(-3px, -1px) rotate(-8deg) }
- 5% { transform: translate(0px, -1px) rotate(-10deg) }
- 10% { transform: translate(1px, -3px) rotate(0deg) }
- 15% { transform: translate(1px, 1px) rotate(11deg) }
- 20% { transform: translate(-2px, 1px) rotate(1deg) }
- 25% { transform: translate(-1px, -2px) rotate(-2deg) }
- 30% { transform: translate(-1px, 2px) rotate(-3deg) }
- 35% { transform: translate(2px, 1px) rotate(6deg) }
- 40% { transform: translate(-2px, -3px) rotate(-9deg) }
- 45% { transform: translate(0px, -1px) rotate(-12deg) }
- 50% { transform: translate(1px, 2px) rotate(10deg) }
- 55% { transform: translate(0px, -3px) rotate(8deg) }
- 60% { transform: translate(1px, -1px) rotate(8deg) }
- 65% { transform: translate(0px, -1px) rotate(-7deg) }
- 70% { transform: translate(-1px, -3px) rotate(6deg) }
- 75% { transform: translate(0px, -2px) rotate(4deg) }
- 80% { transform: translate(-2px, -1px) rotate(3deg) }
- 85% { transform: translate(1px, -3px) rotate(-10deg) }
- 90% { transform: translate(1px, 0px) rotate(3deg) }
- 95% { transform: translate(-2px, 0px) rotate(-3deg) }
- 100% { transform: translate(2px, 1px) rotate(2deg) }
-}
-
-@keyframes mfm-rubberBand {
- from { transform: scale3d(1, 1, 1); }
- 30% { transform: scale3d(1.25, 0.75, 1); }
- 40% { transform: scale3d(0.75, 1.25, 1); }
- 50% { transform: scale3d(1.15, 0.85, 1); }
- 65% { transform: scale3d(0.95, 1.05, 1); }
- 75% { transform: scale3d(1.05, 0.95, 1); }
- to { transform: scale3d(1, 1, 1); }
-}
-
-@keyframes mfm-rainbow {
- 0% { filter: hue-rotate(0deg) contrast(150%) saturate(150%); }
- 100% { filter: hue-rotate(360deg) contrast(150%) saturate(150%); }
-}
-</style>
-
-<style lang="scss" scoped>
-.havbbuyv {
- white-space: pre-wrap;
-
- &.nowrap {
- white-space: pre;
- word-wrap: normal; // https://codeday.me/jp/qa/20190424/690106.html
- overflow: hidden;
- text-overflow: ellipsis;
- }
-
- ::v-deep(.quote) {
- display: block;
- margin: 8px;
- padding: 6px 0 6px 12px;
- color: var(--fg);
- border-left: solid 3px var(--fg);
- opacity: 0.7;
- }
-
- ::v-deep(pre) {
- font-size: 0.8em;
- }
-
- > ::v-deep(code) {
- font-size: 0.8em;
- word-break: break-all;
- padding: 4px 6px;
- }
-}
-</style>
diff --git a/packages/client/src/components/global/MkPageHeader.vue b/packages/client/src/components/global/MkPageHeader.vue
deleted file mode 100644
index a228dfe883..0000000000
--- a/packages/client/src/components/global/MkPageHeader.vue
+++ /dev/null
@@ -1,368 +0,0 @@
-<template>
-<div v-if="show" ref="el" class="fdidabkb" :class="{ slim: narrow, thin: thin_ }" :style="{ background: bg }" @click="onClick">
- <div v-if="narrow" class="buttons left">
- <MkAvatar v-if="props.displayMyAvatar && $i" class="avatar" :user="$i" :disable-preview="true"/>
- </div>
- <template v-if="metadata">
- <div v-if="!hideTitle" class="titleContainer" @click="showTabsPopup">
- <MkAvatar v-if="metadata.avatar" class="avatar" :user="metadata.avatar" :disable-preview="true" :show-indicator="true"/>
- <i v-else-if="metadata.icon" class="icon" :class="metadata.icon"></i>
-
- <div class="title">
- <MkUserName v-if="metadata.userName" :user="metadata.userName" :nowrap="true" class="title"/>
- <div v-else-if="metadata.title" class="title">{{ metadata.title }}</div>
- <div v-if="!narrow && metadata.subtitle" class="subtitle">
- {{ metadata.subtitle }}
- </div>
- <div v-if="narrow && hasTabs" class="subtitle activeTab">
- {{ tabs.find(tab => tab.key === props.tab)?.title }}
- <i class="chevron ti ti-chevron-down"></i>
- </div>
- </div>
- </div>
- <div v-if="!narrow || hideTitle" class="tabs">
- <button v-for="tab in tabs" :ref="(el) => tabRefs[tab.key] = (el as HTMLElement)" v-tooltip.noDelay="tab.title" class="tab _button" :class="{ active: tab.key != null && tab.key === props.tab }" @mousedown="(ev) => onTabMousedown(tab, ev)" @click="(ev) => onTabClick(tab, ev)">
- <i v-if="tab.icon" class="icon" :class="tab.icon"></i>
- <span v-if="!tab.iconOnly" class="title">{{ tab.title }}</span>
- </button>
- <div ref="tabHighlightEl" class="highlight"></div>
- </div>
- </template>
- <div class="buttons right">
- <template v-for="action in actions">
- <button v-tooltip.noDelay="action.text" class="_button button" :class="{ highlighted: action.highlighted }" @click.stop="action.handler" @touchstart="preventDrag"><i :class="action.icon"></i></button>
- </template>
- </div>
-</div>
-</template>
-
-<script lang="ts" setup>
-import { onMounted, onUnmounted, ref, inject, watch, nextTick } from 'vue';
-import tinycolor from 'tinycolor2';
-import { popupMenu } from '@/os';
-import { scrollToTop } from '@/scripts/scroll';
-import { globalEvents } from '@/events';
-import { injectPageMetadata } from '@/scripts/page-metadata';
-import { $i } from '@/account';
-
-type Tab = {
- key: string;
- title: string;
- icon?: string;
- iconOnly?: boolean;
- onClick?: (ev: MouseEvent) => void;
-};
-
-const props = withDefaults(defineProps<{
- tabs?: Tab[];
- tab?: string;
- actions?: {
- text: string;
- icon: string;
- highlighted?: boolean;
- handler: (ev: MouseEvent) => void;
- }[];
- thin?: boolean;
- displayMyAvatar?: boolean;
-}>(), {
- tabs: () => ([] as Tab[])
-});
-
-const emit = defineEmits<{
- (ev: 'update:tab', key: string);
-}>();
-
-const metadata = injectPageMetadata();
-
-const hideTitle = inject('shouldOmitHeaderTitle', false);
-const thin_ = props.thin || inject('shouldHeaderThin', false);
-
-const el = $ref<HTMLElement | undefined>(undefined);
-const tabRefs: Record<string, HTMLElement | null> = {};
-const tabHighlightEl = $ref<HTMLElement | null>(null);
-const bg = ref<string | undefined>(undefined);
-let narrow = $ref(false);
-const hasTabs = $computed(() => props.tabs.length > 0);
-const hasActions = $computed(() => props.actions && props.actions.length > 0);
-const show = $computed(() => {
- return !hideTitle || hasTabs || hasActions;
-});
-
-const showTabsPopup = (ev: MouseEvent) => {
- if (!hasTabs) return;
- if (!narrow) return;
- ev.preventDefault();
- ev.stopPropagation();
- const menu = props.tabs.map(tab => ({
- text: tab.title,
- icon: tab.icon,
- active: tab.key != null && tab.key === props.tab,
- action: (ev) => {
- onTabClick(tab, ev);
- },
- }));
- popupMenu(menu, (ev.currentTarget ?? ev.target) as HTMLElement);
-};
-
-const preventDrag = (ev: TouchEvent) => {
- ev.stopPropagation();
-};
-
-const onClick = () => {
- if (el) {
- scrollToTop(el as HTMLElement, { behavior: 'smooth' });
- }
-};
-
-function onTabMousedown(tab: Tab, ev: MouseEvent): void {
- // ユーザビリティの観点からmousedown時にはonClickは呼ばない
- if (tab.key) {
- emit('update:tab', tab.key);
- }
-}
-
-function onTabClick(tab: Tab, ev: MouseEvent): void {
- if (tab.onClick) {
- ev.preventDefault();
- ev.stopPropagation();
- tab.onClick(ev);
- }
- if (tab.key) {
- emit('update:tab', tab.key);
- }
-}
-
-const calcBg = () => {
- const rawBg = metadata?.bg || 'var(--bg)';
- const tinyBg = tinycolor(rawBg.startsWith('var(') ? getComputedStyle(document.documentElement).getPropertyValue(rawBg.slice(4, -1)) : rawBg);
- tinyBg.setAlpha(0.85);
- bg.value = tinyBg.toRgbString();
-};
-
-let ro: ResizeObserver | null;
-
-onMounted(() => {
- calcBg();
- globalEvents.on('themeChanged', calcBg);
-
- watch(() => [props.tab, props.tabs], () => {
- nextTick(() => {
- const tabEl = props.tab ? tabRefs[props.tab] : undefined;
- if (tabEl && tabHighlightEl && tabEl.parentElement) {
- // offsetWidth や offsetLeft は少数を丸めてしまうため getBoundingClientRect を使う必要がある
- // https://developer.mozilla.org/ja/docs/Web/API/HTMLElement/offsetWidth#%E5%80%A4
- const parentRect = tabEl.parentElement.getBoundingClientRect();
- const rect = tabEl.getBoundingClientRect();
- tabHighlightEl.style.width = rect.width + 'px';
- tabHighlightEl.style.left = (rect.left - parentRect.left) + 'px';
- }
- });
- }, {
- immediate: true,
- });
-
- if (el && el.parentElement) {
- narrow = el.parentElement.offsetWidth < 500;
- ro = new ResizeObserver((entries, observer) => {
- if (el.parentElement && document.body.contains(el as HTMLElement)) {
- narrow = el.parentElement.offsetWidth < 500;
- }
- });
- ro.observe(el.parentElement as HTMLElement);
- }
-});
-
-onUnmounted(() => {
- globalEvents.off('themeChanged', calcBg);
- if (ro) ro.disconnect();
-});
-</script>
-
-<style lang="scss" scoped>
-.fdidabkb {
- --height: 52px;
- display: flex;
- width: 100%;
- -webkit-backdrop-filter: var(--blur, blur(15px));
- backdrop-filter: var(--blur, blur(15px));
- border-bottom: solid 0.5px var(--divider);
- contain: strict;
- height: var(--height);
-
- &.thin {
- --height: 42px;
-
- > .buttons {
- > .button {
- font-size: 0.9em;
- }
- }
- }
-
- &.slim {
- text-align: center;
-
- > .titleContainer {
- flex: 1;
- margin: 0 auto;
-
- > *:first-child {
- margin-left: auto;
- }
-
- > *:last-child {
- margin-right: auto;
- }
- }
- }
-
- > .buttons {
- --margin: 8px;
- display: flex;
- align-items: center;
- min-width: var(--height);
- height: var(--height);
- margin: 0 var(--margin);
-
- &.left {
- margin-right: auto;
-
- > .avatar {
- $size: 32px;
- display: inline-block;
- width: $size;
- height: $size;
- vertical-align: bottom;
- margin: 0 8px;
- pointer-events: none;
- }
- }
-
- &.right {
- margin-left: auto;
- }
-
- &:empty {
- width: var(--height);
- }
-
- > .button {
- display: flex;
- align-items: center;
- justify-content: center;
- height: calc(var(--height) - (var(--margin) * 2));
- width: calc(var(--height) - (var(--margin) * 2));
- box-sizing: border-box;
- position: relative;
- border-radius: 5px;
-
- &:hover {
- background: rgba(0, 0, 0, 0.05);
- }
-
- &.highlighted {
- color: var(--accent);
- }
- }
-
- > .fullButton {
- & + .fullButton {
- margin-left: 12px;
- }
- }
- }
-
- > .titleContainer {
- display: flex;
- align-items: center;
- max-width: 400px;
- overflow: auto;
- white-space: nowrap;
- text-align: left;
- font-weight: bold;
- flex-shrink: 0;
- margin-left: 24px;
-
- > .avatar {
- $size: 32px;
- display: inline-block;
- width: $size;
- height: $size;
- vertical-align: bottom;
- margin: 0 8px;
- pointer-events: none;
- }
-
- > .icon {
- margin-right: 8px;
- width: 16px;
- text-align: center;
- }
-
- > .title {
- min-width: 0;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- line-height: 1.1;
-
- > .subtitle {
- opacity: 0.6;
- font-size: 0.8em;
- font-weight: normal;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
-
- &.activeTab {
- text-align: center;
-
- > .chevron {
- display: inline-block;
- margin-left: 6px;
- }
- }
- }
- }
- }
-
- > .tabs {
- position: relative;
- margin-left: 16px;
- font-size: 0.8em;
- overflow: auto;
- white-space: nowrap;
-
- > .tab {
- display: inline-block;
- position: relative;
- padding: 0 10px;
- height: 100%;
- font-weight: normal;
- opacity: 0.7;
-
- &:hover {
- opacity: 1;
- }
-
- &.active {
- opacity: 1;
- }
-
- > .icon + .title {
- margin-left: 8px;
- }
- }
-
- > .highlight {
- position: absolute;
- bottom: 0;
- height: 3px;
- background: var(--accent);
- border-radius: 999px;
- transition: all 0.2s ease;
- pointer-events: none;
- }
- }
-}
-</style>
diff --git a/packages/client/src/components/global/MkSpacer.vue b/packages/client/src/components/global/MkSpacer.vue
deleted file mode 100644
index b3a42d77e7..0000000000
--- a/packages/client/src/components/global/MkSpacer.vue
+++ /dev/null
@@ -1,96 +0,0 @@
-<template>
-<div ref="root" :class="$style.root" :style="{ padding: margin + 'px' }">
- <div ref="content" :class="$style.content">
- <slot></slot>
- </div>
-</div>
-</template>
-
-<script lang="ts" setup>
-import { inject, onMounted, onUnmounted, ref } from 'vue';
-import { deviceKind } from '@/scripts/device-kind';
-
-const props = withDefaults(defineProps<{
- contentMax?: number | null;
- marginMin?: number;
- marginMax?: number;
-}>(), {
- contentMax: null,
- marginMin: 12,
- marginMax: 24,
-});
-
-let ro: ResizeObserver;
-let root = $ref<HTMLElement>();
-let content = $ref<HTMLElement>();
-let margin = $ref(0);
-const widthHistory = [null, null] as [number | null, number | null];
-const heightHistory = [null, null] as [number | null, number | null];
-const shouldSpacerMin = inject('shouldSpacerMin', false);
-
-const adjust = (rect: { width: number; height: number; }) => {
- if (shouldSpacerMin || deviceKind === 'smartphone') {
- margin = props.marginMin;
- return;
- }
-
- if (rect.width > (props.contentMax ?? 0) || (rect.width > 360 && window.innerWidth > 400)) {
- margin = props.marginMax;
- } else {
- margin = props.marginMin;
- }
-};
-
-onMounted(() => {
- ro = new ResizeObserver((entries) => {
- /* iOSが対応していない
- adjust({
- width: entries[0].borderBoxSize[0].inlineSize,
- height: entries[0].borderBoxSize[0].blockSize,
- });
- */
-
- const width = root!.offsetWidth;
- const height = root!.offsetHeight;
-
- //#region Prevent infinite resizing
- // https://github.com/misskey-dev/misskey/issues/9076
- const pastWidth = widthHistory.pop();
- widthHistory.unshift(width);
- const pastHeight = heightHistory.pop();
- heightHistory.unshift(height);
-
-
- if (pastWidth === width && pastHeight === height) {
- return;
- }
- //#endregion
-
- adjust({
- width,
- height,
- });
- });
- ro.observe(root!);
-
- if (props.contentMax) {
- content!.style.maxWidth = `${props.contentMax}px`;
- }
-});
-
-onUnmounted(() => {
- ro.disconnect();
-});
-</script>
-
-<style lang="scss" module>
-.root {
- box-sizing: border-box;
- width: 100%;
-}
-
-.content {
- margin: 0 auto;
- container-type: inline-size;
-}
-</style>
diff --git a/packages/client/src/components/global/MkStickyContainer.vue b/packages/client/src/components/global/MkStickyContainer.vue
deleted file mode 100644
index 44f4f065a6..0000000000
--- a/packages/client/src/components/global/MkStickyContainer.vue
+++ /dev/null
@@ -1,66 +0,0 @@
-<template>
-<div ref="rootEl">
- <div ref="headerEl">
- <slot name="header"></slot>
- </div>
- <div ref="bodyEl" :data-sticky-container-header-height="headerHeight">
- <slot></slot>
- </div>
-</div>
-</template>
-
-<script lang="ts">
-// なんか動かない
-//const CURRENT_STICKY_TOP = Symbol('CURRENT_STICKY_TOP');
-const CURRENT_STICKY_TOP = 'CURRENT_STICKY_TOP';
-</script>
-
-<script lang="ts" setup>
-import { onMounted, onUnmounted, provide, inject, Ref, ref, watch } from 'vue';
-
-const rootEl = $ref<HTMLElement>();
-const headerEl = $ref<HTMLElement>();
-const bodyEl = $ref<HTMLElement>();
-
-let headerHeight = $ref<string | undefined>();
-let childStickyTop = $ref(0);
-const parentStickyTop = inject<Ref<number>>(CURRENT_STICKY_TOP, ref(0));
-provide(CURRENT_STICKY_TOP, $$(childStickyTop));
-
-const calc = () => {
- childStickyTop = parentStickyTop.value + headerEl.offsetHeight;
- headerHeight = headerEl.offsetHeight.toString();
-};
-
-const observer = new ResizeObserver(() => {
- window.setTimeout(() => {
- calc();
- }, 100);
-});
-
-onMounted(() => {
- calc();
-
- watch(parentStickyTop, calc);
-
- watch($$(childStickyTop), () => {
- bodyEl.style.setProperty('--stickyTop', `${childStickyTop}px`);
- }, {
- immediate: true,
- });
-
- headerEl.style.position = 'sticky';
- headerEl.style.top = 'var(--stickyTop, 0)';
- headerEl.style.zIndex = '1000';
-
- observer.observe(headerEl);
-});
-
-onUnmounted(() => {
- observer.disconnect();
-});
-</script>
-
-<style lang="scss" module>
-
-</style>
diff --git a/packages/client/src/components/global/MkTime.vue b/packages/client/src/components/global/MkTime.vue
deleted file mode 100644
index f72b153f56..0000000000
--- a/packages/client/src/components/global/MkTime.vue
+++ /dev/null
@@ -1,56 +0,0 @@
-<template>
-<time :title="absolute">
- <template v-if="mode === 'relative'">{{ relative }}</template>
- <template v-else-if="mode === 'absolute'">{{ absolute }}</template>
- <template v-else-if="mode === 'detail'">{{ absolute }} ({{ relative }})</template>
-</time>
-</template>
-
-<script lang="ts" setup>
-import { onUnmounted } from 'vue';
-import { i18n } from '@/i18n';
-
-const props = withDefaults(defineProps<{
- time: Date | string;
- mode?: 'relative' | 'absolute' | 'detail';
-}>(), {
- mode: 'relative',
-});
-
-const _time = typeof props.time === 'string' ? new Date(props.time) : props.time;
-const absolute = _time.toLocaleString();
-
-let now = $shallowRef(new Date());
-const relative = $computed(() => {
- const ago = (now.getTime() - _time.getTime()) / 1000/*ms*/;
- return (
- ago >= 31536000 ? i18n.t('_ago.yearsAgo', { n: Math.round(ago / 31536000).toString() }) :
- ago >= 2592000 ? i18n.t('_ago.monthsAgo', { n: Math.round(ago / 2592000).toString() }) :
- ago >= 604800 ? i18n.t('_ago.weeksAgo', { n: Math.round(ago / 604800).toString() }) :
- ago >= 86400 ? i18n.t('_ago.daysAgo', { n: Math.round(ago / 86400).toString() }) :
- ago >= 3600 ? i18n.t('_ago.hoursAgo', { n: Math.round(ago / 3600).toString() }) :
- ago >= 60 ? i18n.t('_ago.minutesAgo', { n: (~~(ago / 60)).toString() }) :
- ago >= 10 ? i18n.t('_ago.secondsAgo', { n: (~~(ago % 60)).toString() }) :
- ago >= -1 ? i18n.ts._ago.justNow :
- i18n.ts._ago.future);
-});
-
-function tick() {
- // TODO: パフォーマンス向上のため、このコンポーネントが画面内に表示されている場合のみ更新する
- now = new Date();
-
- tickId = window.setTimeout(() => {
- window.requestAnimationFrame(tick);
- }, 10000);
-}
-
-let tickId: number;
-
-if (props.mode === 'relative' || props.mode === 'detail') {
- tickId = window.requestAnimationFrame(tick);
-
- onUnmounted(() => {
- window.cancelAnimationFrame(tickId);
- });
-}
-</script>
diff --git a/packages/client/src/components/global/MkUrl.vue b/packages/client/src/components/global/MkUrl.vue
deleted file mode 100644
index 9f5be96224..0000000000
--- a/packages/client/src/components/global/MkUrl.vue
+++ /dev/null
@@ -1,89 +0,0 @@
-<template>
-<component
- :is="self ? 'MkA' : 'a'" ref="el" class="ieqqeuvs _link" :[attr]="self ? props.url.substring(local.length) : props.url" :rel="rel" :target="target"
- @contextmenu.stop="() => {}"
->
- <template v-if="!self">
- <span class="schema">{{ schema }}//</span>
- <span class="hostname">{{ hostname }}</span>
- <span v-if="port != ''" class="port">:{{ port }}</span>
- </template>
- <template v-if="pathname === '/' && self">
- <span class="self">{{ hostname }}</span>
- </template>
- <span v-if="pathname != ''" class="pathname">{{ self ? pathname.substring(1) : pathname }}</span>
- <span class="query">{{ query }}</span>
- <span class="hash">{{ hash }}</span>
- <i v-if="target === '_blank'" class="ti ti-external-link icon"></i>
-</component>
-</template>
-
-<script lang="ts" setup>
-import { defineAsyncComponent, ref } from 'vue';
-import { toUnicode as decodePunycode } from 'punycode/';
-import { url as local } from '@/config';
-import * as os from '@/os';
-import { useTooltip } from '@/scripts/use-tooltip';
-import { safeURIDecode } from '@/scripts/safe-uri-decode';
-
-const props = defineProps<{
- url: string;
- rel?: string;
-}>();
-
-const self = props.url.startsWith(local);
-const url = new URL(props.url);
-const el = ref();
-
-useTooltip(el, (showing) => {
- os.popup(defineAsyncComponent(() => import('@/components/MkUrlPreviewPopup.vue')), {
- showing,
- url: props.url,
- source: el.value,
- }, {}, 'closed');
-});
-
-const schema = url.protocol;
-const hostname = decodePunycode(url.hostname);
-const port = url.port;
-const pathname = safeURIDecode(url.pathname);
-const query = safeURIDecode(url.search);
-const hash = safeURIDecode(url.hash);
-const attr = self ? 'to' : 'href';
-const target = self ? null : '_blank';
-</script>
-
-<style lang="scss" scoped>
-.ieqqeuvs {
- word-break: break-all;
-
- > .icon {
- padding-left: 2px;
- font-size: .9em;
- }
-
- > .self {
- font-weight: bold;
- }
-
- > .schema {
- opacity: 0.5;
- }
-
- > .hostname {
- font-weight: bold;
- }
-
- > .pathname {
- opacity: 0.8;
- }
-
- > .query {
- opacity: 0.5;
- }
-
- > .hash {
- font-style: italic;
- }
-}
-</style>
diff --git a/packages/client/src/components/global/MkUserName.vue b/packages/client/src/components/global/MkUserName.vue
deleted file mode 100644
index 090de3df30..0000000000
--- a/packages/client/src/components/global/MkUserName.vue
+++ /dev/null
@@ -1,15 +0,0 @@
-<template>
-<Mfm :text="user.name || user.username" :plain="true" :nowrap="nowrap" :custom-emojis="user.emojis"/>
-</template>
-
-<script lang="ts" setup>
-import { } from 'vue';
-import * as misskey from 'misskey-js';
-
-const props = withDefaults(defineProps<{
- user: misskey.entities.User;
- nowrap?: boolean;
-}>(), {
- nowrap: true,
-});
-</script>
diff --git a/packages/client/src/components/global/RouterView.vue b/packages/client/src/components/global/RouterView.vue
deleted file mode 100644
index e21a57471c..0000000000
--- a/packages/client/src/components/global/RouterView.vue
+++ /dev/null
@@ -1,61 +0,0 @@
-<template>
-<KeepAlive :max="defaultStore.state.numberOfPageCache">
- <Suspense>
- <component :is="currentPageComponent" :key="key" v-bind="Object.fromEntries(currentPageProps)"/>
-
- <template #fallback>
- <MkLoading/>
- </template>
- </Suspense>
-</KeepAlive>
-</template>
-
-<script lang="ts" setup>
-import { inject, nextTick, onBeforeUnmount, onMounted, onUnmounted, provide, watch } from 'vue';
-import { Resolved, Router } from '@/nirax';
-import { defaultStore } from '@/store';
-
-const props = defineProps<{
- router?: Router;
-}>();
-
-const router = props.router ?? inject('router');
-
-if (router == null) {
- throw new Error('no router provided');
-}
-
-const currentDepth = inject('routerCurrentDepth', 0);
-provide('routerCurrentDepth', currentDepth + 1);
-
-function resolveNested(current: Resolved, d = 0): Resolved | null {
- if (d === currentDepth) {
- return current;
- } else {
- if (current.child) {
- return resolveNested(current.child, d + 1);
- } else {
- return null;
- }
- }
-}
-
-const current = resolveNested(router.current)!;
-let currentPageComponent = $shallowRef(current.route.component);
-let currentPageProps = $ref(current.props);
-let key = $ref(current.route.path + JSON.stringify(Object.fromEntries(current.props)));
-
-function onChange({ resolved, key: newKey }) {
- const current = resolveNested(resolved);
- if (current == null) return;
- currentPageComponent = current.route.component;
- currentPageProps = current.props;
- key = current.route.path + JSON.stringify(Object.fromEntries(current.props));
-}
-
-router.addListener('change', onChange);
-
-onBeforeUnmount(() => {
- router.removeListener('change', onChange);
-});
-</script>
diff --git a/packages/client/src/components/global/i18n.ts b/packages/client/src/components/global/i18n.ts
deleted file mode 100644
index 1fd293ba10..0000000000
--- a/packages/client/src/components/global/i18n.ts
+++ /dev/null
@@ -1,42 +0,0 @@
-import { h, defineComponent } from 'vue';
-
-export default defineComponent({
- props: {
- src: {
- type: String,
- required: true,
- },
- tag: {
- type: String,
- required: false,
- default: 'span',
- },
- textTag: {
- type: String,
- required: false,
- default: null,
- },
- },
- render() {
- let str = this.src;
- const parsed = [] as (string | { arg: string; })[];
- while (true) {
- const nextBracketOpen = str.indexOf('{');
- const nextBracketClose = str.indexOf('}');
-
- if (nextBracketOpen === -1) {
- parsed.push(str);
- break;
- } else {
- if (nextBracketOpen > 0) parsed.push(str.substr(0, nextBracketOpen));
- parsed.push({
- arg: str.substring(nextBracketOpen + 1, nextBracketClose),
- });
- }
-
- str = str.substr(nextBracketClose + 1);
- }
-
- return h(this.tag, parsed.map(x => typeof x === 'string' ? (this.textTag ? h(this.textTag, x) : x) : this.$slots[x.arg]()));
- },
-});