diff --git a/src/web/app/desktop/router.ts b/src/web/app/desktop/router.ts
deleted file mode 100644
index 6ba8bda12..000000000
--- a/src/web/app/desktop/router.ts
+++ /dev/null
@@ -1,100 +0,0 @@
-/**
- * Desktop App Router
- */
-
-import * as riot from 'riot';
-import * as route from 'page';
-import MiOS from '../common/mios';
-let page = null;
-
-export default (mios: MiOS) => {
-	route('/',                       index);
-	route('/selectdrive',            selectDrive);
-	route('/i/customize-home',       customizeHome);
-	route('/i/drive',                drive);
-	route('/i/drive/folder/:folder', drive);
-	route('/i/messaging/:user',      messaging);
-	route('/i/mentions',             mentions);
-	route('/post::post',             post);
-	route('/search',                 search);
-	route('/:user',                  user.bind(null, 'home'));
-	route('/:user/graphs',           user.bind(null, 'graphs'));
-	route('/:user/:post',            post);
-	route('*',                       notFound);
-
-	function index() {
-		mios.isSignedIn ? home() : entrance();
-	}
-
-	function home() {
-		mount(document.createElement('mk-home-page'));
-	}
-
-	function customizeHome() {
-		mount(document.createElement('mk-home-customize-page'));
-	}
-
-	function entrance() {
-		mount(document.createElement('mk-entrance'));
-		document.documentElement.setAttribute('data-page', 'entrance');
-	}
-
-	function mentions() {
-		const el = document.createElement('mk-home-page');
-		el.setAttribute('mode', 'mentions');
-		mount(el);
-	}
-
-	function search(ctx) {
-		const el = document.createElement('mk-search-page');
-		el.setAttribute('query', ctx.querystring.substr(2));
-		mount(el);
-	}
-
-	function user(page, ctx) {
-		const el = document.createElement('mk-user-page');
-		el.setAttribute('user', ctx.params.user);
-		el.setAttribute('page', page);
-		mount(el);
-	}
-
-	function post(ctx) {
-		const el = document.createElement('mk-post-page');
-		el.setAttribute('post', ctx.params.post);
-		mount(el);
-	}
-
-	function selectDrive() {
-		mount(document.createElement('mk-selectdrive-page'));
-	}
-
-	function drive(ctx) {
-		const el = document.createElement('mk-drive-page');
-		if (ctx.params.folder) el.setAttribute('folder', ctx.params.folder);
-		mount(el);
-	}
-
-	function messaging(ctx) {
-		const el = document.createElement('mk-messaging-room-page');
-		el.setAttribute('user', ctx.params.user);
-		mount(el);
-	}
-
-	function notFound() {
-		mount(document.createElement('mk-not-found'));
-	}
-
-	(riot as any).mixin('page', {
-		page: route
-	});
-
-	// EXEC
-	(route as any)();
-};
-
-function mount(content) {
-	document.documentElement.removeAttribute('data-page');
-	if (page) page.unmount();
-	const body = document.getElementById('app');
-	page = riot.mount(body.appendChild(content))[0];
-}
diff --git a/src/web/app/desktop/script.ts b/src/web/app/desktop/script.ts
index 6c40ae0a3..e7c8f8e49 100644
--- a/src/web/app/desktop/script.ts
+++ b/src/web/app/desktop/script.ts
@@ -26,6 +26,7 @@ import MkDrive from './views/pages/drive.vue';
 import MkHomeCustomize from './views/pages/home-customize.vue';
 import MkMessagingRoom from './views/pages/messaging-room.vue';
 import MkPost from './views/pages/post.vue';
+import MkSearch from './views/pages/search.vue';
 
 /**
  * init
@@ -76,6 +77,7 @@ init(async (launch) => {
 		{ path: '/i/drive', component: MkDrive },
 		{ path: '/i/drive/folder/:folder', component: MkDrive },
 		{ path: '/selectdrive', component: MkSelectDrive },
+		{ path: '/search', component: MkSearch },
 		{ path: '/:user', component: MkUser },
 		{ path: '/:user/:post', component: MkPost }
 	]);
diff --git a/src/web/app/desktop/views/pages/user/user.home.vue b/src/web/app/desktop/views/pages/user/user.home.vue
index bf96741cb..dbf02bd07 100644
--- a/src/web/app/desktop/views/pages/user/user.home.vue
+++ b/src/web/app/desktop/views/pages/user/user.home.vue
@@ -10,7 +10,7 @@
 	</div>
 	<main>
 		<mk-post-detail v-if="user.pinned_post" :post="user.pinned_post" compact/>
-		<x-timeline ref="tl" :user="user"/>
+		<x-timeline class="timeline" ref="tl" :user="user"/>
 	</main>
 	<div>
 		<div ref="right">
@@ -25,19 +25,19 @@
 
 <script lang="ts">
 import Vue from 'vue';
-import XUserTimeline from './user.timeline.vue';
-import XUserProfile from './user.profile.vue';
-import XUserPhotos from './user.photos.vue';
-import XUserFollowersYouKnow from './user.followers-you-know.vue';
-import XUserFriends from './user.friends.vue';
+import XTimeline from './user.timeline.vue';
+import XProfile from './user.profile.vue';
+import XPhotos from './user.photos.vue';
+import XFollowersYouKnow from './user.followers-you-know.vue';
+import XFriends from './user.friends.vue';
 
 export default Vue.extend({
 	components: {
-		XUserTimeline,
-		XUserProfile,
-		XUserPhotos,
-		XUserFollowersYouKnow,
-		XUserFriends
+		XTimeline,
+		XProfile,
+		XPhotos,
+		XFollowersYouKnow,
+		XFriends
 	},
 	props: ['user'],
 	methods: {
@@ -64,7 +64,7 @@ export default Vue.extend({
 		padding 16px
 		width calc(100% - 275px * 2)
 
-		> .mk-user-timeline
+		> .timeline
 			border solid 1px rgba(0, 0, 0, 0.075)
 			border-radius 6px
 
diff --git a/src/web/app/desktop/views/pages/user/user.timeline.vue b/src/web/app/desktop/views/pages/user/user.timeline.vue
index 51c7589fd..d8fff6ce6 100644
--- a/src/web/app/desktop/views/pages/user/user.timeline.vue
+++ b/src/web/app/desktop/views/pages/user/user.timeline.vue
@@ -87,6 +87,10 @@ export default Vue.extend({
 			if (current > document.body.offsetHeight - 16/*遊び*/) {
 				this.more();
 			}
+		},
+		warp(date) {
+			this.date = date;
+			this.fetch();
 		}
 	}
 });
diff --git a/src/web/app/mobile/router.ts b/src/web/app/mobile/router.ts
deleted file mode 100644
index 050fa7fc2..000000000
--- a/src/web/app/mobile/router.ts
+++ /dev/null
@@ -1,143 +0,0 @@
-/**
- * Mobile App Router
- */
-
-import * as riot from 'riot';
-import * as route from 'page';
-import MiOS from '../common/mios';
-let page = null;
-
-export default (mios: MiOS) => {
-	route('/',                           index);
-	route('/selectdrive',                selectDrive);
-	route('/i/notifications',            notifications);
-	route('/i/messaging',                messaging);
-	route('/i/messaging/:username',      messaging);
-	route('/i/drive',                    drive);
-	route('/i/drive/folder/:folder',     drive);
-	route('/i/drive/file/:file',         drive);
-	route('/i/settings',                 settings);
-	route('/i/settings/profile',         settingsProfile);
-	route('/i/settings/signin-history',  settingsSignin);
-	route('/i/settings/twitter',         settingsTwitter);
-	route('/i/settings/authorized-apps', settingsAuthorizedApps);
-	route('/post/new',                   newPost);
-	route('/post::post',                 post);
-	route('/search',                     search);
-	route('/:user',                      user.bind(null, 'overview'));
-	route('/:user/graphs',               user.bind(null, 'graphs'));
-	route('/:user/followers',            userFollowers);
-	route('/:user/following',            userFollowing);
-	route('/:user/:post',                post);
-	route('*',                           notFound);
-
-	function index() {
-		mios.isSignedIn ? home() : entrance();
-	}
-
-	function home() {
-		mount(document.createElement('mk-home-page'));
-	}
-
-	function entrance() {
-		mount(document.createElement('mk-entrance'));
-	}
-
-	function notifications() {
-		mount(document.createElement('mk-notifications-page'));
-	}
-
-	function messaging(ctx) {
-		if (ctx.params.username) {
-			const el = document.createElement('mk-messaging-room-page');
-			el.setAttribute('username', ctx.params.username);
-			mount(el);
-		} else {
-			mount(document.createElement('mk-messaging-page'));
-		}
-	}
-
-	function newPost() {
-		mount(document.createElement('mk-new-post-page'));
-	}
-
-	function settings() {
-		mount(document.createElement('mk-settings-page'));
-	}
-
-	function settingsProfile() {
-		mount(document.createElement('mk-profile-setting-page'));
-	}
-
-	function settingsSignin() {
-		mount(document.createElement('mk-signin-history-page'));
-	}
-
-	function settingsTwitter() {
-		mount(document.createElement('mk-twitter-setting-page'));
-	}
-
-	function settingsAuthorizedApps() {
-		mount(document.createElement('mk-authorized-apps-page'));
-	}
-
-	function search(ctx) {
-		const el = document.createElement('mk-search-page');
-		el.setAttribute('query', ctx.querystring.substr(2));
-		mount(el);
-	}
-
-	function user(page, ctx) {
-		const el = document.createElement('mk-user-page');
-		el.setAttribute('user', ctx.params.user);
-		el.setAttribute('page', page);
-		mount(el);
-	}
-
-	function userFollowing(ctx) {
-		const el = document.createElement('mk-user-following-page');
-		el.setAttribute('user', ctx.params.user);
-		mount(el);
-	}
-
-	function userFollowers(ctx) {
-		const el = document.createElement('mk-user-followers-page');
-		el.setAttribute('user', ctx.params.user);
-		mount(el);
-	}
-
-	function post(ctx) {
-		const el = document.createElement('mk-post-page');
-		el.setAttribute('post', ctx.params.post);
-		mount(el);
-	}
-
-	function drive(ctx) {
-		const el = document.createElement('mk-drive-page');
-		if (ctx.params.folder) el.setAttribute('folder', ctx.params.folder);
-		if (ctx.params.file) el.setAttribute('file', ctx.params.file);
-		mount(el);
-	}
-
-	function selectDrive() {
-		mount(document.createElement('mk-selectdrive-page'));
-	}
-
-	function notFound() {
-		mount(document.createElement('mk-not-found'));
-	}
-
-	(riot as any).mixin('page', {
-		page: route
-	});
-
-	// EXEC
-	(route as any)();
-};
-
-function mount(content) {
-	document.documentElement.style.background = '#fff';
-	if (page) page.unmount();
-	const body = document.getElementById('app');
-	page = riot.mount(body.appendChild(content))[0];
-}
diff --git a/src/web/app/mobile/script.ts b/src/web/app/mobile/script.ts
index dce6640ea..6e69b3ed3 100644
--- a/src/web/app/mobile/script.ts
+++ b/src/web/app/mobile/script.ts
@@ -23,6 +23,9 @@ import MkNotifications from './views/pages/notifications.vue';
 import MkMessaging from './views/pages/messaging.vue';
 import MkMessagingRoom from './views/pages/messaging-room.vue';
 import MkPost from './views/pages/post.vue';
+import MkSearch from './views/pages/search.vue';
+import MkFollowers from './views/pages/followers.vue';
+import MkFollowing from './views/pages/following.vue';
 
 /**
  * init
@@ -58,7 +61,10 @@ init((launch) => {
 		{ path: '/i/drive/folder/:folder', component: MkDrive },
 		{ path: '/i/drive/file/:file', component: MkDrive },
 		{ path: '/selectdrive', component: MkSelectDrive },
+		{ path: '/search', component: MkSearch },
 		{ path: '/:user', component: MkUser },
+		{ path: '/:user/followers', component: MkFollowers },
+		{ path: '/:user/following', component: MkFollowing },
 		{ path: '/:user/:post', component: MkPost }
 	]);
 }, true);
diff --git a/src/web/app/mobile/tags/page/entrance.tag b/src/web/app/mobile/tags/page/entrance.tag
deleted file mode 100644
index 17ba1cd7b..000000000
--- a/src/web/app/mobile/tags/page/entrance.tag
+++ /dev/null
@@ -1,66 +0,0 @@
-<mk-entrance>
-	<main><img src="/assets/title.svg" alt="Misskey"/>
-		<mk-entrance-signin v-if="mode == 'signin'"/>
-		<mk-entrance-signup v-if="mode == 'signup'"/>
-		<div class="introduction" v-if="mode == 'introduction'">
-			<mk-introduction/>
-			<button @click="signin">%i18n:common.ok%</button>
-		</div>
-	</main>
-	<footer>
-		<p class="c">{ _COPYRIGHT_ }</p>
-	</footer>
-	<style lang="stylus" scoped>
-		:scope
-			display block
-			height 100%
-
-			> main
-				display block
-
-				> img
-					display block
-					width 130px
-					height 120px
-					margin 0 auto
-
-				> .introduction
-					max-width 300px
-					margin 0 auto
-					color #666
-
-					> button
-						display block
-						margin 16px auto 0 auto
-
-			> footer
-				> .c
-					margin 0
-					text-align center
-					line-height 64px
-					font-size 10px
-					color rgba(#000, 0.5)
-
-	</style>
-	<script lang="typescript">
-		this.mode = 'signin';
-
-		this.signup = () => {
-			this.update({
-				mode: 'signup'
-			});
-		};
-
-		this.signin = () => {
-			this.update({
-				mode: 'signin'
-			});
-		};
-
-		this.introduction = () => {
-			this.update({
-				mode: 'introduction'
-			});
-		};
-	</script>
-</mk-entrance>
diff --git a/src/web/app/mobile/tags/page/entrance/signin.tag b/src/web/app/mobile/tags/page/entrance/signin.tag
deleted file mode 100644
index e6deea8c3..000000000
--- a/src/web/app/mobile/tags/page/entrance/signin.tag
+++ /dev/null
@@ -1,52 +0,0 @@
-<mk-entrance-signin>
-	<mk-signin/>
-	<a href={ _API_URL_ + '/signin/twitter' }>Twitterでサインイン</a>
-	<div class="divider"><span>or</span></div>
-	<button class="signup" @click="parent.signup">%i18n:mobile.tags.mk-entrance-signin.signup%</button><a class="introduction" @click="parent.introduction">%i18n:mobile.tags.mk-entrance-signin.about%</a>
-	<style lang="stylus" scoped>
-		:scope
-			display block
-			margin 0 auto
-			padding 0 8px
-			max-width 350px
-			text-align center
-
-			> .signup
-				padding 16px
-				width 100%
-				font-size 1em
-				color #fff
-				background $theme-color
-				border-radius 3px
-
-			> .divider
-				padding 16px 0
-				text-align center
-
-				&:after
-					content ""
-					display block
-					position absolute
-					top 50%
-					width 100%
-					height 1px
-					border-top solid 1px rgba(0, 0, 0, 0.1)
-
-				> *
-					z-index 1
-					padding 0 8px
-					color rgba(0, 0, 0, 0.5)
-					background #fdfdfd
-
-			> .introduction
-				display inline-block
-				margin-top 16px
-				font-size 12px
-				color #666
-
-
-
-
-
-	</style>
-</mk-entrance-signin>
diff --git a/src/web/app/mobile/tags/page/entrance/signup.tag b/src/web/app/mobile/tags/page/entrance/signup.tag
deleted file mode 100644
index d219bb100..000000000
--- a/src/web/app/mobile/tags/page/entrance/signup.tag
+++ /dev/null
@@ -1,38 +0,0 @@
-<mk-entrance-signup>
-	<mk-signup/>
-	<button class="cancel" type="button" @click="parent.signin" title="%i18n:mobile.tags.mk-entrance-signup.cancel%">%fa:times%</button>
-	<style lang="stylus" scoped>
-		:scope
-			display block
-			margin 0 auto
-			padding 0 8px
-			max-width 350px
-
-			> .cancel
-				cursor pointer
-				display block
-				position absolute
-				top 0
-				right 0
-				z-index 1
-				margin 0
-				padding 0
-				font-size 1.2em
-				color #999
-				border none
-				outline none
-				box-shadow none
-				background transparent
-				transition opacity 0.1s ease
-
-				&:hover
-					color #555
-
-				&:active
-					color #222
-
-				> [data-fa]
-					padding 14px
-
-	</style>
-</mk-entrance-signup>
diff --git a/src/web/app/mobile/tags/page/settings/authorized-apps.tag b/src/web/app/mobile/tags/page/settings/authorized-apps.tag
deleted file mode 100644
index 35cc961f0..000000000
--- a/src/web/app/mobile/tags/page/settings/authorized-apps.tag
+++ /dev/null
@@ -1,17 +0,0 @@
-<mk-authorized-apps-page>
-	<mk-ui ref="ui">
-		<mk-authorized-apps/>
-	</mk-ui>
-	<style lang="stylus" scoped>
-		:scope
-			display block
-	</style>
-	<script lang="typescript">
-		import ui from '../../../scripts/ui-event';
-
-		this.on('mount', () => {
-			document.title = 'Misskey | %i18n:mobile.tags.mk-authorized-apps-page.application%';
-			ui.trigger('title', '%fa:puzzle-piece%%i18n:mobile.tags.mk-authorized-apps-page.application%');
-		});
-	</script>
-</mk-authorized-apps-page>
diff --git a/src/web/app/mobile/tags/page/settings/signin.tag b/src/web/app/mobile/tags/page/settings/signin.tag
deleted file mode 100644
index 7a57406c1..000000000
--- a/src/web/app/mobile/tags/page/settings/signin.tag
+++ /dev/null
@@ -1,17 +0,0 @@
-<mk-signin-history-page>
-	<mk-ui ref="ui">
-		<mk-signin-history/>
-	</mk-ui>
-	<style lang="stylus" scoped>
-		:scope
-			display block
-	</style>
-	<script lang="typescript">
-		import ui from '../../../scripts/ui-event';
-
-		this.on('mount', () => {
-			document.title = 'Misskey | %i18n:mobile.tags.mk-signin-history-page.signin-history%';
-			ui.trigger('title', '%fa:sign-in-alt%%i18n:mobile.tags.mk-signin-history-page.signin-history%');
-		});
-	</script>
-</mk-signin-history-page>
diff --git a/src/web/app/mobile/tags/page/settings/twitter.tag b/src/web/app/mobile/tags/page/settings/twitter.tag
deleted file mode 100644
index ca5fe2c43..000000000
--- a/src/web/app/mobile/tags/page/settings/twitter.tag
+++ /dev/null
@@ -1,17 +0,0 @@
-<mk-twitter-setting-page>
-	<mk-ui ref="ui">
-		<mk-twitter-setting/>
-	</mk-ui>
-	<style lang="stylus" scoped>
-		:scope
-			display block
-	</style>
-	<script lang="typescript">
-		import ui from '../../../scripts/ui-event';
-
-		this.on('mount', () => {
-			document.title = 'Misskey | %i18n:mobile.tags.mk-twitter-setting-page.twitter-integration%';
-			ui.trigger('title', '%fa:B twitter%%i18n:mobile.tags.mk-twitter-setting-page.twitter-integration%');
-		});
-	</script>
-</mk-twitter-setting-page>