diff --git a/src/components/rich_content/rich_content.jsx b/src/components/rich_content/rich_content.jsx
index 95ab102c6..322b4a8fa 100644
--- a/src/components/rich_content/rich_content.jsx
+++ b/src/components/rich_content/rich_content.jsx
@@ -313,7 +313,88 @@ export default {
const newChildren = Array.isArray(children)
? [...children].reverse().map(processItemReverse).reverse()
: children
- return {newChildren}
+ const attrs = getAttrs(opener)
+ const newAttrs = { ...attrs }
+ const fullAttrs = getAttrs(opener, () => true)
+ const classname = fullAttrs['class']
+ const isMFM = classname?.startsWith('mfm-')
+ if (isMFM) {
+ const mfmOperator = /^mfm-(\w+)$/.exec(classname)?.[1]
+ newAttrs['class'] = 'mfm'
+ newAttrs['data-mfm-operator'] = mfmOperator
+ switch(mfmOperator) {
+ case 'position': {
+ const x = fullAttrs['data-mfm-x'] || 0
+ const y = fullAttrs['data-mfm-y'] || 0
+ newAttrs.style = [
+ 'transform:',
+ `translateX(calc(${x} * (var(--emoji-size) / 2)))`,
+ `translateY(calc(${y} * (var(--emoji-size) / 2)))`,
+ ].join(' ')
+ break
+ }
+ case 'scale': {
+ const x = fullAttrs['data-mfm-x'] || 1
+ const y = fullAttrs['data-mfm-y'] || 1
+ newAttrs.style = [
+ 'transform:',
+ `scale(${x}, ${y})`,
+ ].join(' ')
+ break
+ }
+ case 'rotate': {
+ const deg = fullAttrs['data-mfm-deg'] || 0
+ newAttrs.style = [
+ 'transform:',
+ `rotate(${deg}deg);`,
+ 'transform-origin:',
+ 'center',
+ ].join(' ')
+ break
+ }
+ case 'bg': {
+ const color = fullAttrs['data-mfm-color'] || 0
+ newAttrs.style = [
+ `background-color: #${color}`,
+ ].join(' ')
+ break
+ }
+ case 'fg': {
+ const color = fullAttrs['data-mfm-color'] || 0
+ newAttrs.style = [
+ `color: #${color}`,
+ ].join(' ')
+ break
+ }
+ case 'spin': {
+ const speed = fullAttrs['data-mfm-speed']
+ const y = fullAttrs['data-mfm-y'] != null
+ const x = fullAttrs['data-mfm-x'] != null
+ const anim = [
+ x ? 'mfm-spinX' : null,
+ y ? 'mfm-spinY' : null,
+ 'mfm-spin'
+ ].filter(a => a)[0]
+ newAttrs.style = `animation: ${speed} linear 3s infinite normal none running ${anim}`
+ break
+ }
+ case 'flip': {
+ newAttrs.style = 'transform: scaleX(-1)'
+ break
+ }
+ case 'jump':
+ case 'twitch':
+ case 'shake':
+ case 'bounce':
+ newAttrs.style = `animation: 0.75s linear 0s infinite normal none running mfm-${mfmOperator}`
+ break
+ default:
+ console.log(mfmOperator, opener)
+ console.log(mfmOperator)
+ break
+ }
+ }
+ return {newChildren}
} else {
return
}
diff --git a/src/components/rich_content/rich_content.scss b/src/components/rich_content/rich_content.scss
index 33effc623..230ea8be5 100644
--- a/src/components/rich_content/rich_content.scss
+++ b/src/components/rich_content/rich_content.scss
@@ -1,6 +1,11 @@
.RichContent {
font-family: var(--font);
+ .mfm {
+ display: inline-block;
+ font-size: calc(var(--emoji-size) / 2);
+ }
+
&.-faint {
color: var(--text);
/* stylelint-disable declaration-no-important */
@@ -106,3 +111,289 @@ a .RichContent {
/* stylelint-disable-next-line declaration-no-important */
color: var(--link) !important;
}
+
+@keyframes mfm-spin {
+ 0% {
+ transform: rotate(0deg);
+ }
+
+ 100% {
+ transform: rotate(360deg);
+ }
+}
+
+@keyframes mfm-spinX {
+ 0% {
+ transform: perspective(8em) rotateX(0);
+ }
+
+ 100% {
+ transform: perspective(8em) rotateX(360deg);
+ }
+}
+
+@keyframes mfm-spinY {
+ 0% {
+ transform: perspective(8em) rotateY(0);
+ }
+
+ 100% {
+ transform: perspective(8em) 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)
+ }
+
+ 25% {
+ transform: translateY(-16px) scale(1)
+ }
+
+ 50% {
+ transform: translateY(0) scale(1)
+ }
+
+ 75% {
+ transform: translateY(0) scale(1.5,.75)
+ }
+
+ 100% {
+ transform: translateY(0) scale(1)
+ }
+}
+
+@keyframes mfm-twitch {
+ 0% {
+ transform: translate(7px,-2px)
+ }
+
+ 5% {
+ transform: translate(-3px,1px)
+ }
+
+ 10% {
+ transform: translate(-7px,-1px)
+ }
+
+ 15% {
+ transform: translateY(-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)
+ }
+
+ 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)
+ }
+}
+
+@keyframes mfm-shake {
+ 0% {
+ transform: translate(-3px,-1px) rotate(-8deg)
+ }
+
+ 5% {
+ transform: translateY(-1px) rotate(-10deg)
+ }
+
+ 10% {
+ transform: translate(1px,-3px) rotate(0)
+ }
+
+ 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: translateY(-1px)rotate(-12deg)
+ }
+
+ 50% {
+ transform: translate(1px,2px)rotate(10deg)
+ }
+
+ 55% {
+ transform: translateY(-3px)rotate(8deg)
+ }
+
+ 60% {
+ transform: translate(1px,-1px)rotate(8deg)
+ }
+
+ 65% {
+ transform: translateY(-1px)rotate(-7deg)
+ }
+
+ 70% {
+ transform: translate(-1px,-3px)rotate(6deg)
+ }
+
+ 75% {
+ transform: translateY(-2px)rotate(4deg)
+ }
+
+ 80% {
+ transform: translate(-2px,-1px)rotate(3deg)
+ }
+
+ 85% {
+ transform: translate(1px,-3px)rotate(-10deg)
+ }
+
+ 90% {
+ transform: translate(1px)rotate(3deg)
+ }
+
+ 95% {
+ transform: translate(-2px)rotate(-3deg)
+ }
+
+ 100% {
+ transform: translate(2px,1px)rotate(2deg)
+ }
+}
+
+@keyframes mfm-rubberBand {
+ 0% {
+ transform: scale(1)
+ }
+
+ 30% {
+ transform: scale(1.25,.75)
+ }
+
+ 40% {
+ transform: scale(.75,1.25)
+ }
+
+ 50% {
+ transform: scale(1.15,.85)
+ }
+
+ 65% {
+ transform: scale(.95,1.05)
+ }
+
+ 75% {
+ transform: scale(1.05,.95)
+ }
+
+ 100% {
+ transform: scale(1)
+ }
+}
+
+@keyframes mfm-rainbow {
+ 0% {
+ filter: hue-rotate()contrast(150%)saturate(150%)
+ }
+
+ 100% {
+ filter: hue-rotate(360deg)contrast(150%)saturate(150%)
+ }
+}