mirror of
https://iceshrimp.dev/Crimekillz/jointrashposs.git
synced 2024-11-24 17:59:07 +01:00
(add) mfm preview
This commit is contained in:
parent
1125dd7923
commit
447024f3c1
199
assets/css/mfm.scss
Normal file
199
assets/css/mfm.scss
Normal file
@ -0,0 +1,199 @@
|
|||||||
|
@keyframes blink {
|
||||||
|
0% { opacity: 1; transform: scale(1); }
|
||||||
|
30% { opacity: 1; transform: scale(1); }
|
||||||
|
90% { opacity: 0; transform: scale(0.5); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes tada {
|
||||||
|
from {
|
||||||
|
transform: scale3d(1, 1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
10%,
|
||||||
|
20% {
|
||||||
|
transform: scale3d(0.9, 0.9, 0.9) rotate3d(0, 0, 1, -3deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
30%,
|
||||||
|
50%,
|
||||||
|
70%,
|
||||||
|
90% {
|
||||||
|
transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
40%,
|
||||||
|
60%,
|
||||||
|
80% {
|
||||||
|
transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
to {
|
||||||
|
transform: scale3d(1, 1, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
._anime_bounce {
|
||||||
|
will-change: transform;
|
||||||
|
animation: bounce ease 0.7s;
|
||||||
|
animation-iteration-count: 1;
|
||||||
|
transform-origin: 50% 50%;
|
||||||
|
}
|
||||||
|
._anime_bounce_ready {
|
||||||
|
will-change: transform;
|
||||||
|
transform: scaleX(0.90) scaleY(0.90) ;
|
||||||
|
}
|
||||||
|
._anime_bounce_standBy {
|
||||||
|
transition: transform 0.1s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes bounce {
|
||||||
|
0% {
|
||||||
|
transform: scaleX(0.90) scaleY(0.90) ;
|
||||||
|
}
|
||||||
|
19% {
|
||||||
|
transform: scaleX(1.10) scaleY(1.10) ;
|
||||||
|
}
|
||||||
|
48% {
|
||||||
|
transform: scaleX(0.95) scaleY(0.95) ;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: scaleX(1.00) scaleY(1.00) ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MFM -----------------------------
|
||||||
|
|
||||||
|
._mfm_blur_ {
|
||||||
|
filter: blur(6px);
|
||||||
|
transition: filter 0.3s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
filter: blur(0px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mfm-x2 {
|
||||||
|
--mfm-zoom-size: 200%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mfm-x3 {
|
||||||
|
--mfm-zoom-size: 400%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mfm-x4 {
|
||||||
|
--mfm-zoom-size: 600%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mfm-x2, .mfm-x3, .mfm-x4 {
|
||||||
|
font-size: var(--mfm-zoom-size);
|
||||||
|
|
||||||
|
.mfm-x2, .mfm-x3, .mfm-x4 {
|
||||||
|
/* only half effective */
|
||||||
|
font-size: calc(var(--mfm-zoom-size) / 2 + 50%);
|
||||||
|
|
||||||
|
.mfm-x2, .mfm-x3, .mfm-x4 {
|
||||||
|
/* disabled */
|
||||||
|
font-size: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes mfm-spin {
|
||||||
|
0% { transform: rotate(0deg); }
|
||||||
|
100% { transform: rotate(360deg); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes mfm-spinX {
|
||||||
|
0% { transform: perspective(128px) rotateX(0deg); }
|
||||||
|
100% { transform: perspective(128px) rotateX(360deg); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes mfm-spinY {
|
||||||
|
0% { transform: perspective(128px) rotateY(0deg); }
|
||||||
|
100% { transform: perspective(128px) 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, 1); }
|
||||||
|
25% { transform: translateY(-16px) scale(1, 1); }
|
||||||
|
50% { transform: translateY(0) scale(1, 1); }
|
||||||
|
75% { transform: translateY(0) scale(1.5, 0.75); }
|
||||||
|
100% { transform: translateY(0) scale(1, 1); }
|
||||||
|
}
|
||||||
|
|
||||||
|
// const val = () => `translate(${Math.floor(Math.random() * 20) - 10}px, ${Math.floor(Math.random() * 20) - 10}px)`;
|
||||||
|
// let css = '';
|
||||||
|
// for (let i = 0; i <= 100; i += 5) { css += `${i}% { transform: ${val()} }\n`; }
|
||||||
|
@keyframes mfm-twitch {
|
||||||
|
0% { transform: translate(7px, -2px) }
|
||||||
|
5% { transform: translate(-3px, 1px) }
|
||||||
|
10% { transform: translate(-7px, -1px) }
|
||||||
|
15% { transform: translate(0px, -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, 0px) }
|
||||||
|
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) }
|
||||||
|
}
|
||||||
|
|
||||||
|
// const val = () => `translate(${Math.floor(Math.random() * 6) - 3}px, ${Math.floor(Math.random() * 6) - 3}px) rotate(${Math.floor(Math.random() * 24) - 12}deg)`;
|
||||||
|
// let css = '';
|
||||||
|
// for (let i = 0; i <= 100; i += 5) { css += `${i}% { transform: ${val()} }\n`; }
|
||||||
|
@keyframes mfm-shake {
|
||||||
|
0% { transform: translate(-3px, -1px) rotate(-8deg) }
|
||||||
|
5% { transform: translate(0px, -1px) rotate(-10deg) }
|
||||||
|
10% { transform: translate(1px, -3px) rotate(0deg) }
|
||||||
|
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: translate(0px, -1px) rotate(-12deg) }
|
||||||
|
50% { transform: translate(1px, 2px) rotate(10deg) }
|
||||||
|
55% { transform: translate(0px, -3px) rotate(8deg) }
|
||||||
|
60% { transform: translate(1px, -1px) rotate(8deg) }
|
||||||
|
65% { transform: translate(0px, -1px) rotate(-7deg) }
|
||||||
|
70% { transform: translate(-1px, -3px) rotate(6deg) }
|
||||||
|
75% { transform: translate(0px, -2px) rotate(4deg) }
|
||||||
|
80% { transform: translate(-2px, -1px) rotate(3deg) }
|
||||||
|
85% { transform: translate(1px, -3px) rotate(-10deg) }
|
||||||
|
90% { transform: translate(1px, 0px) rotate(3deg) }
|
||||||
|
95% { transform: translate(-2px, 0px) rotate(-3deg) }
|
||||||
|
100% { transform: translate(2px, 1px) rotate(2deg) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes mfm-rubberBand {
|
||||||
|
from { transform: scale3d(1, 1, 1); }
|
||||||
|
30% { transform: scale3d(1.25, 0.75, 1); }
|
||||||
|
40% { transform: scale3d(0.75, 1.25, 1); }
|
||||||
|
50% { transform: scale3d(1.15, 0.85, 1); }
|
||||||
|
65% { transform: scale3d(0.95, 1.05, 1); }
|
||||||
|
75% { transform: scale3d(1.05, 0.95, 1); }
|
||||||
|
to { transform: scale3d(1, 1, 1); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes mfm-rainbow {
|
||||||
|
0% { filter: hue-rotate(0deg) contrast(150%) saturate(150%); }
|
||||||
|
100% { filter: hue-rotate(360deg) contrast(150%) saturate(150%); }
|
||||||
|
}
|
13
components/content/MfmPreview.vue
Normal file
13
components/content/MfmPreview.vue
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<template>
|
||||||
|
<div class="rounded-lg border border-slate-200 dark:border-slate-800 p-6 mfm-root mb-4">
|
||||||
|
<MkMfm :text="text" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import MkMfm from '@/components/mk/Mfm';
|
||||||
|
|
||||||
|
defineProps<{
|
||||||
|
text: string;
|
||||||
|
}>();
|
||||||
|
</script>
|
@ -21,7 +21,6 @@ let realTarget = props.target;
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const url = new URL(props.href);
|
const url = new URL(props.href);
|
||||||
console.log(url);
|
|
||||||
if (!url.hostname || rootDomain.hostname === url.hostname) {
|
if (!url.hostname || rootDomain.hostname === url.hostname) {
|
||||||
realHref = localePath(realHref);
|
realHref = localePath(realHref);
|
||||||
}
|
}
|
||||||
|
151
components/docs/AsideTree.vue
Normal file
151
components/docs/AsideTree.vue
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { PropType } from "vue";
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
links: {
|
||||||
|
type: Array as PropType<any>,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
level: {
|
||||||
|
type: Number,
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
max: {
|
||||||
|
type: Number,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
parent: {
|
||||||
|
type: Object as PropType<any>,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
|
||||||
|
const collapsedMap = useState(
|
||||||
|
`docus-docs-aside-collapse-map-${props.parent?._path || "/"}`,
|
||||||
|
() => {
|
||||||
|
if (props.level === 0) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return (props.links as any[])
|
||||||
|
.filter((link) => !!link.children)
|
||||||
|
.reduce((map, link) => {
|
||||||
|
map[link._path] = true;
|
||||||
|
return map;
|
||||||
|
}, {});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const isActive = (link: any) => {
|
||||||
|
return route.path === link._path;
|
||||||
|
};
|
||||||
|
|
||||||
|
const isCollapsed = (link: any) => {
|
||||||
|
if (link.children) {
|
||||||
|
// Directory has been toggled manually, use its state
|
||||||
|
if (typeof collapsedMap.value[link._path] !== "undefined") {
|
||||||
|
return collapsedMap.value[link._path];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if aside.collapsed has been set in YML
|
||||||
|
if ([true, false].includes(link?.aside?.collapsed)) {
|
||||||
|
return link.aside.collapsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return value grabbed from the link
|
||||||
|
if (link?.collapsed) {
|
||||||
|
return link?.collapsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggleCollapse = (link: any) =>
|
||||||
|
(collapsedMap.value[link._path] = !isCollapsed(link));
|
||||||
|
|
||||||
|
const hasNesting = computed(() =>
|
||||||
|
props.links.some((link: any) => link.children)
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<ul class="docs-aside-tree">
|
||||||
|
<li
|
||||||
|
v-for="link in links"
|
||||||
|
:key="link._path"
|
||||||
|
:class="{
|
||||||
|
'has-parent-icon': parent?.icon,
|
||||||
|
'has-children': level > 0 && link.children,
|
||||||
|
bordered: level > 0 || !hasNesting,
|
||||||
|
active: isActive(link),
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
v-if="link.children"
|
||||||
|
class="title-collapsible-button"
|
||||||
|
@click="toggleCollapse(link)"
|
||||||
|
>
|
||||||
|
<span class="content">
|
||||||
|
<Icon
|
||||||
|
v-if="link?.navigation?.icon || link.icon"
|
||||||
|
:name="link?.navigation?.icon || link.icon"
|
||||||
|
class="icon"
|
||||||
|
/>
|
||||||
|
<span>{{
|
||||||
|
link?.navigation?.title || link.title || link._path
|
||||||
|
}}</span>
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
<Icon
|
||||||
|
:name="
|
||||||
|
isCollapsed(link)
|
||||||
|
? 'lucide:chevrons-up-down'
|
||||||
|
: 'lucide:chevrons-down-up'
|
||||||
|
"
|
||||||
|
class="collapsible-icon"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<NuxtLink
|
||||||
|
v-else
|
||||||
|
:to="link.redirect ? link.redirect : link._path"
|
||||||
|
class="link"
|
||||||
|
:exact="link.exact"
|
||||||
|
:class="{
|
||||||
|
padded: level > 0 || !hasNesting,
|
||||||
|
active: isActive(link),
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<span class="content">
|
||||||
|
<Icon
|
||||||
|
v-if="link?.navigation?.icon || link.icon"
|
||||||
|
:name="link?.navigation?.icon || link.icon"
|
||||||
|
class="icon"
|
||||||
|
/>
|
||||||
|
<span>{{
|
||||||
|
link?.navigation?.title || link.title || link._path
|
||||||
|
}}</span>
|
||||||
|
</span>
|
||||||
|
</NuxtLink>
|
||||||
|
|
||||||
|
<DocsAsideTree
|
||||||
|
v-show="!isCollapsed(link)"
|
||||||
|
v-if="
|
||||||
|
link.children?.length && (max === null || level + 1 < max)
|
||||||
|
"
|
||||||
|
:links="link.children"
|
||||||
|
:level="level + 1"
|
||||||
|
:parent="link"
|
||||||
|
:max="max"
|
||||||
|
class="recursive"
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="ts">
|
||||||
|
</style>
|
54
components/mk/Google.vue
Normal file
54
components/mk/Google.vue
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
<template>
|
||||||
|
<div :class="$style.root">
|
||||||
|
<input v-model="query" :class="$style.input" type="search" :placeholder="q">
|
||||||
|
<button :class="$style.button" @click="search"><SearchIco /> 検索</button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import SearchIco from 'bi/search.svg';
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
q: string;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const query = ref(props.q);
|
||||||
|
|
||||||
|
const search = () => {
|
||||||
|
window.open(`https://www.google.com/search?q=${query.value}`, '_blank');
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" module>
|
||||||
|
.root {
|
||||||
|
display: flex;
|
||||||
|
margin: 8px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input {
|
||||||
|
@apply border-slate-200;
|
||||||
|
flex-shrink: 1;
|
||||||
|
padding: 10px;
|
||||||
|
width: 100%;
|
||||||
|
height: 40px;
|
||||||
|
font-size: 16px;
|
||||||
|
border: solid 1px;
|
||||||
|
border-radius: 4px 0 0 4px;
|
||||||
|
-webkit-appearance: textfield;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button {
|
||||||
|
@apply border-slate-200;
|
||||||
|
flex-shrink: 0;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0 16px;
|
||||||
|
border: solid 1px;
|
||||||
|
border-left: none;
|
||||||
|
border-radius: 0 4px 4px 0;
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
box-shadow: 0 2px 4px rgba(#000, 0.15) inset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
333
components/mk/Mfm.ts
Normal file
333
components/mk/Mfm.ts
Normal file
@ -0,0 +1,333 @@
|
|||||||
|
import { VNode, h } from 'vue';
|
||||||
|
import * as mfm from 'mfm-js';
|
||||||
|
import MkGoogle from '@/components/mk/Google.vue';
|
||||||
|
import MkSparkle from '@/components/mk/Sparkle.vue';
|
||||||
|
import NuxtLink from '@/components/g/NuxtLink';
|
||||||
|
import ProseAVue from '@/components/content/ProseA.vue';
|
||||||
|
|
||||||
|
const QUOTE_STYLE = `
|
||||||
|
display: block;
|
||||||
|
margin: 8px;
|
||||||
|
padding: 6px 0 6px 12px;
|
||||||
|
color: var(--fg);
|
||||||
|
border-left: solid 3px var(--fg);
|
||||||
|
opacity: 0.7;
|
||||||
|
`.split('\n').join(' ');
|
||||||
|
|
||||||
|
export default function(props: {
|
||||||
|
text: string;
|
||||||
|
plain?: boolean;
|
||||||
|
nowrap?: boolean;
|
||||||
|
isNote?: boolean;
|
||||||
|
emojiUrls?: string[];
|
||||||
|
rootScale?: number;
|
||||||
|
}) {
|
||||||
|
const isNote = props.isNote !== undefined ? props.isNote : true;
|
||||||
|
|
||||||
|
if (props.text == null || props.text === '') return;
|
||||||
|
|
||||||
|
const ast = (props.plain ? mfm.parseSimple : mfm.parse)(props.text);
|
||||||
|
|
||||||
|
const validTime = (t: string | null | undefined) => {
|
||||||
|
if (t == null) return null;
|
||||||
|
return t.match(/^[0-9.]+s$/) ? t : null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const useAnim = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gen Vue Elements from MFM AST
|
||||||
|
* @param ast MFM AST
|
||||||
|
* @param scale How times large the text is
|
||||||
|
*/
|
||||||
|
const genEl = (ast: mfm.MfmNode[], scale: number) => ast.map((token): VNode | string | (VNode | string)[] => {
|
||||||
|
switch (token.type) {
|
||||||
|
case 'text': {
|
||||||
|
const text = token.props.text.replace(/(\r\n|\n|\r)/g, '\n');
|
||||||
|
|
||||||
|
if (!props.plain) {
|
||||||
|
const res: (VNode | string)[] = [];
|
||||||
|
for (const t of text.split('\n')) {
|
||||||
|
res.push(h('br'));
|
||||||
|
res.push(t);
|
||||||
|
}
|
||||||
|
res.shift();
|
||||||
|
return res;
|
||||||
|
} else {
|
||||||
|
return [text.replace(/\n/g, ' ')];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'bold': {
|
||||||
|
return [h('b', genEl(token.children, scale))];
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'strike': {
|
||||||
|
return [h('del', genEl(token.children, scale))];
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'italic': {
|
||||||
|
return h('i', {
|
||||||
|
style: 'font-style: oblique;',
|
||||||
|
}, genEl(token.children, scale));
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'fn': {
|
||||||
|
// TODO: CSSを文字列で組み立てていくと token.props.args.~~~ 経由でCSSインジェクションできるのでよしなにやる
|
||||||
|
let style;
|
||||||
|
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;` : '');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'jelly': {
|
||||||
|
const speed = validTime(token.props.args.speed) ?? '1s';
|
||||||
|
style = (useAnim ? `animation: mfm-rubberBand ${speed} linear infinite both;` : '');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'twitch': {
|
||||||
|
const speed = validTime(token.props.args.speed) ?? '0.5s';
|
||||||
|
style = useAnim ? `animation: mfm-twitch ${speed} ease infinite;` : '';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'shake': {
|
||||||
|
const speed = validTime(token.props.args.speed) ?? '0.5s';
|
||||||
|
style = useAnim ? `animation: mfm-shake ${speed} ease infinite;` : '';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'spin': {
|
||||||
|
const direction =
|
||||||
|
token.props.args.left ? 'reverse' :
|
||||||
|
token.props.args.alternate ? 'alternate' :
|
||||||
|
'normal';
|
||||||
|
const anime =
|
||||||
|
token.props.args.x ? 'mfm-spinX' :
|
||||||
|
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};` : '';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'jump': {
|
||||||
|
const speed = validTime(token.props.args.speed) ?? '0.75s';
|
||||||
|
style = useAnim ? `animation: mfm-jump ${speed} linear infinite;` : '';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'bounce': {
|
||||||
|
const speed = validTime(token.props.args.speed) ?? '0.75s';
|
||||||
|
style = useAnim ? `animation: mfm-bounce ${speed} linear infinite; transform-origin: center bottom;` : '';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'flip': {
|
||||||
|
const transform =
|
||||||
|
(token.props.args.h && token.props.args.v) ? 'scale(-1, -1)' :
|
||||||
|
token.props.args.v ? 'scaleY(-1)' :
|
||||||
|
'scaleX(-1)';
|
||||||
|
style = `transform: ${transform};`;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'x2': {
|
||||||
|
return h('span', {
|
||||||
|
class: 'mfm-x2',
|
||||||
|
}, genEl(token.children, scale * 2));
|
||||||
|
}
|
||||||
|
case 'x3': {
|
||||||
|
return h('span', {
|
||||||
|
class: 'mfm-x3',
|
||||||
|
}, genEl(token.children, scale * 3));
|
||||||
|
}
|
||||||
|
case 'x4': {
|
||||||
|
return h('span', {
|
||||||
|
class: 'mfm-x4',
|
||||||
|
}, genEl(token.children, scale * 4));
|
||||||
|
}
|
||||||
|
case 'font': {
|
||||||
|
const family =
|
||||||
|
token.props.args.serif ? 'serif' :
|
||||||
|
token.props.args.monospace ? 'monospace' :
|
||||||
|
token.props.args.cursive ? 'cursive' :
|
||||||
|
token.props.args.fantasy ? 'fantasy' :
|
||||||
|
token.props.args.emoji ? 'emoji' :
|
||||||
|
token.props.args.math ? 'math' :
|
||||||
|
null;
|
||||||
|
if (family) style = `font-family: ${family};`;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'blur': {
|
||||||
|
return h('span', {
|
||||||
|
class: '_mfm_blur_',
|
||||||
|
}, genEl(token.children, scale));
|
||||||
|
}
|
||||||
|
case 'rainbow': {
|
||||||
|
const speed = validTime(token.props.args.speed) ?? '1s';
|
||||||
|
style = useAnim ? `animation: mfm-rainbow ${speed} linear infinite;` : '';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'sparkle': {
|
||||||
|
if (!useAnim) {
|
||||||
|
return genEl(token.children, scale);
|
||||||
|
}
|
||||||
|
return h(MkSparkle, {}, genEl(token.children, scale));
|
||||||
|
}
|
||||||
|
case 'rotate': {
|
||||||
|
const degrees = parseFloat(token.props.args.deg ?? '90');
|
||||||
|
style = `transform: rotate(${degrees}deg); transform-origin: center center;`;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'position': {
|
||||||
|
const x = parseFloat(token.props.args.x ?? '0');
|
||||||
|
const y = parseFloat(token.props.args.y ?? '0');
|
||||||
|
style = `transform: translateX(${x}em) translateY(${y}em);`;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'scale': {
|
||||||
|
const x = Math.min(parseFloat(token.props.args.x ?? '1'), 5);
|
||||||
|
const y = Math.min(parseFloat(token.props.args.y ?? '1'), 5);
|
||||||
|
style = `transform: scale(${x}, ${y});`;
|
||||||
|
scale = scale * Math.max(x, y);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'fg': {
|
||||||
|
let color = token.props.args.color;
|
||||||
|
if (!/^[0-9a-f]{3,6}$/i.test(color)) color = 'f00';
|
||||||
|
style = `color: #${color};`;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'bg': {
|
||||||
|
let color = token.props.args.color;
|
||||||
|
if (!/^[0-9a-f]{3,6}$/i.test(color)) color = 'f00';
|
||||||
|
style = `background-color: #${color};`;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (style == null) {
|
||||||
|
return h('span', {}, ['$[', token.props.name, ' ', ...genEl(token.children, scale), ']']);
|
||||||
|
} else {
|
||||||
|
return h('span', {
|
||||||
|
style: 'display: inline-block; ' + style,
|
||||||
|
}, genEl(token.children, scale));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'small': {
|
||||||
|
return [h('small', {
|
||||||
|
style: 'opacity: 0.7;',
|
||||||
|
}, genEl(token.children, scale))];
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'center': {
|
||||||
|
return [h('div', {
|
||||||
|
style: 'text-align:center;',
|
||||||
|
}, genEl(token.children, scale))];
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'url': {
|
||||||
|
return [h(ProseAVue, {
|
||||||
|
key: Math.random(),
|
||||||
|
href: token.props.url,
|
||||||
|
rel: 'nofollow noopener',
|
||||||
|
}, token.props.url)];
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'link': {
|
||||||
|
return [h(NuxtLink, {
|
||||||
|
key: Math.random(),
|
||||||
|
to: token.props.url,
|
||||||
|
rel: 'nofollow noopener',
|
||||||
|
}, genEl(token.children, scale))];
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'mention': {
|
||||||
|
return [h(MkMention, {
|
||||||
|
key: Math.random(),
|
||||||
|
host: (token.props.host) || host,
|
||||||
|
username: token.props.username,
|
||||||
|
})];
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'hashtag': {
|
||||||
|
return [h(NuxtLink, {
|
||||||
|
key: Math.random(),
|
||||||
|
to: `https://misskey.io/tags/${encodeURIComponent(token.props.hashtag)}`,
|
||||||
|
style: 'color:rgb(255, 145, 86);',
|
||||||
|
}, `#${token.props.hashtag}`)];
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'quote': {
|
||||||
|
if (!props.nowrap) {
|
||||||
|
return [h('div', {
|
||||||
|
style: QUOTE_STYLE,
|
||||||
|
}, genEl(token.children, scale))];
|
||||||
|
} else {
|
||||||
|
return [h('span', {
|
||||||
|
style: QUOTE_STYLE,
|
||||||
|
}, genEl(token.children, scale))];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'emojiCode': {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||||
|
if (props.author?.host == null) {
|
||||||
|
return [h(MkCustomEmoji, {
|
||||||
|
key: Math.random(),
|
||||||
|
name: token.props.name,
|
||||||
|
normal: props.plain,
|
||||||
|
host: null,
|
||||||
|
useOriginalSize: scale >= 2.5,
|
||||||
|
})];
|
||||||
|
} else {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||||
|
if (props.emojiUrls && (props.emojiUrls[token.props.name] == null)) {
|
||||||
|
return [h('span', `:${token.props.name}:`)];
|
||||||
|
} else {
|
||||||
|
return [h(MkCustomEmoji, {
|
||||||
|
key: Math.random(),
|
||||||
|
name: token.props.name,
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||||
|
url: props.emojiUrls ? props.emojiUrls[token.props.name] : null,
|
||||||
|
normal: props.plain,
|
||||||
|
host: props.author.host,
|
||||||
|
useOriginalSize: scale >= 2.5,
|
||||||
|
})];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'unicodeEmoji': {
|
||||||
|
return [h('span', token.props.emoji)];
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'mathInline': {
|
||||||
|
return [h('code', token.props.formula)];
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'mathBlock': {
|
||||||
|
return [h('code', token.props.formula)];
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'search': {
|
||||||
|
return [h(MkGoogle, {
|
||||||
|
key: Math.random(),
|
||||||
|
q: token.props.query,
|
||||||
|
})];
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'plain': {
|
||||||
|
return [h('span', genEl(token.children, scale))];
|
||||||
|
}
|
||||||
|
|
||||||
|
default: {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
console.error('unrecognized ast type:', (token as any).type);
|
||||||
|
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).flat(Infinity) as (VNode | string)[];
|
||||||
|
|
||||||
|
return h('span', {
|
||||||
|
// https://codeday.me/jp/qa/20190424/690106.html
|
||||||
|
style: props.nowrap ? 'white-space: pre; word-wrap: normal; overflow: hidden; text-overflow: ellipsis;' : 'white-space: pre-wrap;',
|
||||||
|
}, genEl(ast, props.rootScale ?? 1));
|
||||||
|
}
|
135
components/mk/Sparkle.vue
Normal file
135
components/mk/Sparkle.vue
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
<template>
|
||||||
|
<span :class="$style.root">
|
||||||
|
<span ref="el" style="display: inline-block">
|
||||||
|
<slot></slot>
|
||||||
|
</span>
|
||||||
|
<!-- なぜか path に対する key が機能しないため
|
||||||
|
<svg :width="width" :height="height" :viewBox="`0 0 ${width} ${height}`" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path v-for="particle in particles" :key="particle.id" style="transform-origin: center; transform-box: fill-box;"
|
||||||
|
:transform="`translate(${particle.x} ${particle.y})`"
|
||||||
|
:fill="particle.color"
|
||||||
|
d="M29.427,2.011C29.721,0.83 30.782,0 32,0C33.218,0 34.279,0.83 34.573,2.011L39.455,21.646C39.629,22.347 39.991,22.987 40.502,23.498C41.013,24.009 41.653,24.371 42.354,24.545L61.989,29.427C63.17,29.721 64,30.782 64,32C64,33.218 63.17,34.279 61.989,34.573L42.354,39.455C41.653,39.629 41.013,39.991 40.502,40.502C39.991,41.013 39.629,41.653 39.455,42.354L34.573,61.989C34.279,63.17 33.218,64 32,64C30.782,64 29.721,63.17 29.427,61.989L24.545,42.354C24.371,41.653 24.009,41.013 23.498,40.502C22.987,39.991 22.347,39.629 21.646,39.455L2.011,34.573C0.83,34.279 0,33.218 0,32C0,30.782 0.83,29.721 2.011,29.427L21.646,24.545C22.347,24.371 22.987,24.009 23.498,23.498C24.009,22.987 24.371,22.347 24.545,21.646L29.427,2.011Z"
|
||||||
|
>
|
||||||
|
<animateTransform
|
||||||
|
attributeName="transform"
|
||||||
|
attributeType="XML"
|
||||||
|
type="rotate"
|
||||||
|
from="0 0 0"
|
||||||
|
to="360 0 0"
|
||||||
|
:dur="`${particle.dur}ms`"
|
||||||
|
repeatCount="indefinite"
|
||||||
|
additive="sum"
|
||||||
|
/>
|
||||||
|
<animateTransform
|
||||||
|
attributeName="transform"
|
||||||
|
attributeType="XML"
|
||||||
|
type="scale"
|
||||||
|
:values="`0; ${particle.size}; 0`"
|
||||||
|
:dur="`${particle.dur}ms`"
|
||||||
|
repeatCount="indefinite"
|
||||||
|
additive="sum"
|
||||||
|
/>
|
||||||
|
</path>
|
||||||
|
</svg>
|
||||||
|
-->
|
||||||
|
<!-- MFMで上位レイヤーに表示されるため、リンクをクリックできるようにstyleにpointer-events: none;を付与。 -->
|
||||||
|
<svg
|
||||||
|
v-for="particle in particles"
|
||||||
|
:key="particle.id"
|
||||||
|
:width="width"
|
||||||
|
:height="height"
|
||||||
|
:viewBox="`0 0 ${width} ${height}`"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
style="
|
||||||
|
position: absolute;
|
||||||
|
top: -32px;
|
||||||
|
left: -32px;
|
||||||
|
pointer-events: none;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
style="transform-origin: center; transform-box: fill-box"
|
||||||
|
:transform="`translate(${particle.x} ${particle.y})`"
|
||||||
|
:fill="particle.color"
|
||||||
|
d="M29.427,2.011C29.721,0.83 30.782,0 32,0C33.218,0 34.279,0.83 34.573,2.011L39.455,21.646C39.629,22.347 39.991,22.987 40.502,23.498C41.013,24.009 41.653,24.371 42.354,24.545L61.989,29.427C63.17,29.721 64,30.782 64,32C64,33.218 63.17,34.279 61.989,34.573L42.354,39.455C41.653,39.629 41.013,39.991 40.502,40.502C39.991,41.013 39.629,41.653 39.455,42.354L34.573,61.989C34.279,63.17 33.218,64 32,64C30.782,64 29.721,63.17 29.427,61.989L24.545,42.354C24.371,41.653 24.009,41.013 23.498,40.502C22.987,39.991 22.347,39.629 21.646,39.455L2.011,34.573C0.83,34.279 0,33.218 0,32C0,30.782 0.83,29.721 2.011,29.427L21.646,24.545C22.347,24.371 22.987,24.009 23.498,23.498C24.009,22.987 24.371,22.347 24.545,21.646L29.427,2.011Z"
|
||||||
|
>
|
||||||
|
<animateTransform
|
||||||
|
attributeName="transform"
|
||||||
|
attributeType="XML"
|
||||||
|
type="rotate"
|
||||||
|
from="0 0 0"
|
||||||
|
to="360 0 0"
|
||||||
|
:dur="`${particle.dur}ms`"
|
||||||
|
repeatCount="1"
|
||||||
|
additive="sum"
|
||||||
|
/>
|
||||||
|
<animateTransform
|
||||||
|
attributeName="transform"
|
||||||
|
attributeType="XML"
|
||||||
|
type="scale"
|
||||||
|
:values="`0; ${particle.size}; 0`"
|
||||||
|
:dur="`${particle.dur}ms`"
|
||||||
|
repeatCount="1"
|
||||||
|
additive="sum"
|
||||||
|
/>
|
||||||
|
</path>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { onMounted, onUnmounted, ref, shallowRef } from "vue";
|
||||||
|
|
||||||
|
const particles = ref([]);
|
||||||
|
const el = shallowRef<HTMLElement>();
|
||||||
|
const width = ref(0);
|
||||||
|
const height = ref(0);
|
||||||
|
const colors = ["#FF1493", "#00FFFF", "#FFE202", "#FFE202", "#FFE202"];
|
||||||
|
let stop = false;
|
||||||
|
let ro: ResizeObserver | undefined;
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
ro = new ResizeObserver((entries, observer) => {
|
||||||
|
width.value = el.value?.offsetWidth + 64;
|
||||||
|
height.value = el.value?.offsetHeight + 64;
|
||||||
|
});
|
||||||
|
ro.observe(el.value);
|
||||||
|
const add = () => {
|
||||||
|
if (stop) return;
|
||||||
|
const x = Math.random() * (width.value - 64);
|
||||||
|
const y = Math.random() * (height.value - 64);
|
||||||
|
const sizeFactor = Math.random();
|
||||||
|
const particle = {
|
||||||
|
id: Math.random().toString(),
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
size: 0.2 + (sizeFactor / 10) * 3,
|
||||||
|
dur: 1000 + sizeFactor * 1000,
|
||||||
|
color: colors[Math.floor(Math.random() * colors.length)],
|
||||||
|
};
|
||||||
|
particles.value.push(particle);
|
||||||
|
window.setTimeout(() => {
|
||||||
|
particles.value = particles.value.filter(
|
||||||
|
(x) => x.id !== particle.id
|
||||||
|
);
|
||||||
|
}, particle.dur - 100);
|
||||||
|
|
||||||
|
window.setTimeout(() => {
|
||||||
|
add();
|
||||||
|
}, 500 + Math.random() * 500);
|
||||||
|
};
|
||||||
|
add();
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
if (ro) ro.disconnect();
|
||||||
|
stop = true;
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" module>
|
||||||
|
.root {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
</style>
|
@ -22,10 +22,10 @@ MFMは、Markup language For Misskeyの略で、Misskeyの様々な場所で使
|
|||||||
メンションについての詳細は[こちら](./mention.md)を参照してください。
|
メンションについての詳細は[こちら](./mention.md)を参照してください。
|
||||||
:::
|
:::
|
||||||
|
|
||||||
```:no-line-numbers
|
```
|
||||||
@alice
|
@alice
|
||||||
```
|
```
|
||||||
```:no-line-numbers
|
```
|
||||||
@alice@example.com
|
@alice@example.com
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -35,65 +35,79 @@ MFMは、Markup language For Misskeyの略で、Misskeyの様々な場所で使
|
|||||||
ハッシュタグについての詳細は[こちら](./hashtag.md)を参照してください。
|
ハッシュタグについての詳細は[こちら](./hashtag.md)を参照してください。
|
||||||
:::
|
:::
|
||||||
|
|
||||||
```:no-line-numbers
|
```
|
||||||
#misskey
|
#misskey
|
||||||
```
|
```
|
||||||
|
|
||||||
|
<MfmPreview text="#misskey"></MfmPreview>
|
||||||
|
|
||||||
### URL
|
### URL
|
||||||
URLを示すことができます。
|
URLを示すことができます。
|
||||||
```:no-line-numbers
|
```
|
||||||
https://example.com
|
https://example.com
|
||||||
```
|
```
|
||||||
|
|
||||||
|
<MfmPreview text="https://example.com"></MfmPreview>
|
||||||
|
|
||||||
### リンク
|
### リンク
|
||||||
文章の特定の範囲を、URLに紐づけることができます。
|
文章の特定の範囲を、URLに紐づけることができます。
|
||||||
```:no-line-numbers
|
```
|
||||||
[example link](https://example.com)
|
[example link](https://example.com)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
<MfmPreview text="[example link](https://example.com)"></MfmPreview>
|
||||||
|
|
||||||
### カスタム絵文字
|
### カスタム絵文字
|
||||||
コロンでカスタム絵文字名を囲むと、カスタム絵文字を表示させることができます。
|
コロンでカスタム絵文字名を囲むと、カスタム絵文字を表示させることができます。
|
||||||
:::tip
|
:::tip
|
||||||
カスタム絵文字についての詳細は[こちら](./custom-emoji.md)を参照してください。
|
カスタム絵文字についての詳細は[こちら](./custom-emoji.md)を参照してください。
|
||||||
:::
|
:::
|
||||||
|
|
||||||
```:no-line-numbers
|
```
|
||||||
:misskey:
|
:misskey:
|
||||||
```
|
```
|
||||||
|
|
||||||
### 太字
|
### 太字
|
||||||
文字を太く表示して強調することができます。
|
文字を太く表示して強調することができます。
|
||||||
```:no-line-numbers
|
```
|
||||||
**太字**
|
**太字**
|
||||||
```
|
```
|
||||||
|
|
||||||
|
<MfmPreview text="**太字**"></MfmPreview>
|
||||||
|
|
||||||
### 目立たなくする
|
### 目立たなくする
|
||||||
内容を小さく・薄く表示させることができます。
|
内容を小さく・薄く表示させることができます。
|
||||||
```:no-line-numbers
|
```
|
||||||
<small>MisskeyでFediverseの世界が広がります</small>
|
<small>MisskeyでFediverseの世界が広がります</small>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
<MfmPreview text="<small>MisskeyでFediverseの世界が広がります</small>"></MfmPreview>
|
||||||
|
|
||||||
### 引用
|
### 引用
|
||||||
内容が引用であることを示すことができます。
|
内容が引用であることを示すことができます。
|
||||||
```:no-line-numbers
|
```
|
||||||
> MisskeyでFediverseの世界が広がります
|
> MisskeyでFediverseの世界が広がります
|
||||||
```
|
```
|
||||||
|
|
||||||
|
<MfmPreview text="> MisskeyでFediverseの世界が広がります"></MfmPreview>
|
||||||
|
|
||||||
### 中央寄せ
|
### 中央寄せ
|
||||||
内容を中央寄せで表示させることができます。
|
内容を中央寄せで表示させることができます。
|
||||||
```:no-line-numbers
|
```
|
||||||
<center>MisskeyでFediverseの世界が広がります</center>
|
<center>MisskeyでFediverseの世界が広がります</center>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
<MfmPreview text="<center>MisskeyでFediverseの世界が広がります</center>"></MfmPreview>
|
||||||
|
|
||||||
### コード(インライン)
|
### コード(インライン)
|
||||||
プログラムなどのコードをインラインでシンタックスハイライトします。
|
プログラムなどのコードをインラインでシンタックスハイライトします。
|
||||||
```:no-line-numbers
|
```
|
||||||
`<: "Hello, world!"`
|
`<: "Hello, world!"`
|
||||||
```
|
```
|
||||||
|
|
||||||
### コード(ブロック)
|
### コード(ブロック)
|
||||||
複数行のプログラムなどのコードをブロックでシンタックスハイライトします。
|
複数行のプログラムなどのコードをブロックでシンタックスハイライトします。
|
||||||
```:no-line-numbers
|
```
|
||||||
~ (#i, 100) {
|
~ (#i, 100) {
|
||||||
<: ? ((i % 15) = 0) "FizzBuzz"
|
<: ? ((i % 15) = 0) "FizzBuzz"
|
||||||
.? ((i % 3) = 0) "Fizz"
|
.? ((i % 3) = 0) "Fizz"
|
||||||
@ -104,33 +118,189 @@ https://example.com
|
|||||||
|
|
||||||
### 反転
|
### 反転
|
||||||
内容を上下または左右に反転させます。
|
内容を上下または左右に反転させます。
|
||||||
```:no-line-numbers
|
```
|
||||||
$[flip MisskeyでFediverseの世界が広がります]
|
$[flip MisskeyでFediverseの世界が広がります]
|
||||||
$[flip.v MisskeyでFediverseの世界が広がります]
|
$[flip.v MisskeyでFediverseの世界が広がります]
|
||||||
$[flip.h,v MisskeyでFediverseの世界が広がります]
|
$[flip.h,v MisskeyでFediverseの世界が広がります]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
<MfmPreview text="$[flip MisskeyでFediverseの世界が広がります]
|
||||||
|
$[flip.v MisskeyでFediverseの世界が広がります]
|
||||||
|
$[flip.h,v MisskeyでFediverseの世界が広がります]"></MfmPreview>
|
||||||
|
|
||||||
### フォント
|
### フォント
|
||||||
内容のフォントを指定することができます。
|
内容のフォントを指定することができます。
|
||||||
```:no-line-numbers
|
```
|
||||||
$[font.serif MisskeyでFediverseの世界が広がります]
|
$[font.serif MisskeyでFediverseの世界が広がります]
|
||||||
$[font.monospace MisskeyでFediverseの世界が広がります]
|
$[font.monospace MisskeyでFediverseの世界が広がります]
|
||||||
$[font.cursive MisskeyでFediverseの世界が広がります]
|
$[font.cursive MisskeyでFediverseの世界が広がります]
|
||||||
$[font.fantasy MisskeyでFediverseの世界が広がります]
|
$[font.fantasy MisskeyでFediverseの世界が広がります]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
<MfmPreview text="$[font.serif MisskeyでFediverseの世界が広がります]
|
||||||
|
$[font.monospace MisskeyでFediverseの世界が広がります]
|
||||||
|
$[font.cursive MisskeyでFediverseの世界が広がります]
|
||||||
|
$[font.fantasy MisskeyでFediverseの世界が広がります]"></MfmPreview>
|
||||||
|
|
||||||
### ぼかし
|
### ぼかし
|
||||||
内容をぼかすことができます。ポインターを上に乗せるとはっきり見えるようになります。
|
内容をぼかすことができます。ポインターを上に乗せるとはっきり見えるようになります。
|
||||||
```:no-line-numbers
|
```
|
||||||
$[blur MisskeyでFediverseの世界が広がります]
|
$[blur MisskeyでFediverseの世界が広がります]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
<MfmPreview text="$[blur MisskeyでFediverseの世界が広がります]"></MfmPreview>
|
||||||
|
|
||||||
|
### 検索
|
||||||
|
検索ボックスを表示できます。
|
||||||
|
|
||||||
|
```
|
||||||
|
misskey 検索
|
||||||
|
```
|
||||||
|
|
||||||
|
<MfmPreview text="misskey 検索"></MfmPreview>
|
||||||
|
|
||||||
|
|
||||||
|
### 文字色・背景色
|
||||||
|
文字色と背景色を変更することができます。
|
||||||
|
|
||||||
|
3,4,6桁のカラーコードで色を表現します。
|
||||||
|
|
||||||
|
```
|
||||||
|
$[fg.color=f00 赤字]
|
||||||
|
$[bg.color=ff0 黄背景]
|
||||||
|
```
|
||||||
|
|
||||||
|
<MfmPreview text="$[fg.color=f00 赤字]
|
||||||
|
$[bg.color=ff0 黄背景]"></MfmPreview>
|
||||||
|
|
||||||
|
### 角度変更
|
||||||
|
指定した角度で回転させます。
|
||||||
|
|
||||||
|
```
|
||||||
|
$[rotate.deg=30 misskey]
|
||||||
|
```
|
||||||
|
|
||||||
|
<MfmPreview text="$[rotate.deg=30 misskey]"></MfmPreview>
|
||||||
|
|
||||||
|
### 位置変更
|
||||||
|
位置をずらすことができます。
|
||||||
|
|
||||||
|
```
|
||||||
|
😏$[position.x=0.8,y=0.5 🍮]😀
|
||||||
|
```
|
||||||
|
|
||||||
|
<MfmPreview text="😏$[position.x=0.8,y=0.5 🍮]😀"></MfmPreview>
|
||||||
|
|
||||||
|
### 拡大
|
||||||
|
文字を引き延ばして表示します。
|
||||||
|
|
||||||
|
```
|
||||||
|
$[scale.x=4,y=2 🍮]
|
||||||
|
```
|
||||||
|
|
||||||
|
<MfmPreview text="$[scale.x=4,y=2 🍮]"></MfmPreview>
|
||||||
|
|
||||||
|
```
|
||||||
|
$[x2 x2]
|
||||||
|
$[x3 x3]
|
||||||
|
$[x4 x4]
|
||||||
|
```
|
||||||
|
|
||||||
|
<MfmPreview text="$[x2 x2]
|
||||||
|
$[x3 x3]
|
||||||
|
$[x4 x4]"></MfmPreview>
|
||||||
|
|
||||||
|
### アニメーション(びよんびよん)
|
||||||
|
|
||||||
|
```
|
||||||
|
$[jelly 🍮] $[jelly.speed=5s 🍮]
|
||||||
|
```
|
||||||
|
|
||||||
|
<MfmPreview text="$[x2 $[jelly 🍮] $[jelly.speed=5s 🍮]]"></MfmPreview>
|
||||||
|
|
||||||
|
### アニメーション(じゃーん)
|
||||||
|
|
||||||
|
```
|
||||||
|
$[tada 🍮] $[tada.speed=5s 🍮]
|
||||||
|
```
|
||||||
|
|
||||||
|
<MfmPreview text="$[x2 $[tada 🍮] $[tada.speed=5s 🍮]]"></MfmPreview>
|
||||||
|
|
||||||
|
### アニメーション(ジャンプ)
|
||||||
|
|
||||||
|
```
|
||||||
|
$[jump 🍮] $[jump.speed=5s 🍮]
|
||||||
|
```
|
||||||
|
|
||||||
|
<MfmPreview text="$[x2 $[jump 🍮] $[jump.speed=5s 🍮]]"></MfmPreview>
|
||||||
|
|
||||||
|
### アニメーション(バウンド)
|
||||||
|
|
||||||
|
```
|
||||||
|
$[bounce 🍮] $[bounce.speed=5s 🍮]
|
||||||
|
```
|
||||||
|
|
||||||
|
<MfmPreview text="$[x2 $[bounce 🍮] $[bounce.speed=5s 🍮]]"></MfmPreview>
|
||||||
|
|
||||||
|
### アニメーション(回転)
|
||||||
|
|
||||||
|
```
|
||||||
|
$[spin 🍮] $[spin.left 🍮] $[spin.alternate 🍮]
|
||||||
|
$[spin.x 🍮] $[spin.x,left 🍮] $[spin.x,alternate 🍮]
|
||||||
|
$[spin.y 🍮] $[spin.y,left 🍮] $[spin.y,alternate 🍮]
|
||||||
|
|
||||||
|
$[spin.speed=5s 🍮]
|
||||||
|
```
|
||||||
|
|
||||||
|
<MfmPreview text="$[x2 $[spin 🍮] $[spin.left 🍮] $[spin.alternate 🍮]
|
||||||
|
$[spin.x 🍮] $[spin.x,left 🍮] $[spin.x,alternate 🍮]
|
||||||
|
$[spin.y 🍮] $[spin.y,left 🍮] $[spin.y,alternate 🍮]
|
||||||
|
$[spin.speed=5s 🍮]]"></MfmPreview>
|
||||||
|
|
||||||
|
### アニメーション(ぶるぶる)
|
||||||
|
|
||||||
|
```
|
||||||
|
$[shake 🍮] $[shake.speed=5s 🍮]
|
||||||
|
```
|
||||||
|
|
||||||
|
<MfmPreview text="$[x2 $[shake 🍮] $[shake.speed=5s 🍮]]"></MfmPreview>
|
||||||
|
|
||||||
|
### アニメーション(ブレ)
|
||||||
|
|
||||||
|
```
|
||||||
|
$[twitch 🍮] $[twitch.speed=5s 🍮]
|
||||||
|
```
|
||||||
|
|
||||||
|
<MfmPreview text="$[x2 $[twitch 🍮] $[twitch.speed=5s 🍮]]"></MfmPreview>
|
||||||
|
|
||||||
|
### レインボー
|
||||||
|
|
||||||
|
```
|
||||||
|
$[rainbow 🍮] $[rainbow.speed=5s 🍮]
|
||||||
|
$[rainbow 色なし文字]
|
||||||
|
$[rainbow $[fg.color=f0f 色付き文字]]
|
||||||
|
```
|
||||||
|
|
||||||
|
<MfmPreview text="$[rainbow 🍮] $[rainbow.speed=5s 🍮]
|
||||||
|
$[rainbow 色なし文字]
|
||||||
|
$[rainbow $[fg.color=f0f 色付き文字]]"></MfmPreview>
|
||||||
|
|
||||||
|
### キラキラ
|
||||||
|
|
||||||
|
```
|
||||||
|
$[sparkle 🍮]
|
||||||
|
```
|
||||||
|
|
||||||
|
<MfmPreview text="$[x2 $[sparkle 🍮]]"></MfmPreview>
|
||||||
|
|
||||||
### プレーン
|
### プレーン
|
||||||
内側の構文を全て無効にします。
|
内側の構文を全て無効にします。
|
||||||
```:no-line-numbers
|
```
|
||||||
<plain>**bold** @mention #hashtag `code` $[x2 🍮]</plain>
|
<plain>**bold** @mention #hashtag `code` $[x2 🍮]</plain>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
<MfmPreview text="<plain>**bold** @mention #hashtag `code` $[x2 🍮]</plain>"></MfmPreview>
|
||||||
|
|
||||||
## 開発者向け情報
|
## 開発者向け情報
|
||||||
MFMのパーサー実装はライブラリとして公開されており、簡単にクライアントにMFMを組み込むことが可能です。
|
MFMのパーサー実装はライブラリとして公開されており、簡単にクライアントにMFMを組み込むことが可能です。
|
||||||
- [misskey-dev/mfm.js](https://github.com/misskey-dev/mfm.js) - JavaScriptパーサー実装
|
- [misskey-dev/mfm.js](https://github.com/misskey-dev/mfm.js) - JavaScriptパーサー実装
|
||||||
|
@ -23,6 +23,7 @@ export default defineNuxtConfig({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
css: [
|
css: [
|
||||||
|
"@/assets/css/mfm.scss",
|
||||||
"github-markdown-css/github-markdown.css",
|
"github-markdown-css/github-markdown.css",
|
||||||
"@/assets/css/tailwind.css",
|
"@/assets/css/tailwind.css",
|
||||||
"@/assets/css/bootstrap-forms.scss",
|
"@/assets/css/bootstrap-forms.scss",
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
"bootstrap-icons": "^1.10.5",
|
"bootstrap-icons": "^1.10.5",
|
||||||
"github-markdown-css": "^5.2.0",
|
"github-markdown-css": "^5.2.0",
|
||||||
"meshline": "^3.1.6",
|
"meshline": "^3.1.6",
|
||||||
|
"mfm-js": "^0.23.3",
|
||||||
"nuxt": "^3.6.2",
|
"nuxt": "^3.6.2",
|
||||||
"postcss": "^8.4.25",
|
"postcss": "^8.4.25",
|
||||||
"sass": "^1.63.6",
|
"sass": "^1.63.6",
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<div class="relative container mx-auto max-w-screen-xl p-6 lg:py-0 grid docs-root pb-12">
|
<div class="relative container mx-auto max-w-screen-xl p-6 lg:py-0 grid docs-root pb-12">
|
||||||
<div class="hidden lg:block">
|
<div class="hidden lg:block">
|
||||||
<div class="sticky top-16 h-[calc(100vh-4rem)] overflow-y-scroll border-r border-slate-200 dark:border-slate-700 py-6 pr-6">
|
<div class="sticky top-16 h-[calc(100vh-4rem)] overflow-y-scroll border-r border-slate-200 dark:border-slate-700 py-6 pr-6">
|
||||||
あ
|
<DocsAsideTree :links="navigation" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="lg:p-6 w-full overflow-x-hidden">
|
<div class="lg:p-6 w-full overflow-x-hidden">
|
||||||
@ -41,6 +41,7 @@ const currentLocaleISO = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const { data } = await useAsyncData(`blog-${locale.value}-${slugs.join('-')}`, () => queryContent(`/${locale.value}/docs/${slugs.join('/')}`).findOne());
|
const { data } = await useAsyncData(`blog-${locale.value}-${slugs.join('-')}`, () => queryContent(`/${locale.value}/docs/${slugs.join('/')}`).findOne());
|
||||||
|
const { navigation } = await useAsyncData('navigation', () => fetchContentNavigation());
|
||||||
|
|
||||||
route.meta.title = data.value?.title;
|
route.meta.title = data.value?.title;
|
||||||
</script>
|
</script>
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
<IndexDonation />
|
<IndexDonation />
|
||||||
<IndexSponsors />
|
<IndexSponsors />
|
||||||
</main>
|
</main>
|
||||||
<GFooter class="relative bg-transparent dark:bg-transparent" />
|
<GFooter class="relative !bg-transparent dark:!bg-transparent" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -41,6 +41,9 @@ devDependencies:
|
|||||||
meshline:
|
meshline:
|
||||||
specifier: ^3.1.6
|
specifier: ^3.1.6
|
||||||
version: 3.1.6(three@0.154.0)
|
version: 3.1.6(three@0.154.0)
|
||||||
|
mfm-js:
|
||||||
|
specifier: ^0.23.3
|
||||||
|
version: 0.23.3
|
||||||
nuxt:
|
nuxt:
|
||||||
specifier: ^3.6.2
|
specifier: ^3.6.2
|
||||||
version: 3.6.2(@types/node@18.0.0)(sass@1.63.6)(typescript@5.1.6)
|
version: 3.6.2(@types/node@18.0.0)(sass@1.63.6)(typescript@5.1.6)
|
||||||
@ -3897,6 +3900,12 @@ packages:
|
|||||||
three: 0.154.0
|
three: 0.154.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/mfm-js@0.23.3:
|
||||||
|
resolution: {integrity: sha512-o8scYmbey6rMUmWAlT3k3ntt6khaCLdxlmHhAWV5wTTMj2OK1atQvZfRUq0SIVm1Jig08qlZg/ps71xUqrScNA==}
|
||||||
|
dependencies:
|
||||||
|
twemoji-parser: 14.0.0
|
||||||
|
dev: true
|
||||||
|
|
||||||
/micromark-core-commonmark@1.1.0:
|
/micromark-core-commonmark@1.1.0:
|
||||||
resolution: {integrity: sha512-BgHO1aRbolh2hcrzL2d1La37V0Aoz73ymF8rAcKnohLy93titmv62E0gP8Hrx9PKcKrqCZ1BbLGbP3bEhoXYlw==}
|
resolution: {integrity: sha512-BgHO1aRbolh2hcrzL2d1La37V0Aoz73ymF8rAcKnohLy93titmv62E0gP8Hrx9PKcKrqCZ1BbLGbP3bEhoXYlw==}
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -5873,6 +5882,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==}
|
resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/twemoji-parser@14.0.0:
|
||||||
|
resolution: {integrity: sha512-9DUOTGLOWs0pFWnh1p6NF+C3CkQ96PWmEFwhOVmT3WbecRC+68AIqpsnJXygfkFcp4aXbOp8Dwbhh/HQgvoRxA==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/type-fest@0.21.3:
|
/type-fest@0.21.3:
|
||||||
resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==}
|
resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
Loading…
Reference in New Issue
Block a user