Merge remote-tracking branch 'misskey/master' into feature/2024.9.0
This commit is contained in:
commit
f00576bce6
564 changed files with 19993 additions and 8169 deletions
|
|
@ -17,7 +17,7 @@ export type MkABehavior = 'window' | 'browser' | null;
|
|||
import { computed, inject, shallowRef } from 'vue';
|
||||
import * as os from '@/os.js';
|
||||
import { copyToClipboard } from '@/scripts/copy-to-clipboard.js';
|
||||
import { url } from '@/config.js';
|
||||
import { url } from '@@/js/config.js';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import { useRouter } from '@/router/supplier.js';
|
||||
|
||||
|
|
|
|||
|
|
@ -4,11 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
-->
|
||||
|
||||
<template>
|
||||
<MkCondensedLine v-if="defaultStore.state.enableCondensedLineForAcct" :minScale="2 / 3">
|
||||
<span>@{{ user.username }}</span>
|
||||
<span v-if="user.host || detail || defaultStore.state.showFullAcct" style="opacity: 0.5;">@{{ user.host || host }}</span>
|
||||
</MkCondensedLine>
|
||||
<span v-else>
|
||||
<span>
|
||||
<span>@{{ user.username }}</span>
|
||||
<span v-if="user.host || detail || defaultStore.state.showFullAcct" style="opacity: 0.5;">@{{ user.host || host }}</span>
|
||||
</span>
|
||||
|
|
@ -17,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<script lang="ts" setup>
|
||||
import * as Misskey from 'misskey-js';
|
||||
import { toUnicode } from 'punycode/';
|
||||
import { host as hostRaw } from '@/config.js';
|
||||
import { host as hostRaw } from '@@/js/config.js';
|
||||
import { defaultStore } from '@/store.js';
|
||||
|
||||
defineProps<{
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
import { ref, computed } from 'vue';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import { instance } from '@/instance.js';
|
||||
import { url as local, host } from '@/config.js';
|
||||
import { url as local, host } from '@@/js/config.js';
|
||||
import MkButton from '@/components/MkButton.vue';
|
||||
import { defaultStore } from '@/store.js';
|
||||
import * as os from '@/os.js';
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<template v-if="showDecoration">
|
||||
<img
|
||||
v-for="decoration in decorations ?? user.avatarDecorations"
|
||||
:class="[$style.decoration]"
|
||||
:class="[$style.decoration, { [$style.decorationBlink]: decoration.blink }]"
|
||||
:src="getDecorationUrl(decoration)"
|
||||
:style="{
|
||||
rotate: getDecorationAngle(decoration),
|
||||
|
|
@ -43,10 +43,10 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<script lang="ts" setup>
|
||||
import { watch, ref, computed } from 'vue';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import { extractAvgColorFromBlurhash } from '@@/js/extract-avg-color-from-blurhash.js';
|
||||
import MkImgWithBlurhash from '../MkImgWithBlurhash.vue';
|
||||
import MkA from './MkA.vue';
|
||||
import { getStaticImageUrl } from '@/scripts/media-proxy.js';
|
||||
import { extractAvgColorFromBlurhash } from '@/scripts/extract-avg-color-from-blurhash.js';
|
||||
import { acct, userPage } from '@/filters/user.js';
|
||||
import MkUserOnlineIndicator from '@/components/MkUserOnlineIndicator.vue';
|
||||
import { defaultStore } from '@/store.js';
|
||||
|
|
@ -61,7 +61,7 @@ const props = withDefaults(defineProps<{
|
|||
link?: boolean;
|
||||
preview?: boolean;
|
||||
indicator?: boolean;
|
||||
decorations?: Omit<Misskey.entities.UserDetailed['avatarDecorations'][number], 'id'>[];
|
||||
decorations?: (Omit<Misskey.entities.UserDetailed['avatarDecorations'][number], 'id'> & { blink?: boolean; })[];
|
||||
forceShowDecoration?: boolean;
|
||||
}>(), {
|
||||
target: null,
|
||||
|
|
@ -336,4 +336,17 @@ watch(() => props.user.avatarBlurhash, () => {
|
|||
width: 200%;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.decorationBlink {
|
||||
animation: blink 1s infinite;
|
||||
}
|
||||
|
||||
@keyframes blink {
|
||||
0%, 100% {
|
||||
filter: brightness(2);
|
||||
}
|
||||
50% {
|
||||
filter: brightness(1);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
|
||||
<template>
|
||||
<span :class="$style.container">
|
||||
<span ref="content" :class="$style.content">
|
||||
<span ref="content" :class="$style.content" :style="{ maxWidth: `${100 / minScale}%` }">
|
||||
<slot/>
|
||||
</span>
|
||||
</span>
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ import { copyToClipboard } from '@/scripts/copy-to-clipboard.js';
|
|||
import * as sound from '@/scripts/sound.js';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import MkCustomEmojiDetailedDialog from '@/components/MkCustomEmojiDetailedDialog.vue';
|
||||
import type { MenuItem } from '@/types/menu.js';
|
||||
|
||||
const props = defineProps<{
|
||||
name: string;
|
||||
|
|
@ -86,7 +87,10 @@ const errored = ref(url.value == null);
|
|||
function onClick(ev: MouseEvent) {
|
||||
if (props.menu) {
|
||||
ev.stopPropagation();
|
||||
os.popupMenu([{
|
||||
|
||||
const menuItems: MenuItem[] = [];
|
||||
|
||||
menuItems.push({
|
||||
type: 'label',
|
||||
text: `:${props.name}:`,
|
||||
}, {
|
||||
|
|
@ -96,14 +100,20 @@ function onClick(ev: MouseEvent) {
|
|||
copyToClipboard(`:${props.name}:`);
|
||||
os.success();
|
||||
},
|
||||
}, ...(props.menuReaction && react ? [{
|
||||
text: i18n.ts.doReaction,
|
||||
icon: 'ph-smiley ph-bold ph-lg',
|
||||
action: () => {
|
||||
react(`:${props.name}:`);
|
||||
sound.playMisskeySfx('reaction');
|
||||
},
|
||||
}] : []), {
|
||||
});
|
||||
|
||||
if (props.menuReaction && react) {
|
||||
menuItems.push({
|
||||
text: i18n.ts.doReaction,
|
||||
icon: 'ph-smiley ph-bold ph-lg',
|
||||
action: () => {
|
||||
react(`:${props.name}:`);
|
||||
sound.playMisskeySfx('reaction');
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
menuItems.push({
|
||||
text: i18n.ts.info,
|
||||
icon: 'ti ti-info-circle',
|
||||
action: async () => {
|
||||
|
|
@ -115,7 +125,9 @@ function onClick(ev: MouseEvent) {
|
|||
closed: () => dispose(),
|
||||
});
|
||||
},
|
||||
}], ev.currentTarget ?? ev.target);
|
||||
});
|
||||
|
||||
os.popupMenu(menuItems, ev.currentTarget ?? ev.target);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -10,13 +10,14 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
|
||||
<script lang="ts" setup>
|
||||
import { computed, inject } from 'vue';
|
||||
import { char2fluentEmojiFilePath, char2twemojiFilePath, char2tossfaceFilePath } from '@/scripts/emoji-base.js';
|
||||
import { colorizeEmoji, getEmojiName } from '@@/js/emojilist.js';
|
||||
import { char2fluentEmojiFilePath, char2twemojiFilePath, char2tossfaceFilePath } from '@@/js/emoji-base.js';
|
||||
import { defaultStore } from '@/store.js';
|
||||
import { colorizeEmoji, getEmojiName } from '@/scripts/emojilist.js';
|
||||
import * as os from '@/os.js';
|
||||
import { copyToClipboard } from '@/scripts/copy-to-clipboard.js';
|
||||
import * as sound from '@/scripts/sound.js';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import type { MenuItem } from '@/types/menu.js';
|
||||
|
||||
const props = defineProps<{
|
||||
emoji: string;
|
||||
|
|
@ -40,7 +41,10 @@ function computeTitle(event: PointerEvent): void {
|
|||
function onClick(ev: MouseEvent) {
|
||||
if (props.menu) {
|
||||
ev.stopPropagation();
|
||||
os.popupMenu([{
|
||||
|
||||
const menuItems: MenuItem[] = [];
|
||||
|
||||
menuItems.push({
|
||||
type: 'label',
|
||||
text: props.emoji,
|
||||
}, {
|
||||
|
|
@ -50,14 +54,20 @@ function onClick(ev: MouseEvent) {
|
|||
copyToClipboard(props.emoji);
|
||||
os.success();
|
||||
},
|
||||
}, ...(props.menuReaction && react ? [{
|
||||
text: i18n.ts.doReaction,
|
||||
icon: 'ph-smiley ph-bold ph-lg',
|
||||
action: () => {
|
||||
react(props.emoji);
|
||||
sound.playMisskeySfx('reaction');
|
||||
},
|
||||
}] : [])], ev.currentTarget ?? ev.target);
|
||||
});
|
||||
|
||||
if (props.menuReaction && react) {
|
||||
menuItems.push({
|
||||
text: i18n.ts.doReaction,
|
||||
icon: 'ph-smiley ph-bold ph-lg',
|
||||
action: () => {
|
||||
react(props.emoji);
|
||||
sound.playMisskeySfx('reaction');
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
os.popupMenu(menuItems, ev.currentTarget ?? ev.target);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -2,16 +2,15 @@
|
|||
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||
|
||||
import { StoryObj } from '@storybook/vue3';
|
||||
import { expect, within } from '@storybook/test';
|
||||
import MkMisskeyFlavoredMarkdown from './MkMisskeyFlavoredMarkdown.js';
|
||||
import MkMfm from './MkMfm.js';
|
||||
export const Default = {
|
||||
render(args) {
|
||||
return {
|
||||
components: {
|
||||
MkMisskeyFlavoredMarkdown,
|
||||
MkMfm,
|
||||
},
|
||||
setup() {
|
||||
return {
|
||||
|
|
@ -25,7 +24,7 @@ export const Default = {
|
|||
};
|
||||
},
|
||||
},
|
||||
template: '<MkMisskeyFlavoredMarkdown v-bind="props" />',
|
||||
template: '<MkMfm v-bind="props" />',
|
||||
};
|
||||
},
|
||||
async play({ canvasElement, args }) {
|
||||
|
|
@ -54,25 +53,25 @@ export const Default = {
|
|||
parameters: {
|
||||
layout: 'centered',
|
||||
},
|
||||
} satisfies StoryObj<typeof MkMisskeyFlavoredMarkdown>;
|
||||
} satisfies StoryObj<typeof MkMfm>;
|
||||
export const Plain = {
|
||||
...Default,
|
||||
args: {
|
||||
...Default.args,
|
||||
plain: true,
|
||||
},
|
||||
} satisfies StoryObj<typeof MkMisskeyFlavoredMarkdown>;
|
||||
} satisfies StoryObj<typeof MkMfm>;
|
||||
export const Nowrap = {
|
||||
...Default,
|
||||
args: {
|
||||
...Default.args,
|
||||
nowrap: true,
|
||||
},
|
||||
} satisfies StoryObj<typeof MkMisskeyFlavoredMarkdown>;
|
||||
} satisfies StoryObj<typeof MkMfm>;
|
||||
export const IsNotNote = {
|
||||
...Default,
|
||||
args: {
|
||||
...Default.args,
|
||||
isNote: false,
|
||||
},
|
||||
} satisfies StoryObj<typeof MkMisskeyFlavoredMarkdown>;
|
||||
} satisfies StoryObj<typeof MkMfm>;
|
||||
|
|
@ -18,10 +18,15 @@ import MkCodeInline from '@/components/MkCodeInline.vue';
|
|||
import MkGoogle from '@/components/MkGoogle.vue';
|
||||
import MkSparkle from '@/components/MkSparkle.vue';
|
||||
import MkA, { MkABehavior } from '@/components/global/MkA.vue';
|
||||
import { host } from '@/config.js';
|
||||
import { host } from '@@/js/config.js';
|
||||
import { defaultStore } from '@/store.js';
|
||||
import { nyaize as doNyaize } from '@/scripts/nyaize.js';
|
||||
import { safeParseFloat } from '@/scripts/safe-parse.js';
|
||||
|
||||
function safeParseFloat(str: unknown): number | null {
|
||||
if (typeof str !== 'string' || str === '') return null;
|
||||
const num = parseFloat(str);
|
||||
if (isNaN(num)) return null;
|
||||
return num;
|
||||
}
|
||||
|
||||
const QUOTE_STYLE = `
|
||||
display: block;
|
||||
|
|
@ -92,7 +97,7 @@ export default function (props: MfmProps, { emit }: { emit: SetupContext<MfmEven
|
|||
case 'text': {
|
||||
let text = token.props.text.replace(/(\r\n|\n|\r)/g, '\n');
|
||||
if (!disableNyaize && shouldNyaize) {
|
||||
text = doNyaize(text);
|
||||
text = Misskey.nyaize(text);
|
||||
}
|
||||
|
||||
if (!props.plain) {
|
||||
|
|
@ -340,14 +345,14 @@ export default function (props: MfmProps, { emit }: { emit: SetupContext<MfmEven
|
|||
const child = token.children[0];
|
||||
let text = child.type === 'text' ? child.props.text : '';
|
||||
if (!disableNyaize && shouldNyaize) {
|
||||
text = doNyaize(text);
|
||||
text = Misskey.nyaize(text);
|
||||
}
|
||||
return h('ruby', {}, [text.split(' ')[0], h('rt', text.split(' ')[1])]);
|
||||
} else {
|
||||
const rt = token.children.at(-1)!;
|
||||
let text = rt.type === 'text' ? rt.props.text : '';
|
||||
if (!disableNyaize && shouldNyaize) {
|
||||
text = doNyaize(text);
|
||||
text = Misskey.nyaize(text);
|
||||
}
|
||||
return h('ruby', {}, [...genEl(token.children.slice(0, token.children.length - 1), scale), h('rt', text.trim())]);
|
||||
}
|
||||
|
|
@ -459,7 +464,6 @@ export default function (props: MfmProps, { emit }: { emit: SetupContext<MfmEven
|
|||
}
|
||||
|
||||
case 'emojiCode': {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (props.author?.host == null) {
|
||||
return [h(MkCustomEmoji, {
|
||||
key: Math.random(),
|
||||
|
|
@ -53,7 +53,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
import { onMounted, onUnmounted, ref, inject, shallowRef, computed } from 'vue';
|
||||
import tinycolor from 'tinycolor2';
|
||||
import XTabs, { Tab } from './MkPageHeader.tabs.vue';
|
||||
import { scrollToTop } from '@/scripts/scroll.js';
|
||||
import { scrollToTop } from '@@/js/scroll.js';
|
||||
import { globalEvents } from '@/events.js';
|
||||
import { injectReactiveMetadata } from '@/scripts/page-metadata.js';
|
||||
import { $i, openAccountMenu as openAccountMenu_ } from '@/account.js';
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
ref="bodyEl"
|
||||
:data-sticky-container-header-height="headerHeight"
|
||||
:data-sticky-container-footer-height="footerHeight"
|
||||
style="position: relative; z-index: 0;"
|
||||
>
|
||||
<slot></slot>
|
||||
</div>
|
||||
|
|
@ -24,7 +25,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<script lang="ts" setup>
|
||||
import { onMounted, onUnmounted, provide, inject, Ref, ref, watch, shallowRef } from 'vue';
|
||||
|
||||
import { CURRENT_STICKY_BOTTOM, CURRENT_STICKY_TOP } from '@/const.js';
|
||||
import { CURRENT_STICKY_BOTTOM, CURRENT_STICKY_TOP } from '@@/js/const.js';
|
||||
|
||||
const rootEl = shallowRef<HTMLElement>();
|
||||
const headerEl = shallowRef<HTMLElement>();
|
||||
|
|
@ -83,14 +84,14 @@ onMounted(() => {
|
|||
if (headerEl.value != null) {
|
||||
headerEl.value.style.position = 'sticky';
|
||||
headerEl.value.style.top = 'var(--stickyTop, 0)';
|
||||
headerEl.value.style.zIndex = '1000';
|
||||
headerEl.value.style.zIndex = '1';
|
||||
observer.observe(headerEl.value);
|
||||
}
|
||||
|
||||
if (footerEl.value != null) {
|
||||
footerEl.value.style.position = 'sticky';
|
||||
footerEl.value.style.bottom = 'var(--stickyBottom, 0)';
|
||||
footerEl.value.style.zIndex = '1000';
|
||||
footerEl.value.style.zIndex = '1';
|
||||
observer.observe(footerEl.value);
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import { expect } from '@storybook/test';
|
|||
import { StoryObj } from '@storybook/vue3';
|
||||
import MkTime from './MkTime.vue';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import { dateTimeFormat } from '@/scripts/intl-const.js';
|
||||
import { dateTimeFormat } from '@@/js/intl-const.js';
|
||||
const now = new Date('2023-04-01T00:00:00.000Z');
|
||||
const future = new Date('2024-04-01T00:00:00.000Z');
|
||||
const oneHourAgo = new Date(now.getTime() - 3600000);
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
import isChromatic from 'chromatic/isChromatic';
|
||||
import { onMounted, onUnmounted, ref, computed } from 'vue';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import { dateTimeFormat } from '@/scripts/intl-const.js';
|
||||
import { dateTimeFormat } from '@@/js/intl-const.js';
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
time: Date | string | number | null;
|
||||
|
|
|
|||
|
|
@ -29,14 +29,21 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<script lang="ts" setup>
|
||||
import { defineAsyncComponent, ref } from 'vue';
|
||||
import { toUnicode as decodePunycode } from 'punycode/';
|
||||
import { url as local } from '@/config.js';
|
||||
import { url as local } from '@@/js/config.js';
|
||||
import * as os from '@/os.js';
|
||||
import { useTooltip } from '@/scripts/use-tooltip.js';
|
||||
import { safeURIDecode } from '@/scripts/safe-uri-decode.js';
|
||||
import { isEnabledUrlPreview } from '@/instance.js';
|
||||
import { MkABehavior } from '@/components/global/MkA.vue';
|
||||
import { warningExternalWebsite } from '@/scripts/warning-external-website.js';
|
||||
|
||||
function safeURIDecode(str: string): string {
|
||||
try {
|
||||
return decodeURIComponent(str);
|
||||
} catch {
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
url: string;
|
||||
rel?: string;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue