From f9a7cd0daa869b23b100f6d32e71f0faaa7873fc Mon Sep 17 00:00:00 2001
From: Leah <kevinlukej@gmail.com>
Date: Thu, 23 May 2024 21:40:25 +0000
Subject: [PATCH] Ported firefish crop and fade mfm

---
 locales/en-US.yml                             |  4 +++
 .../frontend/src/components/MkMfmWindow.vue   | 26 ++++++++++++++-
 .../global/MkMisskeyFlavoredMarkdown.ts       | 32 +++++++++++++++++++
 packages/frontend/src/const.ts                |  4 ++-
 packages/frontend/src/style.scss              |  9 ++++++
 5 files changed, 73 insertions(+), 2 deletions(-)

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