+
@@ -31,7 +32,7 @@ defineProps<{
.label {
font-weight: bold;
padding: 1.5em 0 0 0;
- margin: 0 0 16px 0;
+ margin: 0 0 8px 0;
&:empty {
display: none;
@@ -45,4 +46,10 @@ defineProps<{
.main {
margin: 1.5em 0 0 0;
}
+
+.description {
+ font-size: 0.85em;
+ color: var(--fgTransparentWeak);
+ margin: 0 0 8px 0;
+}
diff --git a/packages/frontend/src/components/form/suspense.vue b/packages/frontend/src/components/form/suspense.vue
index e3db639ff0..eaf5ae4744 100644
--- a/packages/frontend/src/components/form/suspense.vue
+++ b/packages/frontend/src/components/form/suspense.vue
@@ -21,7 +21,6 @@ SPDX-License-Identifier: AGPL-3.0-only
+
+
diff --git a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.stories.impl.ts b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.stories.impl.ts
index 9d9febf693..9cdb490e4b 100644
--- a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.stories.impl.ts
+++ b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.stories.impl.ts
@@ -7,7 +7,7 @@
import { StoryObj } from '@storybook/vue3';
import { within } from '@storybook/testing-library';
import { expect } from '@storybook/jest';
-import MkMisskeyFlavoredMarkdown from './MkMisskeyFlavoredMarkdown.ts';
+import MkMisskeyFlavoredMarkdown from './MkMisskeyFlavoredMarkdown.js';
export const Default = {
render(args) {
return {
diff --git a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts
index fe599dcead..650c79dff7 100644
--- a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts
+++ b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts
@@ -3,7 +3,7 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
-import { VNode, h } from 'vue';
+import { VNode, h, SetupContext } from 'vue';
import * as mfm from 'mfm-js';
import * as Misskey from 'misskey-js';
import MkUrl from '@/components/global/MkUrl.vue';
@@ -37,14 +37,18 @@ type MfmProps = {
isNote?: boolean;
emojiUrls?: string[];
rootScale?: number;
- nyaize: boolean | 'respect';
+ nyaize?: boolean | 'respect';
parsedNodes?: mfm.MfmNode[] | null;
enableEmojiMenu?: boolean;
enableEmojiMenuReaction?: boolean;
};
+type MfmEvents = {
+ clickEv(id: string): void;
+};
+
// eslint-disable-next-line import/no-default-export
-export default function(props: MfmProps) {
+export default function(props: MfmProps, context: SetupContext
) {
const isNote = props.isNote ?? true;
const shouldNyaize = props.nyaize ? props.nyaize === 'respect' ? props.author?.isCat : false : false;
@@ -107,22 +111,26 @@ export default function(props: MfmProps) {
switch (token.props.name) {
case 'tada': {
const speed = validTime(token.props.args.speed) ?? '1s';
- style = 'font-size: 150%;' + (useAnim ? `animation: tada ${speed} linear infinite both;` : '');
+ const delay = validTime(token.props.args.delay) ?? '0s';
+ style = 'font-size: 150%;' + (useAnim ? `animation: tada ${speed} linear infinite both; animation-delay: ${delay};` : '');
break;
}
case 'jelly': {
const speed = validTime(token.props.args.speed) ?? '1s';
- style = (useAnim ? `animation: mfm-rubberBand ${speed} linear infinite both;` : '');
+ const delay = validTime(token.props.args.delay) ?? '0s';
+ style = (useAnim ? `animation: mfm-rubberBand ${speed} linear infinite both; animation-delay: ${delay};` : '');
break;
}
case 'twitch': {
const speed = validTime(token.props.args.speed) ?? '0.5s';
- style = useAnim ? `animation: mfm-twitch ${speed} ease infinite;` : '';
+ const delay = validTime(token.props.args.delay) ?? '0s';
+ style = useAnim ? `animation: mfm-twitch ${speed} ease infinite; animation-delay: ${delay};` : '';
break;
}
case 'shake': {
const speed = validTime(token.props.args.speed) ?? '0.5s';
- style = useAnim ? `animation: mfm-shake ${speed} ease infinite;` : '';
+ const delay = validTime(token.props.args.delay) ?? '0s';
+ style = useAnim ? `animation: mfm-shake ${speed} ease infinite; animation-delay: ${delay};` : '';
break;
}
case 'spin': {
@@ -135,17 +143,20 @@ export default function(props: MfmProps) {
token.props.args.y ? 'mfm-spinY' :
'mfm-spin';
const speed = validTime(token.props.args.speed) ?? '1.5s';
- style = useAnim ? `animation: ${anime} ${speed} linear infinite; animation-direction: ${direction};` : '';
+ const delay = validTime(token.props.args.delay) ?? '0s';
+ style = useAnim ? `animation: ${anime} ${speed} linear infinite; animation-direction: ${direction}; animation-delay: ${delay};` : '';
break;
}
case 'jump': {
const speed = validTime(token.props.args.speed) ?? '0.75s';
- style = useAnim ? `animation: mfm-jump ${speed} linear infinite;` : '';
+ const delay = validTime(token.props.args.delay) ?? '0s';
+ style = useAnim ? `animation: mfm-jump ${speed} linear infinite; animation-delay: ${delay};` : '';
break;
}
case 'bounce': {
const speed = validTime(token.props.args.speed) ?? '0.75s';
- style = useAnim ? `animation: mfm-bounce ${speed} linear infinite; transform-origin: center bottom;` : '';
+ const delay = validTime(token.props.args.delay) ?? '0s';
+ style = useAnim ? `animation: mfm-bounce ${speed} linear infinite; transform-origin: center bottom; animation-delay: ${delay};` : '';
break;
}
case 'flip': {
@@ -195,7 +206,8 @@ export default function(props: MfmProps) {
}, genEl(token.children, scale));
}
const speed = validTime(token.props.args.speed) ?? '1s';
- style = `animation: mfm-rainbow ${speed} linear infinite;`;
+ const delay = validTime(token.props.args.delay) ?? '0s';
+ style = `animation: mfm-rainbow ${speed} linear infinite; animation-delay: ${delay};`;
break;
}
case 'sparkle': {
@@ -230,13 +242,13 @@ export default function(props: MfmProps) {
case 'fg': {
let color = token.props.args.color;
if (!/^[0-9a-f]{3,6}$/i.test(color)) color = 'f00';
- style = `color: #${color};`;
+ style = `color: #${color}; overflow-wrap: anywhere;`;
break;
}
case 'bg': {
let color = token.props.args.color;
if (!/^[0-9a-f]{3,6}$/i.test(color)) color = 'f00';
- style = `background-color: #${color};`;
+ style = `background-color: #${color}; overflow-wrap: anywhere;`;
break;
}
case 'ruby': {
@@ -273,6 +285,13 @@ export default function(props: MfmProps) {
}),
]);
}
+ case 'clickable': {
+ return h('span', { onClick(ev: MouseEvent): void {
+ ev.stopPropagation();
+ ev.preventDefault();
+ context.emit('clickEv', token.props.args.ev ?? '');
+ } }, genEl(token.children, scale));
+ }
}
if (style === undefined) {
return h('span', {}, ['$[', token.props.name, ' ', ...genEl(token.children, scale), ']']);
diff --git a/packages/frontend/src/components/global/MkPageHeader.vue b/packages/frontend/src/components/global/MkPageHeader.vue
index 301e691fa0..8624aebdcf 100644
--- a/packages/frontend/src/components/global/MkPageHeader.vue
+++ b/packages/frontend/src/components/global/MkPageHeader.vue
@@ -53,7 +53,7 @@ import { PageHeaderItem } from '@/types/page-header.js';
const props = withDefaults(defineProps<{
tabs?: Tab[];
tab?: string;
- actions?: PageHeaderItem[];
+ actions?: PageHeaderItem[] | null;
thin?: boolean;
displayMyAvatar?: boolean;
}>(), {
diff --git a/packages/frontend/src/components/global/MkStickyContainer.vue b/packages/frontend/src/components/global/MkStickyContainer.vue
index 1d707af2d1..70cc68b14c 100644
--- a/packages/frontend/src/components/global/MkStickyContainer.vue
+++ b/packages/frontend/src/components/global/MkStickyContainer.vue
@@ -20,7 +20,7 @@ SPDX-License-Identifier: AGPL-3.0-only
diff --git a/packages/frontend/src/pages/settings/avatar-decoration.dialog.vue b/packages/frontend/src/pages/settings/avatar-decoration.dialog.vue
new file mode 100644
index 0000000000..329ab4d47a
--- /dev/null
+++ b/packages/frontend/src/pages/settings/avatar-decoration.dialog.vue
@@ -0,0 +1,154 @@
+
+
+
+
+ {{ i18n.ts.avatarDecorations }}
+
+
+
+
+
{{ decoration.name }}
+
+
+
+
+ {{ i18n.ts.angle }}
+
+
+ X {{ i18n.ts.position }}
+
+
+ Y {{ i18n.ts.position }}
+
+
+ {{ i18n.ts.flip }}
+
+
+
+
+
+ {{ i18n.ts.update }}
+ {{ i18n.ts.detach }}
+ {{ i18n.ts.attach }}
+
+
+
+
+
+
+
+
diff --git a/packages/frontend/src/pages/settings/avatar-decoration.vue b/packages/frontend/src/pages/settings/avatar-decoration.vue
new file mode 100644
index 0000000000..6551fc917e
--- /dev/null
+++ b/packages/frontend/src/pages/settings/avatar-decoration.vue
@@ -0,0 +1,152 @@
+
+
+
+
+
+
{{ i18n.t('_profile.avatarDecorationMax', { max: $i.policies.avatarDecorationLimit }) }} ({{ i18n.t('remainingN', { n: $i.policies.avatarDecorationLimit - $i.avatarDecorations.length }) }})
+
+
+
+
+
{{ i18n.ts.inUse }}
+
+
+
+
+
+
{{ i18n.ts.detachAll }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/frontend/src/pages/settings/custom-css.vue b/packages/frontend/src/pages/settings/custom-css.vue
index e33e778246..2564562089 100644
--- a/packages/frontend/src/pages/settings/custom-css.vue
+++ b/packages/frontend/src/pages/settings/custom-css.vue
@@ -7,15 +7,15 @@ SPDX-License-Identifier: AGPL-3.0-only
{{ i18n.ts.customCssWarn }}
-
+
CSS
-
+
+
+
diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue
index 0c83abf7f6..3e5f5cb8c8 100644
--- a/packages/frontend/src/pages/settings/general.vue
+++ b/packages/frontend/src/pages/settings/general.vue
@@ -48,6 +48,7 @@ SPDX-License-Identifier: AGPL-3.0-only
{{ i18n.ts.collapseRenotes }}
{{ i18n.ts.enableAdvancedMfm }}
{{ i18n.ts.enableAnimatedMfm }}
+ {{ i18n.ts.enableQuickAddMfmFunction }}
{{ i18n.ts.showGapBetweenNotesInTimeline }}
{{ i18n.ts.loadRawImages }}
@@ -122,6 +123,7 @@ SPDX-License-Identifier: AGPL-3.0-only
{{ i18n.ts.useSystemFont }}
{{ i18n.ts.disableDrawer }}
{{ i18n.ts.forceShowAds }}
+ {{ i18n.ts.seasonalScreenEffect }}