diff --git a/locales/en-US.yml b/locales/en-US.yml index 07173c131e..c442d41c1e 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -2543,12 +2543,16 @@ _mfm: rotateDescription: "Turns content by a specified angle." position: "Position" positionDescription: "Move content by a specified amount." + crop: "Crop" + cropDescription: "Crop content." followMouse: "Follow Mouse" followMouseDescription: "Content will follow the mouse. On mobile it will follow wherever the user taps." scale: "Scale" scaleDescription: "Scale content by a specified amount." foreground: "Foreground color" foregroundDescription: "Change the foreground color of text." + fade: 'Fade' + fadeDescription: 'Fade text in and out.' background: "Background color" backgroundDescription: "Change the background color of text." plain: "Plain" diff --git a/packages/frontend/src/components/MkMfmWindow.vue b/packages/frontend/src/components/MkMfmWindow.vue index fd23eb0097..a742ad184c 100644 --- a/packages/frontend/src/components/MkMfmWindow.vue +++ b/packages/frontend/src/components/MkMfmWindow.vue @@ -304,6 +304,16 @@ </div> </div> </div> + <div class="section _block"> + <div class="title">{{ i18n.ts._mfm.crop }}</div> + <div class="content"> + <p>{{ i18n.ts._mfm.cropDescription }}</p> + <div class="preview"> + <Mfm :text="preview_crop" /> + <MkTextarea v-model="preview_crop"><span>MFM</span></MkTextarea> + </div> + </div> + </div> <div class="section _block"> <div class="title">{{ i18n.ts._mfm.position }}</div> <div class="content"> @@ -316,7 +326,7 @@ </div> <div class="section _block" style="overflow: hidden"> <div class="title">{{ i18n.ts._mfm.followMouse }}</div> - <MkInfo warn>{{ i18n.ts._mfm.uncommonFeature }}</MkInfo> + <MkInfo warn>{{ i18n.ts._mfm.uncommonFeature }}</MkInfo> <br/> <div class="content"> <p>{{ i18n.ts._mfm.followMouseDescription }}</p> @@ -336,6 +346,16 @@ </div> </div> </div> + <div class="section _block"> + <div class="title">{{ i18n.ts._mfm.fade }}</div> + <div class="content"> + <p>{{ i18n.ts._mfm.fadeDescription }}</p> + <div class="preview"> + <Mfm :text="preview_fade" /> + <MkTextarea v-model="preview_fade"><span>MFM</span></MkTextarea> + </div> + </div> + </div> <div class="section _block"> <div class="title">{{ i18n.ts._mfm.foreground }}</div> <div class="content"> @@ -445,6 +465,9 @@ const preview_rotate = ref( '$[rotate 🍮]\n$[rotate.deg=45 🍮]\n$[rotate.x,deg=45 Hello, world!]', ); const preview_position = ref('$[position.y=-1 🍮]\n$[position.x=-1 🍮]'); +const preview_crop = ref( + "$[crop.top=50 🍮] $[crop.right=50 🍮] $[crop.bottom=50 🍮] $[crop.left=50 🍮]", +); const preview_followmouse = ref('$[followmouse.x 🍮]\n$[followmouse.x,y,rotateByVelocity,speed=0.4 🍮]'); const preview_scale = ref( '$[scale.x=1.3 🍮]\n$[scale.x=1.5,y=3 🍮]\n$[scale.y=0.3 🍮]', @@ -454,6 +477,7 @@ const preview_bg = ref('$[bg.color=31748f Background color]'); const preview_plain = ref( '<plain>**bold** @mention #hashtag `code` $[x2 🍮]</plain>', ); +const preview_fade = ref(`$[fade 🍮] $[fade.out 🍮] $[fade.speed=3s 🍮] $[fade.delay=3s 🍮]`); </script> <style lang="scss" scoped> diff --git a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts index 2f699ccd84..69fe41e2d5 100644 --- a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts +++ b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts @@ -228,6 +228,22 @@ export default function (props: MfmProps, { emit }: { emit: SetupContext<MfmEven } return h(MkSparkle, {}, genEl(token.children, scale)); } + case 'fade': { + // Dont run with reduced motion on + if (!defaultStore.state.animation) { + style = ''; + break; + } + + const direction = token.props.args.out + ? 'alternate-reverse' + : 'alternate'; + const speed = validTime(token.props.args.speed) ?? '1.5s'; + const delay = validTime(token.props.args.delay) ?? '0s'; + const loop = safeParseFloat(token.props.args.loop) ?? 'infinite'; + style = `animation: mfm-fade ${speed} ${delay} linear ${loop}; animation-direction: ${direction};`; + break; + } case 'rotate': { const degrees = safeParseFloat(token.props.args.deg) ?? 90; style = `transform: rotate(${degrees}deg); transform-origin: center center;`; @@ -262,6 +278,22 @@ export default function (props: MfmProps, { emit }: { emit: SetupContext<MfmEven style = `transform: translateX(${x}em) translateY(${y}em);`; break; } + case 'crop': { + const top = Number.parseFloat( + (token.props.args.top ?? '0').toString(), + ); + const right = Number.parseFloat( + (token.props.args.right ?? '0').toString(), + ); + const bottom = Number.parseFloat( + (token.props.args.bottom ?? '0').toString(), + ); + const left = Number.parseFloat( + (token.props.args.left ?? '0').toString(), + ); + style = `clip-path: inset(${top}% ${right}% ${bottom}% ${left}%);`; + break; + } case 'scale': { if (!defaultStore.state.advancedMfm) { style = ''; diff --git a/packages/frontend/src/const.ts b/packages/frontend/src/const.ts index 4c7df613a5..5109c34c02 100644 --- a/packages/frontend/src/const.ts +++ b/packages/frontend/src/const.ts @@ -162,7 +162,7 @@ export const DEFAULT_SERVER_ERROR_IMAGE_URL = 'https://launcher.moe/error.png'; export const DEFAULT_NOT_FOUND_IMAGE_URL = 'https://launcher.moe/missingpage.webp'; export const DEFAULT_INFO_IMAGE_URL = 'https://launcher.moe/nothinghere.png'; -export const MFM_TAGS = ['tada', 'jelly', 'twitch', 'shake', 'spin', 'jump', 'bounce', 'flip', 'x2', 'x3', 'x4', 'scale', 'position', 'fg', 'bg', 'border', 'font', 'blur', 'rainbow', 'sparkle', 'rotate', 'ruby', 'unixtime', 'followmouse']; +export const MFM_TAGS = ['tada', 'jelly', 'twitch', 'shake', 'spin', 'jump', 'bounce', 'flip', 'x2', 'x3', 'x4', 'scale', 'position', 'fg', 'bg', 'border', 'font', 'blur', 'rainbow', 'sparkle', 'rotate', 'ruby', 'unixtime', 'crop', 'fade', 'followmouse']; export const MFM_PARAMS: Record<typeof MFM_TAGS[number], string[]> = { tada: ['speed=', 'delay='], jelly: ['speed=', 'delay='], @@ -186,5 +186,7 @@ export const MFM_PARAMS: Record<typeof MFM_TAGS[number], string[]> = { rotate: ['deg='], ruby: [], unixtime: [], + fade: ['speed=', 'delay=', 'loop=', 'out'], + crop: ['top=', 'bottom=', 'left=', 'right='], followmouse: ['x', 'y', 'rotateByVelocity', 'speed='], }; diff --git a/packages/frontend/src/style.scss b/packages/frontend/src/style.scss index d876009961..dbf7d3ccef 100644 --- a/packages/frontend/src/style.scss +++ b/packages/frontend/src/style.scss @@ -698,3 +698,12 @@ rt { 0% { filter: hue-rotate(0deg) contrast(150%) saturate(150%); } 100% { filter: hue-rotate(360deg) contrast(150%) saturate(150%); } } + +@keyframes mfm-fade { + 0% { + opacity: 0; + } + 100% { + opacity: 1; + } +} \ No newline at end of file