From 82671161e8c6f4e861b0b8edf632e1fb27143377 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Thu, 8 Jun 2017 04:49:42 +0900
Subject: [PATCH 01/75] :art:

---
 src/web/app/common/tags/activity-table.tag      | 17 ++++++++++++-----
 .../app/desktop/tags/home-widgets/activity.tag  | 17 ++++++++++++-----
 2 files changed, 24 insertions(+), 10 deletions(-)

diff --git a/src/web/app/common/tags/activity-table.tag b/src/web/app/common/tags/activity-table.tag
index 49c5fe26c..2ecfc3b11 100644
--- a/src/web/app/common/tags/activity-table.tag
+++ b/src/web/app/common/tags/activity-table.tag
@@ -2,7 +2,15 @@
 	<svg if={ data } ref="canvas" viewBox="0 0 53 7" preserveAspectRatio="none">
 		<rect each={ data } width="1" height="1"
 			riot-x={ x } riot-y={ date.weekday }
-			fill={ color }></rect>
+			rx="1" ry="1"
+			fill={ color }
+			style="transform: scale({ v });"/>
+		<rect class="today" width="1" height="1"
+			riot-x={ data[data.length - 1].x } riot-y={ data[data.length - 1].date.weekday }
+			rx="1" ry="1"
+			fill="none"
+			stroke-width="0.1"
+			stroke="#f73520"/>
 	</svg>
 	<style>
 		:scope
@@ -16,7 +24,6 @@
 
 				> rect
 					transform-origin center
-					transform scale(0.8)
 
 	</style>
 	<script>
@@ -33,9 +40,9 @@
 				let x = 0;
 				data.reverse().forEach(d => {
 					d.x = x;
-					let v = d.total / this.peak;
-					if (v > 1) v = 1;
-					d.color = `hsl(180, ${v * 100}%, ${15 + ((1 - v) * 80)}%)`;
+					d.v = d.total / this.peak;
+					if (d.v > 1) d.v = 1;
+					d.color = `hsl(170, ${d.v * 100}%, ${15 + ((1 - d.v) * 80)}%)`;
 					d.date.weekday = (new Date(d.date.year, d.date.month - 1, d.date.day)).getDay();
 					if (d.date.weekday == 6) x++;
 				});
diff --git a/src/web/app/desktop/tags/home-widgets/activity.tag b/src/web/app/desktop/tags/home-widgets/activity.tag
index ba201e1c2..3abfd12c5 100644
--- a/src/web/app/desktop/tags/home-widgets/activity.tag
+++ b/src/web/app/desktop/tags/home-widgets/activity.tag
@@ -4,7 +4,15 @@
 	<svg if={ !initializing } ref="canvas" viewBox="0 0 21 7" preserveAspectRatio="none">
 		<rect each={ data } width="1" height="1"
 			riot-x={ x } riot-y={ date.weekday }
-			fill={ color }></rect>
+			rx="1" ry="1"
+			fill={ color }
+			style="transform: scale({ v });"/>
+		<rect class="today" width="1" height="1"
+			riot-x={ data[data.length - 1].x } riot-y={ data[data.length - 1].date.weekday }
+			rx="1" ry="1"
+			fill="none"
+			stroke-width="0.1"
+			stroke="#f73520"/>
 	</svg>
 	<style>
 		:scope
@@ -40,7 +48,6 @@
 
 				> rect
 					transform-origin center
-					transform scale(0.8)
 
 	</style>
 	<script>
@@ -59,9 +66,9 @@
 				let x = 0;
 				data.reverse().forEach(d => {
 					d.x = x;
-					let v = d.total / this.peak;
-					if (v > 1) v = 1;
-					d.color = `hsl(180, ${v * 100}%, ${15 + ((1 - v) * 80)}%)`;
+					d.v = d.total / this.peak;
+					if (d.v > 1) d.v = 1;
+					d.color = `hsl(170, ${d.v * 100}%, ${15 + ((1 - d.v) * 80)}%)`;
 					d.date.weekday = (new Date(d.date.year, d.date.month - 1, d.date.day)).getDay();
 					if (d.date.weekday == 6) x++;
 				});

From 2b741dc8f8cc51dbcc6910cb0df622eaa327d5eb Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Thu, 8 Jun 2017 04:50:12 +0900
Subject: [PATCH 02/75] v2029

---
 package.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/package.json b/package.json
index a128185f6..b5d6c1b52 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
 {
 	"name": "misskey",
 	"author": "syuilo <i@syuilo.com>",
-	"version": "0.0.2027",
+	"version": "0.0.2029",
 	"license": "MIT",
 	"description": "A miniblog-based SNS",
 	"bugs": "https://github.com/syuilo/misskey/issues",

From acc9e07e93df2e0867b21a20065de8ae2ee9e51b Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Thu, 8 Jun 2017 06:13:52 +0900
Subject: [PATCH 03/75] [Client] Improve activity home widget

---
 locales/en.yml                                |  1 +
 locales/ja.yml                                |  1 +
 .../desktop/tags/home-widgets/activity.tag    | 69 ++++++++++++++++---
 3 files changed, 63 insertions(+), 8 deletions(-)

diff --git a/locales/en.yml b/locales/en.yml
index 74ff52979..6104cbdc9 100644
--- a/locales/en.yml
+++ b/locales/en.yml
@@ -245,6 +245,7 @@ desktop:
 
     mk-activity-home-widget:
       title: "Activity"
+      toggle: "Toggle views"
 
     mk-user-recommendation-home-widget:
       title: "Recommended users"
diff --git a/locales/ja.yml b/locales/ja.yml
index dda0da585..e83c0eb72 100644
--- a/locales/ja.yml
+++ b/locales/ja.yml
@@ -245,6 +245,7 @@ desktop:
 
     mk-activity-home-widget:
       title: "アクティビティ"
+      toggle: "表示を切り替え"
 
     mk-user-recommendation-home-widget:
       title: "おすすめユーザー"
diff --git a/src/web/app/desktop/tags/home-widgets/activity.tag b/src/web/app/desktop/tags/home-widgets/activity.tag
index 3abfd12c5..408388b8f 100644
--- a/src/web/app/desktop/tags/home-widgets/activity.tag
+++ b/src/web/app/desktop/tags/home-widgets/activity.tag
@@ -1,19 +1,44 @@
 <mk-activity-home-widget>
 	<p class="title"><i class="fa fa-bar-chart"></i>%i18n:desktop.tags.mk-activity-home-widget.title%</p>
+	<button onclick={ toggle } title="%i18n:desktop.tags.mk-activity-home-widget.toggle%"><i class="fa fa-sort"></i></button>
 	<p class="initializing" if={ initializing }><i class="fa fa-spinner fa-pulse fa-fw"></i>%i18n:common.loading%<mk-ellipsis/></p>
-	<svg if={ !initializing } ref="canvas" viewBox="0 0 21 7" preserveAspectRatio="none">
-		<rect each={ data } width="1" height="1"
+	<svg if={ !initializing && view == 0 } class="calender" viewBox="0 0 21 7" preserveAspectRatio="none">
+		<rect each={ data }
+			width="1" height="1"
 			riot-x={ x } riot-y={ date.weekday }
 			rx="1" ry="1"
 			fill={ color }
 			style="transform: scale({ v });"/>
-		<rect class="today" width="1" height="1"
+		<rect class="today"
+			width="1" height="1"
 			riot-x={ data[data.length - 1].x } riot-y={ data[data.length - 1].date.weekday }
 			rx="1" ry="1"
 			fill="none"
 			stroke-width="0.1"
 			stroke="#f73520"/>
 	</svg>
+	<svg if={ !initializing && view == 1 } class="chart" viewBox="0 0 140 60" preserveAspectRatio="none">
+		<polyline
+			riot-points={ chartPointsPost }
+			fill="none"
+			stroke-width="1"
+			stroke="#41ddde"/>
+		<polyline
+			riot-points={ chartPointsReply }
+			fill="none"
+			stroke-width="1"
+			stroke="#f7796c"/>
+		<polyline
+			riot-points={ chartPointsRepost }
+			fill="none"
+			stroke-width="1"
+			stroke="#a1de41"/>
+		<polyline
+			riot-points={ chartPointsTotal }
+			fill="none"
+			stroke-width="1"
+			stroke="#555"/>
+	</svg>
 	<style>
 		:scope
 			display block
@@ -32,6 +57,23 @@
 				> i
 					margin-right 4px
 
+			> button
+				position absolute
+				z-index 2
+				top 0
+				right 0
+				padding 0
+				width 42px
+				font-size 0.9em
+				line-height 42px
+				color #ccc
+
+				&:hover
+					color #aaa
+
+				&:active
+					color #999
+
 			> .initializing
 				margin 0
 				padding 16px
@@ -46,8 +88,9 @@
 				padding 10px
 				width 100%
 
-				> rect
-					transform-origin center
+				&.calender
+					> rect
+						transform-origin center
 
 	</style>
 	<script>
@@ -55,6 +98,7 @@
 		this.mixin('api');
 
 		this.initializing = true;
+		this.view = 0;
 
 		this.on('mount', () => {
 			this.api('aggregation/users/activity', {
@@ -62,11 +106,11 @@
 				limit: 20 * 7
 			}).then(data => {
 				data.forEach(d => d.total = d.posts + d.replies + d.reposts);
-				this.peak = Math.max.apply(null, data.map(d => d.total)) / 2;
+				const peak = Math.max.apply(null, data.map(d => d.total));
 				let x = 0;
 				data.reverse().forEach(d => {
 					d.x = x;
-					d.v = d.total / this.peak;
+					d.v = d.total / (peak / 2);
 					if (d.v > 1) d.v = 1;
 					d.color = `hsl(170, ${d.v * 100}%, ${15 + ((1 - d.v) * 80)}%)`;
 					d.date.weekday = (new Date(d.date.year, d.date.month - 1, d.date.day)).getDay();
@@ -75,9 +119,18 @@
 
 				this.update({
 					initializing: false,
-					data
+					data,
+					chartPointsPost: data.map((d, i) => `${i},${(1 - (d.posts / peak)) * 60}`).join(' '),
+					chartPointsReply: data.map((d, i) => `${i},${(1 - (d.replies / peak)) * 60}`).join(' '),
+					chartPointsRepost: data.map((d, i) => `${i},${(1 - (d.reposts / peak)) * 60}`).join(' '),
+					chartPointsTotal: data.map((d, i) => `${i},${(1 - (d.total / peak)) * 60}`).join(' ')
 				});
 			});
 		});
+
+		this.toggle = () => {
+			this.view++;
+			if (this.view == 2) this.view = 0;
+		};
 	</script>
 </mk-activity-home-widget>

From bae7cdd6fbf8a22d0963d54bef3d3c24f736a191 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Thu, 8 Jun 2017 06:14:04 +0900
Subject: [PATCH 04/75] v2031

---
 package.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/package.json b/package.json
index b5d6c1b52..d4a5e9605 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
 {
 	"name": "misskey",
 	"author": "syuilo <i@syuilo.com>",
-	"version": "0.0.2029",
+	"version": "0.0.2031",
 	"license": "MIT",
 	"description": "A miniblog-based SNS",
 	"bugs": "https://github.com/syuilo/misskey/issues",

From a8e06a1825d24ea81fd6a56bc0d00c98f735d4a0 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Thu, 8 Jun 2017 16:03:48 +0900
Subject: [PATCH 05/75] [Client] Improve activity home widget

---
 .../desktop/tags/home-widgets/activity.tag    | 201 ++++++++++++------
 1 file changed, 138 insertions(+), 63 deletions(-)

diff --git a/src/web/app/desktop/tags/home-widgets/activity.tag b/src/web/app/desktop/tags/home-widgets/activity.tag
index 408388b8f..b324e1ede 100644
--- a/src/web/app/desktop/tags/home-widgets/activity.tag
+++ b/src/web/app/desktop/tags/home-widgets/activity.tag
@@ -2,43 +2,8 @@
 	<p class="title"><i class="fa fa-bar-chart"></i>%i18n:desktop.tags.mk-activity-home-widget.title%</p>
 	<button onclick={ toggle } title="%i18n:desktop.tags.mk-activity-home-widget.toggle%"><i class="fa fa-sort"></i></button>
 	<p class="initializing" if={ initializing }><i class="fa fa-spinner fa-pulse fa-fw"></i>%i18n:common.loading%<mk-ellipsis/></p>
-	<svg if={ !initializing && view == 0 } class="calender" viewBox="0 0 21 7" preserveAspectRatio="none">
-		<rect each={ data }
-			width="1" height="1"
-			riot-x={ x } riot-y={ date.weekday }
-			rx="1" ry="1"
-			fill={ color }
-			style="transform: scale({ v });"/>
-		<rect class="today"
-			width="1" height="1"
-			riot-x={ data[data.length - 1].x } riot-y={ data[data.length - 1].date.weekday }
-			rx="1" ry="1"
-			fill="none"
-			stroke-width="0.1"
-			stroke="#f73520"/>
-	</svg>
-	<svg if={ !initializing && view == 1 } class="chart" viewBox="0 0 140 60" preserveAspectRatio="none">
-		<polyline
-			riot-points={ chartPointsPost }
-			fill="none"
-			stroke-width="1"
-			stroke="#41ddde"/>
-		<polyline
-			riot-points={ chartPointsReply }
-			fill="none"
-			stroke-width="1"
-			stroke="#f7796c"/>
-		<polyline
-			riot-points={ chartPointsRepost }
-			fill="none"
-			stroke-width="1"
-			stroke="#a1de41"/>
-		<polyline
-			riot-points={ chartPointsTotal }
-			fill="none"
-			stroke-width="1"
-			stroke="#555"/>
-	</svg>
+	<mk-activity-home-widget-calender if={ !initializing && view == 0 } data={ [].concat(data) }/>
+	<mk-activity-home-widget-chart if={ !initializing && view == 1 } data={ [].concat(data) }/>
 	<style>
 		:scope
 			display block
@@ -83,15 +48,6 @@
 				> i
 					margin-right 4px
 
-			> svg
-				display block
-				padding 10px
-				width 100%
-
-				&.calender
-					> rect
-						transform-origin center
-
 	</style>
 	<script>
 		this.mixin('i');
@@ -105,25 +61,9 @@
 				user_id: this.I.id,
 				limit: 20 * 7
 			}).then(data => {
-				data.forEach(d => d.total = d.posts + d.replies + d.reposts);
-				const peak = Math.max.apply(null, data.map(d => d.total));
-				let x = 0;
-				data.reverse().forEach(d => {
-					d.x = x;
-					d.v = d.total / (peak / 2);
-					if (d.v > 1) d.v = 1;
-					d.color = `hsl(170, ${d.v * 100}%, ${15 + ((1 - d.v) * 80)}%)`;
-					d.date.weekday = (new Date(d.date.year, d.date.month - 1, d.date.day)).getDay();
-					if (d.date.weekday == 6) x++;
-				});
-
 				this.update({
 					initializing: false,
-					data,
-					chartPointsPost: data.map((d, i) => `${i},${(1 - (d.posts / peak)) * 60}`).join(' '),
-					chartPointsReply: data.map((d, i) => `${i},${(1 - (d.replies / peak)) * 60}`).join(' '),
-					chartPointsRepost: data.map((d, i) => `${i},${(1 - (d.reposts / peak)) * 60}`).join(' '),
-					chartPointsTotal: data.map((d, i) => `${i},${(1 - (d.total / peak)) * 60}`).join(' ')
+					data
 				});
 			});
 		});
@@ -134,3 +74,138 @@
 		};
 	</script>
 </mk-activity-home-widget>
+
+<mk-activity-home-widget-calender>
+	<svg viewBox="0 0 21 7" preserveAspectRatio="none">
+		<rect each={ data }
+			width="1" height="1"
+			riot-x={ x } riot-y={ date.weekday }
+			rx="1" ry="1"
+			fill={ color }
+			style="transform: scale({ v });"/>
+		<rect class="today"
+			width="1" height="1"
+			riot-x={ data[data.length - 1].x } riot-y={ data[data.length - 1].date.weekday }
+			rx="1" ry="1"
+			fill="none"
+			stroke-width="0.1"
+			stroke="#f73520"/>
+	</svg>
+	<style>
+		:scope
+			> svg
+				display block
+				padding 10px
+				width 100%
+
+				> rect
+					transform-origin center
+
+	</style>
+	<script>
+		this.data = this.opts.data;
+		this.data.forEach(d => d.total = d.posts + d.replies + d.reposts);
+		const peak = Math.max.apply(null, this.data.map(d => d.total));
+
+		let x = 0;
+		this.data.reverse().forEach(d => {
+			d.x = x;
+			d.v = d.total / (peak / 2);
+			if (d.v > 1) d.v = 1;
+			d.color = `hsl(170, ${d.v * 100}%, ${15 + ((1 - d.v) * 80)}%)`;
+			d.date.weekday = (new Date(d.date.year, d.date.month - 1, d.date.day)).getDay();
+			if (d.date.weekday == 6) x++;
+		});
+	</script>
+</mk-activity-home-widget-calender>
+
+<mk-activity-home-widget-chart>
+	<svg riot-viewBox="0 0 { viewBoxX } 60" preserveAspectRatio="none" onmousedown={ onMousedown }>
+		<polyline
+			riot-points={ pointsPost }
+			fill="none"
+			stroke-width="1"
+			stroke="#41ddde"/>
+		<polyline
+			riot-points={ pointsReply }
+			fill="none"
+			stroke-width="1"
+			stroke="#f7796c"/>
+		<polyline
+			riot-points={ pointsRepost }
+			fill="none"
+			stroke-width="1"
+			stroke="#a1de41"/>
+		<polyline
+			riot-points={ pointsTotal }
+			fill="none"
+			stroke-width="1"
+			stroke="#555"/>
+	</svg>
+	<style>
+		:scope
+			> svg
+				display block
+				padding 10px
+				width 100%
+				cursor all-scroll
+	</style>
+	<script>
+		this.viewBoxX = 140;
+		this.zoom = 1;
+		this.pos = 0;
+
+		this.data = this.opts.data.reverse();
+		this.data.forEach(d => d.total = d.posts + d.replies + d.reposts);
+		const peak = Math.max.apply(null, this.data.map(d => d.total));
+
+		this.on('mount', () => {
+			this.render();
+		});
+
+		this.render = () => {
+			this.update({
+				pointsPost: this.data.map((d, i) => `${(i * this.zoom) + this.pos},${(1 - (d.posts / peak)) * 60}`).join(' '),
+				pointsReply: this.data.map((d, i) => `${(i * this.zoom) + this.pos},${(1 - (d.replies / peak)) * 60}`).join(' '),
+				pointsRepost: this.data.map((d, i) => `${(i * this.zoom) + this.pos},${(1 - (d.reposts / peak)) * 60}`).join(' '),
+				pointsTotal: this.data.map((d, i) => `${(i * this.zoom) + this.pos},${(1 - (d.total / peak)) * 60}`).join(' ')
+			});
+		};
+
+		this.onMousedown = e => {
+			e.preventDefault();
+
+			const clickX = e.clientX;
+			const clickY = e.clientY;
+			const baseZoom = this.zoom;
+			const basePos = this.pos;
+
+			// 動かした時
+			dragListen(me => {
+				let moveLeft = me.clientX - clickX;
+				let moveTop = me.clientY - clickY;
+
+				this.zoom = baseZoom + (moveTop / 20);
+				this.pos = basePos + moveLeft;
+				if (this.zoom < 1) this.zoom = 1;
+				if (this.pos > 0) this.pos = 0;
+				if (this.pos < -((this.data.length * this.zoom) - this.viewBoxX)) this.pos = -((this.data.length * this.zoom) - this.viewBoxX);
+
+				this.render();
+			});
+		};
+
+		function dragListen(fn) {
+			window.addEventListener('mousemove',  fn);
+			window.addEventListener('mouseleave', dragClear.bind(null, fn));
+			window.addEventListener('mouseup',    dragClear.bind(null, fn));
+		}
+
+		function dragClear(fn) {
+			window.removeEventListener('mousemove',  fn);
+			window.removeEventListener('mouseleave', dragClear);
+			window.removeEventListener('mouseup',    dragClear);
+		}
+	</script>
+</mk-activity-home-widget-chart>
+

From 7190dc1f552cd94bc717f8fe46aa2961139b362a Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Thu, 8 Jun 2017 16:04:00 +0900
Subject: [PATCH 06/75] v2033

---
 package.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/package.json b/package.json
index d4a5e9605..3614e9565 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
 {
 	"name": "misskey",
 	"author": "syuilo <i@syuilo.com>",
-	"version": "0.0.2031",
+	"version": "0.0.2033",
 	"license": "MIT",
 	"description": "A miniblog-based SNS",
 	"bugs": "https://github.com/syuilo/misskey/issues",

From 0da5d3d35e28e44098571ce597c805ad9baa20c3 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Thu, 8 Jun 2017 16:13:53 +0900
Subject: [PATCH 07/75] Fix bug

---
 src/web/app/desktop/tags/home-widgets/activity.tag | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/web/app/desktop/tags/home-widgets/activity.tag b/src/web/app/desktop/tags/home-widgets/activity.tag
index b324e1ede..39878e91d 100644
--- a/src/web/app/desktop/tags/home-widgets/activity.tag
+++ b/src/web/app/desktop/tags/home-widgets/activity.tag
@@ -185,11 +185,11 @@
 				let moveLeft = me.clientX - clickX;
 				let moveTop = me.clientY - clickY;
 
-				this.zoom = baseZoom + (moveTop / 20);
+				this.zoom = baseZoom + (-moveTop / 20);
 				this.pos = basePos + moveLeft;
 				if (this.zoom < 1) this.zoom = 1;
 				if (this.pos > 0) this.pos = 0;
-				if (this.pos < -((this.data.length * this.zoom) - this.viewBoxX)) this.pos = -((this.data.length * this.zoom) - this.viewBoxX);
+				if (this.pos < -(((this.data.length - 1) * this.zoom) - this.viewBoxX)) this.pos = -(((this.data.length - 1) * this.zoom) - this.viewBoxX);
 
 				this.render();
 			});

From 9a2670c722e2870c8ae604f5c9d2ca9d5b3a0ffb Mon Sep 17 00:00:00 2001
From: "greenkeeper[bot]" <greenkeeper[bot]@users.noreply.github.com>
Date: Thu, 8 Jun 2017 08:24:43 +0000
Subject: [PATCH 08/75] fix(package): update riot to version 3.6.0

---
 package.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/package.json b/package.json
index 3614e9565..373750f3f 100644
--- a/package.json
+++ b/package.json
@@ -133,7 +133,7 @@
 		"redis": "2.7.1",
 		"request": "2.81.0",
 		"rimraf": "2.6.1",
-		"riot": "3.5.1",
+		"riot": "3.6.0",
 		"rndstr": "1.0.0",
 		"s-age": "1.1.0",
 		"serve-favicon": "2.4.3",

From 3a78e08bac1a49f32a0b8145feaefcd01df88fea Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Thu, 8 Jun 2017 17:37:51 +0900
Subject: [PATCH 09/75] [Client] :art:

---
 src/web/app/desktop/tags/notifications.tag | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/src/web/app/desktop/tags/notifications.tag b/src/web/app/desktop/tags/notifications.tag
index ce24ac437..21e4fe7fa 100644
--- a/src/web/app/desktop/tags/notifications.tag
+++ b/src/web/app/desktop/tags/notifications.tag
@@ -63,7 +63,7 @@
 			<p class="date" if={ i != notifications.length - 1 && notification._date != notifications[i + 1]._date }><span><i class="fa fa-angle-up"></i>{ notification._datetext }</span><span><i class="fa fa-angle-down"></i>{ notifications[i + 1]._datetext }</span></p>
 		</virtual>
 	</div>
-	<button class="more" if={ moreNotifications } onclick={ fetchMoreNotifications } disabled={ fetchingMoreNotifications }>
+	<button class="more { fetching: fetchingMoreNotifications }" if={ moreNotifications } onclick={ fetchMoreNotifications } disabled={ fetchingMoreNotifications }>
 		<i class="fa fa-spinner fa-pulse fa-fw" if={ fetchingMoreNotifications }></i>{ fetchingMoreNotifications ? '%i18n:common.loading%' : '%i18n:desktop.tags.mk-notifications.more%' }
 	</button>
 	<p class="empty" if={ notifications.length == 0 && !loading }>ありません!</p>
@@ -184,6 +184,9 @@
 				&:active
 					background rgba(0, 0, 0, 0.05)
 
+				&.fetching
+					cursor wait
+
 				> i
 					margin-right 4px
 

From 4e96a490f85bbf6d17e1e654d8ef5d24450f88cb Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Thu, 8 Jun 2017 17:55:41 +0900
Subject: [PATCH 10/75] [Client] Improve activity home widget

---
 src/web/app/desktop/tags/home-widgets/activity.tag | 13 ++++++++++++-
 1 file changed, 12 insertions(+), 1 deletion(-)

diff --git a/src/web/app/desktop/tags/home-widgets/activity.tag b/src/web/app/desktop/tags/home-widgets/activity.tag
index 39878e91d..621702fa1 100644
--- a/src/web/app/desktop/tags/home-widgets/activity.tag
+++ b/src/web/app/desktop/tags/home-widgets/activity.tag
@@ -77,12 +77,19 @@
 
 <mk-activity-home-widget-calender>
 	<svg viewBox="0 0 21 7" preserveAspectRatio="none">
+		<rect each={ data } class="day"
+			width="1" height="1"
+			riot-x={ x } riot-y={ date.weekday }
+			rx="1" ry="1"
+			fill="transparent">
+			<title>{ date.year }/{ date.month }/{ date.day }<br/>Post: { posts }, Reply: { replies }, Repost: { reposts }</title>
+		</rect>
 		<rect each={ data }
 			width="1" height="1"
 			riot-x={ x } riot-y={ date.weekday }
 			rx="1" ry="1"
 			fill={ color }
-			style="transform: scale({ v });"/>
+			style="pointer-events: none; transform: scale({ v });"/>
 		<rect class="today"
 			width="1" height="1"
 			riot-x={ data[data.length - 1].x } riot-y={ data[data.length - 1].date.weekday }
@@ -101,6 +108,10 @@
 				> rect
 					transform-origin center
 
+					&.day
+						&:hover
+							fill rgba(0, 0, 0, 0.05)
+
 	</style>
 	<script>
 		this.data = this.opts.data;

From f93eb0020724e255fe52bc5e7b99c8df71bf07f1 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Thu, 8 Jun 2017 17:55:57 +0900
Subject: [PATCH 11/75] v2038

---
 package.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/package.json b/package.json
index 373750f3f..fd10ea50c 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
 {
 	"name": "misskey",
 	"author": "syuilo <i@syuilo.com>",
-	"version": "0.0.2033",
+	"version": "0.0.2038",
 	"license": "MIT",
 	"description": "A miniblog-based SNS",
 	"bugs": "https://github.com/syuilo/misskey/issues",

From 4e5545af384f610a56b3d19ea73c3b801b0be6c6 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Fri, 9 Jun 2017 01:03:54 +0900
Subject: [PATCH 12/75] nanka iroiro

---
 locales/en.yml                                |   4 +
 locales/ja.yml                                |   4 +
 package.json                                  | 299 +++++++++---------
 src/api/stream/server.ts                      |  20 ++
 src/api/streaming.ts                          |   6 +
 src/index.ts                                  |   6 +
 src/utils/stats.ts                            |  25 ++
 src/web/app/common/scripts/home-stream.js     |  18 ++
 .../app/common/scripts/messaging-stream.js    |  52 +--
 src/web/app/common/scripts/server-stream.js   |  14 +
 src/web/app/common/scripts/stream.js          |  15 +-
 src/web/app/common/tags/messaging/room.tag    |  16 +-
 .../desktop/tags/home-widgets/activity.tag    |  15 +-
 .../app/desktop/tags/home-widgets/server.tag  | 177 +++++++++++
 src/web/app/desktop/tags/home.tag             |   1 +
 src/web/app/desktop/tags/index.js             |   1 +
 src/web/app/init.js                           |   4 +-
 17 files changed, 472 insertions(+), 205 deletions(-)
 create mode 100644 src/api/stream/server.ts
 create mode 100644 src/utils/stats.ts
 create mode 100644 src/web/app/common/scripts/home-stream.js
 create mode 100644 src/web/app/common/scripts/server-stream.js
 create mode 100644 src/web/app/desktop/tags/home-widgets/server.tag

diff --git a/locales/en.yml b/locales/en.yml
index 6104cbdc9..e15a7c9ae 100644
--- a/locales/en.yml
+++ b/locales/en.yml
@@ -243,6 +243,10 @@ desktop:
       title: "Notifications"
       settings: "Notification settings"
 
+    mk-server-home-widget:
+      title: "Server info"
+      toggle: "Toggle views"
+
     mk-activity-home-widget:
       title: "Activity"
       toggle: "Toggle views"
diff --git a/locales/ja.yml b/locales/ja.yml
index e83c0eb72..5ed1d4f1e 100644
--- a/locales/ja.yml
+++ b/locales/ja.yml
@@ -243,6 +243,10 @@ desktop:
       title: "通知"
       settings: "通知の設定"
 
+    mk-server-home-widget:
+      title: "サーバー情報"
+      toggle: "表示を切り替え"
+
     mk-activity-home-widget:
       title: "アクティビティ"
       toggle: "表示を切り替え"
diff --git a/package.json b/package.json
index fd10ea50c..93f87561f 100644
--- a/package.json
+++ b/package.json
@@ -1,150 +1,153 @@
 {
-	"name": "misskey",
-	"author": "syuilo <i@syuilo.com>",
-	"version": "0.0.2038",
-	"license": "MIT",
-	"description": "A miniblog-based SNS",
-	"bugs": "https://github.com/syuilo/misskey/issues",
-	"repository": "https://github.com/syuilo/misskey.git",
-	"main": "./built/index.js",
-	"private": true,
-	"scripts": {
-		"config": "node ./tools/init.js",
-		"start": "node ./built",
-		"debug": "DEBUG=misskey:* node ./built",
-		"swagger": "node ./swagger.js",
-		"build": "gulp build",
-		"rebuild": "gulp rebuild",
-		"clean": "gulp clean",
-		"cleanall": "gulp cleanall",
-		"lint": "gulp lint",
-		"test": "gulp test"
-	},
-	"devDependencies": {
-		"@types/bcryptjs": "2.4.0",
-		"@types/body-parser": "1.16.3",
-		"@types/chai": "4.0.0",
-		"@types/chai-http": "0.0.30",
-		"@types/chalk": "0.4.31",
-		"@types/compression": "0.0.33",
-		"@types/cors": "2.8.1",
-		"@types/debug": "0.0.29",
-		"@types/deep-equal": "1.0.0",
-		"@types/elasticsearch": "5.0.13",
-		"@types/event-stream": "3.3.31",
-		"@types/express": "4.0.35",
-		"@types/gm": "1.17.31",
-		"@types/gulp": "4.0.3",
-		"@types/gulp-mocha": "0.0.30",
-		"@types/gulp-rename": "0.0.32",
-		"@types/gulp-replace": "0.0.30",
-		"@types/gulp-tslint": "3.6.31",
-		"@types/gulp-typescript": "2.13.0",
-		"@types/gulp-uglify": "0.0.30",
-		"@types/gulp-util": "3.0.31",
-		"@types/inquirer": "0.0.34",
-		"@types/is-root": "1.0.0",
-		"@types/is-url": "1.2.28",
-		"@types/js-yaml": "3.5.30",
-		"@types/mocha": "2.2.41",
-		"@types/mongodb": "2.2.3",
-		"@types/monk": "1.0.5",
-		"@types/morgan": "1.7.32",
-		"@types/ms": "0.7.29",
-		"@types/multer": "0.0.34",
-		"@types/node": "7.0.29",
-		"@types/ratelimiter": "2.1.28",
-		"@types/redis": "2.6.0",
-		"@types/request": "0.0.43",
-		"@types/rimraf": "0.0.28",
-		"@types/riot": "2.6.2",
-		"@types/serve-favicon": "2.2.28",
-		"@types/uuid": "3.0.0",
-		"@types/webpack": "2.2.15",
-		"@types/webpack-stream": "3.2.7",
-		"@types/websocket": "0.0.33",
-		"chai": "4.0.2",
-		"chai-http": "3.0.0",
-		"css-loader": "0.28.4",
-		"event-stream": "3.3.4",
-		"gulp": "3.9.1",
-		"gulp-cssnano": "2.1.2",
-		"gulp-imagemin": "3.3.0",
-		"gulp-mocha": "4.3.1",
-		"gulp-pug": "3.3.0",
-		"gulp-rename": "1.2.2",
-		"gulp-replace": "0.5.4",
-		"gulp-tslint": "8.1.1",
-		"gulp-typescript": "3.1.7",
-		"gulp-uglify": "3.0.0",
-		"gulp-util": "3.0.8",
-		"mocha": "3.4.2",
-		"riot-tag-loader": "1.0.0",
-		"string-replace-webpack-plugin": "0.1.3",
-		"style-loader": "0.18.2",
-		"stylus": "0.54.5",
-		"stylus-loader": "3.0.1",
-		"swagger-jsdoc": "1.9.4",
-		"tslint": "5.4.3",
-		"uglify-es": "3.0.15",
-		"uglify-es-webpack-plugin": "0.0.2",
-		"uglify-js": "git+https://github.com/mishoo/UglifyJS2.git#harmony",
-		"webpack": "2.6.1"
-	},
-	"dependencies": {
-		"accesses": "2.5.0",
-		"animejs": "2.0.2",
-		"autwh": "0.0.1",
-		"bcryptjs": "2.4.3",
-		"body-parser": "1.17.2",
-		"cafy": "2.4.0",
-		"chalk": "1.1.3",
-		"compression": "1.6.2",
-		"cors": "2.8.3",
-		"cropperjs": "1.0.0-rc.2",
-		"crypto": "0.0.3",
-		"debug": "2.6.8",
-		"deep-equal": "1.0.1",
-		"deepcopy": "0.6.3",
-		"download": "6.2.2",
-		"elasticsearch": "13.0.1",
-		"escape-regexp": "0.0.1",
-		"express": "4.15.3",
-		"file-type": "5.0.0",
-		"fuckadblock": "3.2.1",
-		"gm": "1.23.0",
-		"inquirer": "3.1.0",
-		"is-root": "1.0.0",
-		"is-url": "1.2.2",
-		"js-yaml": "3.8.4",
-		"mongodb": "2.2.28",
-		"monk": "6.0.0",
-		"morgan": "1.8.2",
-		"ms": "2.0.0",
-		"multer": "1.3.0",
-		"nprogress": "0.2.0",
-		"page": "1.7.1",
-		"pictograph": "2.0.4",
-		"prominence": "0.2.0",
-		"pug": "2.0.0-rc.2",
-		"ratelimiter": "3.0.3",
-		"recaptcha-promise": "0.1.2",
-		"reconnecting-websocket": "3.0.5",
-		"redis": "2.7.1",
-		"request": "2.81.0",
-		"rimraf": "2.6.1",
-		"riot": "3.6.0",
-		"rndstr": "1.0.0",
-		"s-age": "1.1.0",
-		"serve-favicon": "2.4.3",
-		"summaly": "2.0.3",
-		"syuilo-password-strength": "0.0.1",
-		"tcp-port-used": "0.1.2",
-		"textarea-caret": "3.0.2",
-		"ts-node": "3.0.6",
-		"typescript": "2.3.4",
-		"uuid": "3.0.1",
-		"vhost": "3.0.2",
-		"websocket": "1.0.24"
-	}
+  "name": "misskey",
+  "author": "syuilo <i@syuilo.com>",
+  "version": "0.0.2038",
+  "license": "MIT",
+  "description": "A miniblog-based SNS",
+  "bugs": "https://github.com/syuilo/misskey/issues",
+  "repository": "https://github.com/syuilo/misskey.git",
+  "main": "./built/index.js",
+  "private": true,
+  "scripts": {
+    "config": "node ./tools/init.js",
+    "start": "node ./built",
+    "debug": "DEBUG=misskey:* node ./built",
+    "swagger": "node ./swagger.js",
+    "build": "gulp build",
+    "rebuild": "gulp rebuild",
+    "clean": "gulp clean",
+    "cleanall": "gulp cleanall",
+    "lint": "gulp lint",
+    "test": "gulp test"
+  },
+  "devDependencies": {
+    "@types/bcryptjs": "2.4.0",
+    "@types/body-parser": "1.16.3",
+    "@types/chai": "4.0.0",
+    "@types/chai-http": "0.0.30",
+    "@types/chalk": "0.4.31",
+    "@types/compression": "0.0.33",
+    "@types/cors": "2.8.1",
+    "@types/debug": "0.0.29",
+    "@types/deep-equal": "1.0.0",
+    "@types/elasticsearch": "5.0.13",
+    "@types/event-stream": "3.3.31",
+    "@types/express": "4.0.35",
+    "@types/gm": "1.17.31",
+    "@types/gulp": "4.0.3",
+    "@types/gulp-mocha": "0.0.30",
+    "@types/gulp-rename": "0.0.32",
+    "@types/gulp-replace": "0.0.30",
+    "@types/gulp-tslint": "3.6.31",
+    "@types/gulp-typescript": "2.13.0",
+    "@types/gulp-uglify": "0.0.30",
+    "@types/gulp-util": "3.0.31",
+    "@types/inquirer": "0.0.34",
+    "@types/is-root": "1.0.0",
+    "@types/is-url": "1.2.28",
+    "@types/js-yaml": "3.5.30",
+    "@types/mocha": "2.2.41",
+    "@types/mongodb": "2.2.3",
+    "@types/monk": "1.0.5",
+    "@types/morgan": "1.7.32",
+    "@types/ms": "0.7.29",
+    "@types/multer": "0.0.34",
+    "@types/node": "7.0.29",
+    "@types/ratelimiter": "2.1.28",
+    "@types/redis": "2.6.0",
+    "@types/request": "0.0.43",
+    "@types/rimraf": "0.0.28",
+    "@types/riot": "2.6.2",
+    "@types/serve-favicon": "2.2.28",
+    "@types/uuid": "3.0.0",
+    "@types/webpack": "2.2.15",
+    "@types/webpack-stream": "3.2.7",
+    "@types/websocket": "0.0.33",
+    "chai": "4.0.2",
+    "chai-http": "3.0.0",
+    "css-loader": "0.28.4",
+    "event-stream": "3.3.4",
+    "gulp": "3.9.1",
+    "gulp-cssnano": "2.1.2",
+    "gulp-imagemin": "3.3.0",
+    "gulp-mocha": "4.3.1",
+    "gulp-pug": "3.3.0",
+    "gulp-rename": "1.2.2",
+    "gulp-replace": "0.5.4",
+    "gulp-tslint": "8.1.1",
+    "gulp-typescript": "3.1.7",
+    "gulp-uglify": "3.0.0",
+    "gulp-util": "3.0.8",
+    "mocha": "3.4.2",
+    "riot-tag-loader": "1.0.0",
+    "string-replace-webpack-plugin": "0.1.3",
+    "style-loader": "0.18.2",
+    "stylus": "0.54.5",
+    "stylus-loader": "3.0.1",
+    "swagger-jsdoc": "1.9.4",
+    "tslint": "5.4.3",
+    "uglify-es": "3.0.15",
+    "uglify-es-webpack-plugin": "0.0.2",
+    "uglify-js": "git+https://github.com/mishoo/UglifyJS2.git#harmony",
+    "webpack": "2.6.1"
+  },
+  "dependencies": {
+    "accesses": "2.5.0",
+    "animejs": "2.0.2",
+    "autwh": "0.0.1",
+    "bcryptjs": "2.4.3",
+    "body-parser": "1.17.2",
+    "cafy": "2.4.0",
+    "chalk": "1.1.3",
+    "compression": "1.6.2",
+    "cors": "2.8.3",
+    "cropperjs": "1.0.0-rc.2",
+    "crypto": "0.0.3",
+    "debug": "2.6.8",
+    "deep-equal": "1.0.1",
+    "deepcopy": "0.6.3",
+    "diskusage": "^0.2.2",
+    "download": "6.2.2",
+    "elasticsearch": "13.0.1",
+    "escape-regexp": "0.0.1",
+    "express": "4.15.3",
+    "file-type": "5.0.0",
+    "fuckadblock": "3.2.1",
+    "gm": "1.23.0",
+    "inquirer": "3.1.0",
+    "is-root": "1.0.0",
+    "is-url": "1.2.2",
+    "js-yaml": "3.8.4",
+    "mongodb": "2.2.28",
+    "monk": "6.0.0",
+    "morgan": "1.8.2",
+    "ms": "2.0.0",
+    "multer": "1.3.0",
+    "nprogress": "0.2.0",
+    "os-utils": "0.0.14",
+    "page": "1.7.1",
+    "pictograph": "2.0.4",
+    "prominence": "0.2.0",
+    "pug": "2.0.0-rc.2",
+    "ratelimiter": "3.0.3",
+    "recaptcha-promise": "0.1.2",
+    "reconnecting-websocket": "3.0.5",
+    "redis": "2.7.1",
+    "request": "2.81.0",
+    "rimraf": "2.6.1",
+    "riot": "3.6.0",
+    "rndstr": "1.0.0",
+    "s-age": "1.1.0",
+    "serve-favicon": "2.4.3",
+    "summaly": "2.0.3",
+    "syuilo-password-strength": "0.0.1",
+    "tcp-port-used": "0.1.2",
+    "textarea-caret": "3.0.2",
+    "ts-node": "3.0.6",
+    "typescript": "2.3.4",
+    "uuid": "3.0.1",
+    "vhost": "3.0.2",
+    "websocket": "1.0.24",
+    "xev": "^2.0.0"
+  }
 }
diff --git a/src/api/stream/server.ts b/src/api/stream/server.ts
new file mode 100644
index 000000000..6de533749
--- /dev/null
+++ b/src/api/stream/server.ts
@@ -0,0 +1,20 @@
+import * as websocket from 'websocket';
+import Xev from 'xev';
+
+const ev = new Xev();
+
+export default function homeStream(request: websocket.request, connection: websocket.connection): void {
+	const onStats = stats => {
+		connection.send(JSON.stringify({
+			type: 'stats',
+			body: stats
+		}));
+	};
+
+	ev.addListener('stats', onStats);
+
+	connection.on('close', () => {
+		console.log('yooo');
+		ev.removeListener('stats', onStats);
+	});
+}
diff --git a/src/api/streaming.ts b/src/api/streaming.ts
index e1d79481d..c71132100 100644
--- a/src/api/streaming.ts
+++ b/src/api/streaming.ts
@@ -8,6 +8,7 @@ import isNativeToken from './common/is-native-token';
 
 import homeStream from './stream/home';
 import messagingStream from './stream/messaging';
+import serverStream from './stream/server';
 
 module.exports = (server: http.Server) => {
 	/**
@@ -20,6 +21,11 @@ module.exports = (server: http.Server) => {
 	ws.on('request', async (request) => {
 		const connection = request.accept();
 
+		if (request.resourceURL.pathname === '/server') {
+			serverStream(request, connection);
+			return;
+		}
+
 		const user = await authenticate(connection, request.resourceURL.query.i);
 
 		if (user == null) {
diff --git a/src/index.ts b/src/index.ts
index 0b568b5d4..da83dde10 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -12,17 +12,20 @@ import * as chalk from 'chalk';
 // import portUsed = require('tcp-port-used');
 import isRoot = require('is-root');
 import { master } from 'accesses';
+import Xev from 'xev';
 
 import Logger from './utils/logger';
 import ProgressBar from './utils/cli/progressbar';
 import EnvironmentInfo from './utils/environmentInfo';
 import MachineInfo from './utils/machineInfo';
 import DependencyInfo from './utils/dependencyInfo';
+import stats from './utils/stats';
 
 import { Config, path as configPath } from './config';
 import loadConfig from './config';
 
 const clusterLog = debug('misskey:cluster');
+const ev = new Xev();
 
 process.title = 'Misskey';
 
@@ -35,6 +38,9 @@ main();
 function main() {
 	if (cluster.isMaster) {
 		masterMain();
+
+		ev.mount();
+		stats();
 	} else {
 		workerMain();
 	}
diff --git a/src/utils/stats.ts b/src/utils/stats.ts
new file mode 100644
index 000000000..161526831
--- /dev/null
+++ b/src/utils/stats.ts
@@ -0,0 +1,25 @@
+import * as os from 'os';
+const osUtils = require('os-utils');
+import * as diskusage from 'diskusage';
+import Xev from 'xev';
+
+const ev = new Xev();
+
+/**
+ * Report stats regularly
+ */
+export default function() {
+	setInterval(() => {
+		osUtils.cpuUsage(cpuUsage => {
+			const disk = diskusage.checkSync(os.platform() == 'win32' ? 'c:' : '/');
+			ev.emit('stats', {
+				cpu_usage: cpuUsage,
+				mem: {
+					total: os.totalmem(),
+					free: os.freemem()
+				},
+				disk
+			});
+		});
+	}, 1000);
+}
diff --git a/src/web/app/common/scripts/home-stream.js b/src/web/app/common/scripts/home-stream.js
new file mode 100644
index 000000000..24f13cd29
--- /dev/null
+++ b/src/web/app/common/scripts/home-stream.js
@@ -0,0 +1,18 @@
+'use strict';
+
+import Stream from './stream';
+
+/**
+ * Home stream connection
+ */
+class Connection extends Stream {
+	constructor(me) {
+		super('', {
+			i: me.token
+		});
+
+		this.on('i_updated', me.update);
+	}
+}
+
+export default Connection;
diff --git a/src/web/app/common/scripts/messaging-stream.js b/src/web/app/common/scripts/messaging-stream.js
index 50d41c2be..261525d5f 100644
--- a/src/web/app/common/scripts/messaging-stream.js
+++ b/src/web/app/common/scripts/messaging-stream.js
@@ -1,42 +1,22 @@
-const ReconnectingWebSocket = require('reconnecting-websocket');
-import * as riot from 'riot';
-import CONFIG from './config';
+'use strict';
 
-class Connection {
+import Stream from './stream';
+
+/**
+ * Messaging stream connection
+ */
+class Connection extends Stream {
 	constructor(me, otherparty) {
-		// BIND -----------------------------------
-		this.onOpen =    this.onOpen.bind(this);
-		this.onMessage = this.onMessage.bind(this);
-		this.close =     this.close.bind(this);
-		// ----------------------------------------
+		super('messaging', {
+			i: me.token,
+			otherparty
+		});
 
-		this.event = riot.observable();
-		this.me = me;
-
-		const host = CONFIG.apiUrl.replace('http', 'ws');
-		this.socket = new ReconnectingWebSocket(`${host}/messaging?i=${me.token}&otherparty=${otherparty}`);
-		this.socket.addEventListener('open', this.onOpen);
-		this.socket.addEventListener('message', this.onMessage);
-	}
-
-	onOpen() {
-		this.socket.send(JSON.stringify({
-			i: this.me.token
-		}));
-	}
-
-	onMessage(message) {
-		try {
-			const msg = JSON.parse(message.data);
-			if (msg.type) this.event.trigger(msg.type, msg.body);
-		} catch(e) {
-			// noop
-		}
-	}
-
-	close() {
-		this.socket.removeEventListener('open', this.onOpen);
-		this.socket.removeEventListener('message', this.onMessage);
+		this.on('_connected_', () => {
+			this.send({
+				i: me.token
+			});
+		});
 	}
 }
 
diff --git a/src/web/app/common/scripts/server-stream.js b/src/web/app/common/scripts/server-stream.js
new file mode 100644
index 000000000..a1c466b35
--- /dev/null
+++ b/src/web/app/common/scripts/server-stream.js
@@ -0,0 +1,14 @@
+'use strict';
+
+import Stream from './stream';
+
+/**
+ * Server stream connection
+ */
+class Connection extends Stream {
+	constructor() {
+		super('server');
+	}
+}
+
+export default Connection;
diff --git a/src/web/app/common/scripts/stream.js b/src/web/app/common/scripts/stream.js
index ac3dd6715..981118b5d 100644
--- a/src/web/app/common/scripts/stream.js
+++ b/src/web/app/common/scripts/stream.js
@@ -5,10 +5,10 @@ import * as riot from 'riot';
 import CONFIG from './config';
 
 /**
- * Home stream connection
+ * Misskey stream connection
  */
 class Connection {
-	constructor(me) {
+	constructor(endpoint, params) {
 		// BIND -----------------------------------
 		this.onOpen =    this.onOpen.bind(this);
 		this.onClose =   this.onClose.bind(this);
@@ -20,16 +20,19 @@ class Connection {
 		riot.observable(this);
 
 		this.state = 'initializing';
-		this.me = me;
 		this.buffer = [];
 
 		const host = CONFIG.apiUrl.replace('http', 'ws');
-		this.socket = new ReconnectingWebSocket(`${host}?i=${me.token}`);
+		const query = params
+			? Object.keys(params)
+				.map(k => encodeURIComponent(k) + '=' + encodeURIComponent(params[k]))
+				.join('&')
+			: null;
+
+		this.socket = new ReconnectingWebSocket(`${host}/${endpoint}${query ? '?' + query : ''}`);
 		this.socket.addEventListener('open', this.onOpen);
 		this.socket.addEventListener('close', this.onClose);
 		this.socket.addEventListener('message', this.onMessage);
-
-		this.on('i_updated', me.update);
 	}
 
 	/**
diff --git a/src/web/app/common/tags/messaging/room.tag b/src/web/app/common/tags/messaging/room.tag
index bd49a4d78..93f07f2ce 100644
--- a/src/web/app/common/tags/messaging/room.tag
+++ b/src/web/app/common/tags/messaging/room.tag
@@ -137,8 +137,8 @@
 		this.connection = new MessagingStreamConnection(this.I, this.user.id);
 
 		this.on('mount', () => {
-			this.connection.event.on('message', this.onMessage);
-			this.connection.event.on('read', this.onRead);
+			this.connection.on('message', this.onMessage);
+			this.connection.on('read', this.onRead);
 
 			document.addEventListener('visibilitychange', this.onVisibilitychange);
 
@@ -153,8 +153,8 @@
 		});
 
 		this.on('unmount', () => {
-			this.connection.event.off('message', this.onMessage);
-			this.connection.event.off('read', this.onRead);
+			this.connection.off('message', this.onMessage);
+			this.connection.off('read', this.onRead);
 			this.connection.close();
 
 			document.removeEventListener('visibilitychange', this.onVisibilitychange);
@@ -174,10 +174,10 @@
 
 			this.messages.push(message);
 			if (message.user_id != this.I.id && !document.hidden) {
-				this.connection.socket.send(JSON.stringify({
+				this.connection.send({
 					type: 'read',
 					id: message.id
-				}));
+				});
 			}
 			this.update();
 
@@ -239,10 +239,10 @@
 			if (document.hidden) return;
 			this.messages.forEach(message => {
 				if (message.user_id !== this.I.id && !message.is_read) {
-					this.connection.socket.send(JSON.stringify({
+					this.connection.send({
 						type: 'read',
 						id: message.id
-					}));
+					});
 				}
 			});
 		};
diff --git a/src/web/app/desktop/tags/home-widgets/activity.tag b/src/web/app/desktop/tags/home-widgets/activity.tag
index 621702fa1..3a8afdfa3 100644
--- a/src/web/app/desktop/tags/home-widgets/activity.tag
+++ b/src/web/app/desktop/tags/home-widgets/activity.tag
@@ -100,6 +100,8 @@
 	</svg>
 	<style>
 		:scope
+			display block
+
 			> svg
 				display block
 				padding 10px
@@ -131,7 +133,7 @@
 </mk-activity-home-widget-calender>
 
 <mk-activity-home-widget-chart>
-	<svg riot-viewBox="0 0 { viewBoxX } 60" preserveAspectRatio="none" onmousedown={ onMousedown }>
+	<svg riot-viewBox="0 0 { viewBoxX } { viewBoxY }" preserveAspectRatio="none" onmousedown={ onMousedown }>
 		<polyline
 			riot-points={ pointsPost }
 			fill="none"
@@ -155,6 +157,8 @@
 	</svg>
 	<style>
 		:scope
+			display block
+
 			> svg
 				display block
 				padding 10px
@@ -163,6 +167,7 @@
 	</style>
 	<script>
 		this.viewBoxX = 140;
+		this.viewBoxY = 60;
 		this.zoom = 1;
 		this.pos = 0;
 
@@ -176,10 +181,10 @@
 
 		this.render = () => {
 			this.update({
-				pointsPost: this.data.map((d, i) => `${(i * this.zoom) + this.pos},${(1 - (d.posts / peak)) * 60}`).join(' '),
-				pointsReply: this.data.map((d, i) => `${(i * this.zoom) + this.pos},${(1 - (d.replies / peak)) * 60}`).join(' '),
-				pointsRepost: this.data.map((d, i) => `${(i * this.zoom) + this.pos},${(1 - (d.reposts / peak)) * 60}`).join(' '),
-				pointsTotal: this.data.map((d, i) => `${(i * this.zoom) + this.pos},${(1 - (d.total / peak)) * 60}`).join(' ')
+				pointsPost: this.data.map((d, i) => `${(i * this.zoom) + this.pos},${(1 - (d.posts / peak)) * this.viewBoxY}`).join(' '),
+				pointsReply: this.data.map((d, i) => `${(i * this.zoom) + this.pos},${(1 - (d.replies / peak)) * this.viewBoxY}`).join(' '),
+				pointsRepost: this.data.map((d, i) => `${(i * this.zoom) + this.pos},${(1 - (d.reposts / peak)) * this.viewBoxY}`).join(' '),
+				pointsTotal: this.data.map((d, i) => `${(i * this.zoom) + this.pos},${(1 - (d.total / peak)) * this.viewBoxY}`).join(' ')
 			});
 		};
 
diff --git a/src/web/app/desktop/tags/home-widgets/server.tag b/src/web/app/desktop/tags/home-widgets/server.tag
new file mode 100644
index 000000000..7a5129958
--- /dev/null
+++ b/src/web/app/desktop/tags/home-widgets/server.tag
@@ -0,0 +1,177 @@
+<mk-server-home-widget>
+	<p class="title"><i class="fa fa-server"></i>%i18n:desktop.tags.mk-server-home-widget.title%</p>
+	<button onclick={ toggle } title="%i18n:desktop.tags.mk-server-home-widget.toggle%"><i class="fa fa-sort"></i></button>
+	<p class="initializing" if={ initializing }><i class="fa fa-spinner fa-pulse fa-fw"></i>%i18n:common.loading%<mk-ellipsis/></p>
+	<mk-server-home-widget-stats if={ !initializing && view == 0 }/>
+	<mk-server-home-widget-info if={ !initializing && view == 1 } meta={ meta }/>
+	<style>
+		:scope
+			display block
+			background #fff
+
+			> .title
+				z-index 1
+				margin 0
+				padding 0 16px
+				line-height 42px
+				font-size 0.9em
+				font-weight bold
+				color #888
+				box-shadow 0 1px rgba(0, 0, 0, 0.07)
+
+				> i
+					margin-right 4px
+
+			> button
+				position absolute
+				z-index 2
+				top 0
+				right 0
+				padding 0
+				width 42px
+				font-size 0.9em
+				line-height 42px
+				color #ccc
+
+				&:hover
+					color #aaa
+
+				&:active
+					color #999
+
+			> .initializing
+				margin 0
+				padding 16px
+				text-align center
+				color #aaa
+
+				> i
+					margin-right 4px
+
+	</style>
+	<script>
+		this.mixin('api');
+
+		this.initializing = true;
+		this.view = 0;
+
+		this.on('mount', () => {
+			this.api('meta').then(meta => {
+				this.update({
+					initializing: false,
+					meta
+				});
+			});
+		});
+
+		this.toggle = () => {
+			this.view++;
+			if (this.view == 2) this.view = 0;
+		};
+	</script>
+</mk-server-home-widget>
+
+<mk-server-home-widget-stats>
+	<svg riot-viewBox="0 0 { viewBoxX } { viewBoxY }" preserveAspectRatio="none">
+		<text dx="1" dy="5">CPU</text>
+		<polygon
+			riot-points={ cpuPolygonPoints }
+			riot-fill={ cpuColor }
+			fill-opacity="0.5"/>
+		<polyline
+			riot-points={ cpuPolylinePoints }
+			fill="none"
+			stroke-width="1"
+			riot-stroke={ cpuColor }/>
+	</svg>
+	<svg riot-viewBox="0 0 { viewBoxX } { viewBoxY }" preserveAspectRatio="none">
+		<text dx="1" dy="5">MEM</text>
+		<polygon
+			riot-points={ memPolygonPoints }
+			riot-fill={ memColor }
+			fill-opacity="0.5"/>
+		<polyline
+			riot-points={ memPolylinePoints }
+			fill="none"
+			stroke-width="1"
+			riot-stroke={ memColor }/>
+	</svg>
+	<style>
+		:scope
+			display block
+
+			> svg
+				display block
+				padding 10px
+				width 50%
+				float left
+
+				&:first-child
+					padding-right 5px
+
+				&:last-child
+					padding-left 5px
+
+				> text
+					font-size 5px
+					fill #7b7b7b
+
+			&:after
+				content ""
+				display block
+				clear both
+	</style>
+	<script>
+		import Connection from '../../../common/scripts/server-stream';
+
+		this.viewBoxX = 50;
+		this.viewBoxY = 30;
+		this.stats = [];
+		this.connection = new Connection();
+
+		this.on('mount', () => {
+			this.connection.on('stats', this.onStats);
+		});
+
+		this.on('unmount', () => {
+			this.connection.off('stats', this.onStats);
+			this.connection.close();
+		});
+
+		this.onStats = stats => {
+			this.stats.push(stats);
+			if (this.stats.length > 50) this.stats.shift();
+
+			const cpuPolylinePoints = this.stats.map((s, i) => `${this.viewBoxX - ((this.stats.length - 1) - i)},${(1 - s.cpu_usage) * this.viewBoxY}`).join(' ');
+			const memPolylinePoints = this.stats.map((s, i) => `${this.viewBoxX - ((this.stats.length - 1) - i)},${(s.mem.free / s.mem.total) * this.viewBoxY}`).join(' ');
+
+			const cpuPolygonPoints = `${this.viewBoxX - (this.stats.length - 1)},${ this.viewBoxY } ${ cpuPolylinePoints } ${ this.viewBoxX },${ this.viewBoxY }`;
+			const memPolygonPoints = `${this.viewBoxX - (this.stats.length - 1)},${ this.viewBoxY } ${ memPolylinePoints } ${ this.viewBoxX },${ this.viewBoxY }`;
+
+			const cpuColor = `hsl(${180 - (stats.cpu_usage * 180)}, 80%, 70%)`;
+			const memColor = `hsl(${180 - (stats.mem.free / stats.mem.total * 180)}, 80%, 70%)`;
+
+			this.update({
+				cpuPolylinePoints,
+				memPolylinePoints,
+				cpuPolygonPoints,
+				memPolygonPoints,
+				cpuColor,
+				memColor
+			});
+		};
+	</script>
+</mk-server-home-widget-stats>
+
+<mk-server-home-widget-info>
+	<p>Maintainer: { meta.maintainer }</p>
+	<style>
+		:scope
+			display block
+			padding 10px
+	</style>
+	<script>
+		this.meta = this.opts.meta;
+	</script>
+</mk-server-home-widget-info>
+
diff --git a/src/web/app/desktop/tags/home.tag b/src/web/app/desktop/tags/home.tag
index 0e4a2ced1..41053ccbb 100644
--- a/src/web/app/desktop/tags/home.tag
+++ b/src/web/app/desktop/tags/home.tag
@@ -70,6 +70,7 @@
 				'rss-reader',
 				'trends',
 				'photo-stream',
+				'server',
 				'version'
 			],
 			right: [
diff --git a/src/web/app/desktop/tags/index.js b/src/web/app/desktop/tags/index.js
index 1e0ebd44c..177ba4129 100644
--- a/src/web/app/desktop/tags/index.js
+++ b/src/web/app/desktop/tags/index.js
@@ -45,6 +45,7 @@ require('./home-widgets/version.tag');
 require('./home-widgets/recommended-polls.tag');
 require('./home-widgets/trends.tag');
 require('./home-widgets/activity.tag');
+require('./home-widgets/server.tag');
 require('./timeline.tag');
 require('./messaging/window.tag');
 require('./messaging/room-window.tag');
diff --git a/src/web/app/init.js b/src/web/app/init.js
index 17f9a2d09..b442d36a1 100644
--- a/src/web/app/init.js
+++ b/src/web/app/init.js
@@ -8,7 +8,7 @@ import * as riot from 'riot';
 import api from './common/scripts/api';
 import signout from './common/scripts/signout';
 import checkForUpdate from './common/scripts/check-for-update';
-import Connection from './common/scripts/stream';
+import Connection from './common/scripts/home-stream';
 import mixin from './common/mixins';
 import generateDefaultUserdata from './common/scripts/generate-default-userdata';
 import CONFIG from './common/scripts/config';
@@ -95,7 +95,7 @@ export default callback => {
 			});
 		}
 
-		// Init stream connection
+		// Init home stream connection
 		const stream = me ? new Connection(me) : null;
 
 		// ミックスイン初期化

From d3428c1f65019bfeedaa00281f09c6d15a22e531 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Fri, 9 Jun 2017 01:05:10 +0900
Subject: [PATCH 13/75] v2041

---
 package.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/package.json b/package.json
index 93f87561f..eb35a55ec 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
 {
   "name": "misskey",
   "author": "syuilo <i@syuilo.com>",
-  "version": "0.0.2038",
+  "version": "0.0.2041",
   "license": "MIT",
   "description": "A miniblog-based SNS",
   "bugs": "https://github.com/syuilo/misskey/issues",

From 04d07f9bba9c6354c55ba1529d41ba6c9182f3db Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Fri, 9 Jun 2017 01:15:48 +0900
Subject: [PATCH 14/75] Fix bug

---
 src/web/app/desktop/tags/home-widgets/server.tag | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/src/web/app/desktop/tags/home-widgets/server.tag b/src/web/app/desktop/tags/home-widgets/server.tag
index 7a5129958..0d869eec9 100644
--- a/src/web/app/desktop/tags/home-widgets/server.tag
+++ b/src/web/app/desktop/tags/home-widgets/server.tag
@@ -139,17 +139,18 @@
 		});
 
 		this.onStats = stats => {
+			stats.mem.used = stats.mem.total - stats.mem.free;
 			this.stats.push(stats);
 			if (this.stats.length > 50) this.stats.shift();
 
 			const cpuPolylinePoints = this.stats.map((s, i) => `${this.viewBoxX - ((this.stats.length - 1) - i)},${(1 - s.cpu_usage) * this.viewBoxY}`).join(' ');
-			const memPolylinePoints = this.stats.map((s, i) => `${this.viewBoxX - ((this.stats.length - 1) - i)},${(s.mem.free / s.mem.total) * this.viewBoxY}`).join(' ');
+			const memPolylinePoints = this.stats.map((s, i) => `${this.viewBoxX - ((this.stats.length - 1) - i)},${(1 - (s.mem.used / s.mem.total)) * this.viewBoxY}`).join(' ');
 
 			const cpuPolygonPoints = `${this.viewBoxX - (this.stats.length - 1)},${ this.viewBoxY } ${ cpuPolylinePoints } ${ this.viewBoxX },${ this.viewBoxY }`;
 			const memPolygonPoints = `${this.viewBoxX - (this.stats.length - 1)},${ this.viewBoxY } ${ memPolylinePoints } ${ this.viewBoxX },${ this.viewBoxY }`;
 
 			const cpuColor = `hsl(${180 - (stats.cpu_usage * 180)}, 80%, 70%)`;
-			const memColor = `hsl(${180 - (stats.mem.free / stats.mem.total * 180)}, 80%, 70%)`;
+			const memColor = `hsl(${180 - (stats.mem.used / stats.mem.total * 180)}, 80%, 70%)`;
 
 			this.update({
 				cpuPolylinePoints,

From d041232ccd712eb48a10b10ec6a1e83bc6bf9a34 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Fri, 9 Jun 2017 01:21:56 +0900
Subject: [PATCH 15/75] [Client] Improve server home widget

---
 src/web/app/desktop/tags/home-widgets/server.tag | 14 +++++++++++---
 1 file changed, 11 insertions(+), 3 deletions(-)

diff --git a/src/web/app/desktop/tags/home-widgets/server.tag b/src/web/app/desktop/tags/home-widgets/server.tag
index 0d869eec9..94418005b 100644
--- a/src/web/app/desktop/tags/home-widgets/server.tag
+++ b/src/web/app/desktop/tags/home-widgets/server.tag
@@ -73,7 +73,7 @@
 
 <mk-server-home-widget-stats>
 	<svg riot-viewBox="0 0 { viewBoxX } { viewBoxY }" preserveAspectRatio="none">
-		<text dx="1" dy="5">CPU</text>
+		<text dx="1" dy="5">CPU <tspan>{ cpuP }%</tspan></text>
 		<polygon
 			riot-points={ cpuPolygonPoints }
 			riot-fill={ cpuColor }
@@ -85,7 +85,7 @@
 			riot-stroke={ cpuColor }/>
 	</svg>
 	<svg riot-viewBox="0 0 { viewBoxX } { viewBoxY }" preserveAspectRatio="none">
-		<text dx="1" dy="5">MEM</text>
+		<text dx="1" dy="5">MEM <tspan>{ memP }%</tspan></text>
 		<polygon
 			riot-points={ memPolygonPoints }
 			riot-fill={ memColor }
@@ -116,6 +116,9 @@
 					font-size 5px
 					fill #7b7b7b
 
+					> tspan
+						opacity 0.5
+
 			&:after
 				content ""
 				display block
@@ -152,13 +155,18 @@
 			const cpuColor = `hsl(${180 - (stats.cpu_usage * 180)}, 80%, 70%)`;
 			const memColor = `hsl(${180 - (stats.mem.used / stats.mem.total * 180)}, 80%, 70%)`;
 
+			const cpuP = (stats.cpu_usage * 100).toFixed(0);
+			const memP = (stats.mem.used / stats.mem.total * 100).toFixed(0);
+
 			this.update({
 				cpuPolylinePoints,
 				memPolylinePoints,
 				cpuPolygonPoints,
 				memPolygonPoints,
 				cpuColor,
-				memColor
+				memColor,
+				cpuP,
+				memP
 			});
 		};
 	</script>

From 8560e105b8d085a81870e77e305db3e26aa555a7 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Fri, 9 Jun 2017 01:22:12 +0900
Subject: [PATCH 16/75] v2044

---
 package.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/package.json b/package.json
index eb35a55ec..baacd30a8 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
 {
   "name": "misskey",
   "author": "syuilo <i@syuilo.com>",
-  "version": "0.0.2041",
+  "version": "0.0.2044",
   "license": "MIT",
   "description": "A miniblog-based SNS",
   "bugs": "https://github.com/syuilo/misskey/issues",

From 7aea902b5e3741bfc638223e14717c85f4957d0e Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Fri, 9 Jun 2017 01:47:45 +0900
Subject: [PATCH 17/75] =?UTF-8?q?[Client]=20=E3=81=84=E3=81=84=E6=84=9F?=
 =?UTF-8?q?=E3=81=98=E3=81=AB?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/web/app/desktop/tags/home-widgets/server.tag | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/web/app/desktop/tags/home-widgets/server.tag b/src/web/app/desktop/tags/home-widgets/server.tag
index 94418005b..cd3887591 100644
--- a/src/web/app/desktop/tags/home-widgets/server.tag
+++ b/src/web/app/desktop/tags/home-widgets/server.tag
@@ -2,8 +2,8 @@
 	<p class="title"><i class="fa fa-server"></i>%i18n:desktop.tags.mk-server-home-widget.title%</p>
 	<button onclick={ toggle } title="%i18n:desktop.tags.mk-server-home-widget.toggle%"><i class="fa fa-sort"></i></button>
 	<p class="initializing" if={ initializing }><i class="fa fa-spinner fa-pulse fa-fw"></i>%i18n:common.loading%<mk-ellipsis/></p>
-	<mk-server-home-widget-stats if={ !initializing && view == 0 }/>
-	<mk-server-home-widget-info if={ !initializing && view == 1 } meta={ meta }/>
+	<mk-server-home-widget-stats if={ !initializing } show={ view == 0 }/>
+	<mk-server-home-widget-info if={ !initializing } show={ view == 1 } meta={ meta }/>
 	<style>
 		:scope
 			display block
@@ -73,7 +73,6 @@
 
 <mk-server-home-widget-stats>
 	<svg riot-viewBox="0 0 { viewBoxX } { viewBoxY }" preserveAspectRatio="none">
-		<text dx="1" dy="5">CPU <tspan>{ cpuP }%</tspan></text>
 		<polygon
 			riot-points={ cpuPolygonPoints }
 			riot-fill={ cpuColor }
@@ -83,9 +82,9 @@
 			fill="none"
 			stroke-width="1"
 			riot-stroke={ cpuColor }/>
+		<text dx="1" dy="5">CPU <tspan>{ cpuP }%</tspan></text>
 	</svg>
 	<svg riot-viewBox="0 0 { viewBoxX } { viewBoxY }" preserveAspectRatio="none">
-		<text dx="1" dy="5">MEM <tspan>{ memP }%</tspan></text>
 		<polygon
 			riot-points={ memPolygonPoints }
 			riot-fill={ memColor }
@@ -95,6 +94,7 @@
 			fill="none"
 			stroke-width="1"
 			riot-stroke={ memColor }/>
+		<text dx="1" dy="5">MEM <tspan>{ memP }%</tspan></text>
 	</svg>
 	<style>
 		:scope

From 0e7bb3f54fcde8dfd3c0caefd6d37672f2d6780f Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Fri, 9 Jun 2017 03:04:25 +0900
Subject: [PATCH 18/75] :art:

---
 src/web/app/desktop/tags/home-widgets/server.tag | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/web/app/desktop/tags/home-widgets/server.tag b/src/web/app/desktop/tags/home-widgets/server.tag
index cd3887591..c0187edcb 100644
--- a/src/web/app/desktop/tags/home-widgets/server.tag
+++ b/src/web/app/desktop/tags/home-widgets/server.tag
@@ -114,7 +114,7 @@
 
 				> text
 					font-size 5px
-					fill #7b7b7b
+					fill rgba(0, 0, 0, 0.55)
 
 					> tspan
 						opacity 0.5

From b1f4d116971379a2e49d6d4ddd555e489a6630a9 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Fri, 9 Jun 2017 03:04:33 +0900
Subject: [PATCH 19/75] [Client] :v:

---
 src/web/app/desktop/tags/home.tag | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/web/app/desktop/tags/home.tag b/src/web/app/desktop/tags/home.tag
index 41053ccbb..37b2d3cf7 100644
--- a/src/web/app/desktop/tags/home.tag
+++ b/src/web/app/desktop/tags/home.tag
@@ -70,7 +70,6 @@
 				'rss-reader',
 				'trends',
 				'photo-stream',
-				'server',
 				'version'
 			],
 			right: [
@@ -78,6 +77,7 @@
 				'notifications',
 				'user-recommendation',
 				'recommended-polls',
+				'server',
 				'donation',
 				'nav',
 				'tips'

From 5a19e885ebe361af801f4166be755d22975a058e Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Fri, 9 Jun 2017 03:08:56 +0900
Subject: [PATCH 20/75] [Client] :v:

---
 src/web/app/desktop/tags/home-widgets/activity.tag | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/web/app/desktop/tags/home-widgets/activity.tag b/src/web/app/desktop/tags/home-widgets/activity.tag
index 3a8afdfa3..8881fdda1 100644
--- a/src/web/app/desktop/tags/home-widgets/activity.tag
+++ b/src/web/app/desktop/tags/home-widgets/activity.tag
@@ -133,7 +133,8 @@
 </mk-activity-home-widget-calender>
 
 <mk-activity-home-widget-chart>
-	<svg riot-viewBox="0 0 { viewBoxX } { viewBoxY }" preserveAspectRatio="none" onmousedown={ onMousedown }>
+	<svg riot-viewBox="0 0 { viewBoxX } { viewBoxY }" preserveAspectRatio="none" onmousedown={ onMousedown }
+		title="Black ... Total\nBlue ... Posts\nRed ... Replies\nGreen ... Reposts">
 		<polyline
 			riot-points={ pointsPost }
 			fill="none"

From 7b016dc7884e396f7911ed87ff0ba94ef8edaffb Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Fri, 9 Jun 2017 03:10:12 +0900
Subject: [PATCH 21/75] v2048

---
 package.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/package.json b/package.json
index baacd30a8..628d8a68b 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
 {
   "name": "misskey",
   "author": "syuilo <i@syuilo.com>",
-  "version": "0.0.2044",
+  "version": "0.0.2048",
   "license": "MIT",
   "description": "A miniblog-based SNS",
   "bugs": "https://github.com/syuilo/misskey/issues",

From 679e96d37bbc90b3cfd15a2a37ea1b2609ac719c Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Fri, 9 Jun 2017 03:24:10 +0900
Subject: [PATCH 22/75] Fix bug

---
 src/web/app/desktop/tags/home-widgets/activity.tag | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/web/app/desktop/tags/home-widgets/activity.tag b/src/web/app/desktop/tags/home-widgets/activity.tag
index 8881fdda1..dcb3f590f 100644
--- a/src/web/app/desktop/tags/home-widgets/activity.tag
+++ b/src/web/app/desktop/tags/home-widgets/activity.tag
@@ -133,8 +133,8 @@
 </mk-activity-home-widget-calender>
 
 <mk-activity-home-widget-chart>
-	<svg riot-viewBox="0 0 { viewBoxX } { viewBoxY }" preserveAspectRatio="none" onmousedown={ onMousedown }
-		title="Black ... Total\nBlue ... Posts\nRed ... Replies\nGreen ... Reposts">
+	<svg riot-viewBox="0 0 { viewBoxX } { viewBoxY }" preserveAspectRatio="none" onmousedown={ onMousedown }>
+		<title>Black ... Total<br/>Blue ... Posts<br/>Red ... Replies<br/>Green ... Reposts</title>
 		<polyline
 			riot-points={ pointsPost }
 			fill="none"

From aeff3be148e893d7494148319d52a2527d1df8c2 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Fri, 9 Jun 2017 03:24:17 +0900
Subject: [PATCH 23/75] v2050

---
 package.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/package.json b/package.json
index 628d8a68b..801d9dc47 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
 {
   "name": "misskey",
   "author": "syuilo <i@syuilo.com>",
-  "version": "0.0.2048",
+  "version": "0.0.2050",
   "license": "MIT",
   "description": "A miniblog-based SNS",
   "bugs": "https://github.com/syuilo/misskey/issues",

From d82c23f896577c8ca09541c5c6814de66a9918ae Mon Sep 17 00:00:00 2001
From: "greenkeeper[bot]" <greenkeeper[bot]@users.noreply.github.com>
Date: Thu, 8 Jun 2017 22:35:20 +0000
Subject: [PATCH 24/75] fix(package): update file-type to version 5.1.0

---
 package.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/package.json b/package.json
index 801d9dc47..c6d6e57be 100644
--- a/package.json
+++ b/package.json
@@ -111,7 +111,7 @@
     "elasticsearch": "13.0.1",
     "escape-regexp": "0.0.1",
     "express": "4.15.3",
-    "file-type": "5.0.0",
+    "file-type": "5.1.0",
     "fuckadblock": "3.2.1",
     "gm": "1.23.0",
     "inquirer": "3.1.0",

From dc905209f86132d3690bcfabdcb81b05ff8d8080 Mon Sep 17 00:00:00 2001
From: "greenkeeper[bot]" <greenkeeper[bot]@users.noreply.github.com>
Date: Fri, 9 Jun 2017 14:04:01 +0000
Subject: [PATCH 25/75] fix(package): update file-type to version 5.1.1

---
 package.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/package.json b/package.json
index c6d6e57be..1a5e21c4b 100644
--- a/package.json
+++ b/package.json
@@ -111,7 +111,7 @@
     "elasticsearch": "13.0.1",
     "escape-regexp": "0.0.1",
     "express": "4.15.3",
-    "file-type": "5.1.0",
+    "file-type": "5.1.1",
     "fuckadblock": "3.2.1",
     "gm": "1.23.0",
     "inquirer": "3.1.0",

From 1f3f5f0566409d39d64016038ee81efd2bec06fa Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Sat, 10 Jun 2017 02:24:32 +0900
Subject: [PATCH 26/75] [Client] Improve server home widget

---
 .../app/desktop/tags/home-widgets/server.tag  | 123 ++++++++++++++++--
 1 file changed, 111 insertions(+), 12 deletions(-)

diff --git a/src/web/app/desktop/tags/home-widgets/server.tag b/src/web/app/desktop/tags/home-widgets/server.tag
index c0187edcb..44249352e 100644
--- a/src/web/app/desktop/tags/home-widgets/server.tag
+++ b/src/web/app/desktop/tags/home-widgets/server.tag
@@ -2,8 +2,9 @@
 	<p class="title"><i class="fa fa-server"></i>%i18n:desktop.tags.mk-server-home-widget.title%</p>
 	<button onclick={ toggle } title="%i18n:desktop.tags.mk-server-home-widget.toggle%"><i class="fa fa-sort"></i></button>
 	<p class="initializing" if={ initializing }><i class="fa fa-spinner fa-pulse fa-fw"></i>%i18n:common.loading%<mk-ellipsis/></p>
-	<mk-server-home-widget-stats if={ !initializing } show={ view == 0 }/>
-	<mk-server-home-widget-info if={ !initializing } show={ view == 1 } meta={ meta }/>
+	<mk-server-home-widget-cpu-and-memory-usage if={ !initializing } show={ view == 0 } connection={ connection }/>
+	<mk-server-home-widget-disk-usage if={ !initializing } show={ view == 1 } connection={ connection }/>
+	<mk-server-home-widget-info if={ !initializing } show={ view == 2 } connection={ connection } meta={ meta }/>
 	<style>
 		:scope
 			display block
@@ -50,10 +51,13 @@
 
 	</style>
 	<script>
+		import Connection from '../../../common/scripts/server-stream';
+
 		this.mixin('api');
 
 		this.initializing = true;
 		this.view = 0;
+		this.connection = new Connection();
 
 		this.on('mount', () => {
 			this.api('meta').then(meta => {
@@ -64,14 +68,18 @@
 			});
 		});
 
+		this.on('unmount', () => {
+			this.connection.close();
+		});
+
 		this.toggle = () => {
 			this.view++;
-			if (this.view == 2) this.view = 0;
+			if (this.view == 3) this.view = 0;
 		};
 	</script>
 </mk-server-home-widget>
 
-<mk-server-home-widget-stats>
+<mk-server-home-widget-cpu-and-memory-usage>
 	<svg riot-viewBox="0 0 { viewBoxX } { viewBoxY }" preserveAspectRatio="none">
 		<polygon
 			riot-points={ cpuPolygonPoints }
@@ -82,7 +90,7 @@
 			fill="none"
 			stroke-width="1"
 			riot-stroke={ cpuColor }/>
-		<text dx="1" dy="5">CPU <tspan>{ cpuP }%</tspan></text>
+		<text x="1" y="5">CPU <tspan>{ cpuP }%</tspan></text>
 	</svg>
 	<svg riot-viewBox="0 0 { viewBoxX } { viewBoxY }" preserveAspectRatio="none">
 		<polygon
@@ -94,7 +102,7 @@
 			fill="none"
 			stroke-width="1"
 			riot-stroke={ memColor }/>
-		<text dx="1" dy="5">MEM <tspan>{ memP }%</tspan></text>
+		<text x="1" y="5">MEM <tspan>{ memP }%</tspan></text>
 	</svg>
 	<style>
 		:scope
@@ -125,12 +133,10 @@
 				clear both
 	</style>
 	<script>
-		import Connection from '../../../common/scripts/server-stream';
-
 		this.viewBoxX = 50;
 		this.viewBoxY = 30;
 		this.stats = [];
-		this.connection = new Connection();
+		this.connection = this.opts.connection;
 
 		this.on('mount', () => {
 			this.connection.on('stats', this.onStats);
@@ -138,7 +144,6 @@
 
 		this.on('unmount', () => {
 			this.connection.off('stats', this.onStats);
-			this.connection.close();
 		});
 
 		this.onStats = stats => {
@@ -170,7 +175,102 @@
 			});
 		};
 	</script>
-</mk-server-home-widget-stats>
+</mk-server-home-widget-cpu-and-memory-usage>
+
+<mk-server-home-widget-disk-usage>
+	<svg viewBox="0 0 1 1" preserveAspectRatio="none">
+		<circle
+			riot-r={ r }
+			cx="50%" cy="50%"
+			fill="none"
+			stroke-width="0.1"
+			stroke="rgba(0, 0, 0, 0.05)"/>
+		<circle
+			riot-r={ r }
+			cx="50%" cy="50%"
+			riot-stroke-dasharray={ Math.PI * (r * 2) }
+			riot-stroke-dashoffset={ strokeDashoffset }
+			fill="none"
+			stroke-width="0.1"
+			riot-stroke={ color }/>
+		<text x="50%" y="50%" dy="0.05" text-anchor="middle">{ p }%</text>
+	</svg>
+	<div>
+		<p>Storage</p>
+		<p>Total: { bytesToSize(total) }</p>
+		<p>Available: { bytesToSize(available) }</p>
+		<p>Used: { bytesToSize(used) }</p>
+	</div>
+	<style>
+		:scope
+			display block
+
+			> svg
+				display block
+				padding 10px
+				height 100px
+				float left
+
+				> circle
+					transform-origin center
+					transform rotate(-90deg)
+
+				> text
+					font-size 0.15px
+					fill rgba(0, 0, 0, 0.6)
+
+			> div
+				float left
+				width calc(100% - 100px)
+				padding 10px 10px 10px 0
+
+				> p
+					margin 0
+					font-size 12px
+					color #505050
+
+					&:first-child
+						font-weight bold
+
+			&:after
+				content ""
+				display block
+				clear both
+
+	</style>
+	<script>
+		import bytesToSize from '../../../common/scripts/bytes-to-size';
+
+		this.r = 0.4;
+		this.connection = this.opts.connection;
+		this.bytesToSize = bytesToSize;
+
+		this.on('mount', () => {
+			this.connection.on('stats', this.onStats);
+		});
+
+		this.on('unmount', () => {
+			this.connection.off('stats', this.onStats);
+		});
+
+		this.onStats = stats => {
+			stats.disk.used = stats.disk.total - stats.disk.free;
+
+			const color = `hsl(${180 - (stats.disk.used / stats.disk.total * 180)}, 80%, 70%)`;
+			const p = (stats.disk.used / stats.disk.total * 100).toFixed(0);
+			const strokeDashoffset = (1 - (stats.disk.used / stats.disk.total)) * (Math.PI * (this.r * 2));
+
+			this.update({
+				color,
+				p,
+				strokeDashoffset,
+				total: stats.disk.total,
+				used: stats.disk.used,
+				available: stats.disk.available
+			});
+		};
+	</script>
+</mk-server-home-widget-disk-usage>
 
 <mk-server-home-widget-info>
 	<p>Maintainer: { meta.maintainer }</p>
@@ -183,4 +283,3 @@
 		this.meta = this.opts.meta;
 	</script>
 </mk-server-home-widget-info>
-

From 69cf63ee0522e43d877c7fd6fb23838557bc7a36 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Sat, 10 Jun 2017 02:24:50 +0900
Subject: [PATCH 27/75] v2053

---
 package.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/package.json b/package.json
index 1a5e21c4b..10aa0c72e 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
 {
   "name": "misskey",
   "author": "syuilo <i@syuilo.com>",
-  "version": "0.0.2050",
+  "version": "0.0.2053",
   "license": "MIT",
   "description": "A miniblog-based SNS",
   "bugs": "https://github.com/syuilo/misskey/issues",

From d285fc06ede131fb55b454009ff678aa3a578be2 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Sat, 10 Jun 2017 02:28:41 +0900
Subject: [PATCH 28/75] :art:

---
 src/web/app/desktop/tags/home-widgets/activity.tag | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/web/app/desktop/tags/home-widgets/activity.tag b/src/web/app/desktop/tags/home-widgets/activity.tag
index dcb3f590f..29ce8760f 100644
--- a/src/web/app/desktop/tags/home-widgets/activity.tag
+++ b/src/web/app/desktop/tags/home-widgets/activity.tag
@@ -154,7 +154,8 @@
 			riot-points={ pointsTotal }
 			fill="none"
 			stroke-width="1"
-			stroke="#555"/>
+			stroke="#555"
+			stroke-dasharray="2 2"/>
 	</svg>
 	<style>
 		:scope

From 4d35571a58dabddbc0fa4a61ff075ba5ab909c76 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Sat, 10 Jun 2017 02:40:39 +0900
Subject: [PATCH 29/75] [Client] :v:

---
 src/web/app/desktop/tags/home-widgets/server.tag | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/web/app/desktop/tags/home-widgets/server.tag b/src/web/app/desktop/tags/home-widgets/server.tag
index 44249352e..23fba179e 100644
--- a/src/web/app/desktop/tags/home-widgets/server.tag
+++ b/src/web/app/desktop/tags/home-widgets/server.tag
@@ -254,11 +254,11 @@
 		});
 
 		this.onStats = stats => {
-			stats.disk.used = stats.disk.total - stats.disk.free;
+			stats.disk.used = stats.disk.available - stats.disk.free;
 
-			const color = `hsl(${180 - (stats.disk.used / stats.disk.total * 180)}, 80%, 70%)`;
-			const p = (stats.disk.used / stats.disk.total * 100).toFixed(0);
-			const strokeDashoffset = (1 - (stats.disk.used / stats.disk.total)) * (Math.PI * (this.r * 2));
+			const color = `hsl(${180 - (stats.disk.used / stats.disk.available * 180)}, 80%, 70%)`;
+			const p = (stats.disk.used / stats.disk.available * 100).toFixed(0);
+			const strokeDashoffset = (1 - (stats.disk.used / stats.disk.available)) * (Math.PI * (this.r * 2));
 
 			this.update({
 				color,

From 756be3217be99ca447a29b4868f7007ef1dd2dca Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Sat, 10 Jun 2017 02:41:03 +0900
Subject: [PATCH 30/75] v2060

---
 package.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/package.json b/package.json
index 10aa0c72e..5a811fcc6 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
 {
   "name": "misskey",
   "author": "syuilo <i@syuilo.com>",
-  "version": "0.0.2053",
+  "version": "0.0.2060",
   "license": "MIT",
   "description": "A miniblog-based SNS",
   "bugs": "https://github.com/syuilo/misskey/issues",

From 5291ba28bc59e500b9feeb9990aea0fc9567b43e Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Sat, 10 Jun 2017 02:49:44 +0900
Subject: [PATCH 31/75] Revert "[Client] :v:"

This reverts commit 4d35571a58dabddbc0fa4a61ff075ba5ab909c76.
---
 src/web/app/desktop/tags/home-widgets/server.tag | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/web/app/desktop/tags/home-widgets/server.tag b/src/web/app/desktop/tags/home-widgets/server.tag
index 23fba179e..44249352e 100644
--- a/src/web/app/desktop/tags/home-widgets/server.tag
+++ b/src/web/app/desktop/tags/home-widgets/server.tag
@@ -254,11 +254,11 @@
 		});
 
 		this.onStats = stats => {
-			stats.disk.used = stats.disk.available - stats.disk.free;
+			stats.disk.used = stats.disk.total - stats.disk.free;
 
-			const color = `hsl(${180 - (stats.disk.used / stats.disk.available * 180)}, 80%, 70%)`;
-			const p = (stats.disk.used / stats.disk.available * 100).toFixed(0);
-			const strokeDashoffset = (1 - (stats.disk.used / stats.disk.available)) * (Math.PI * (this.r * 2));
+			const color = `hsl(${180 - (stats.disk.used / stats.disk.total * 180)}, 80%, 70%)`;
+			const p = (stats.disk.used / stats.disk.total * 100).toFixed(0);
+			const strokeDashoffset = (1 - (stats.disk.used / stats.disk.total)) * (Math.PI * (this.r * 2));
 
 			this.update({
 				color,

From 455a46f2cb7809fc3efb4cc9ec0b8f9e80bc2be1 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Sat, 10 Jun 2017 03:19:44 +0900
Subject: [PATCH 32/75] [API] Improve /meta

---
 src/api/endpoints/meta.ts | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/src/api/endpoints/meta.ts b/src/api/endpoints/meta.ts
index 98f812abd..be0daba75 100644
--- a/src/api/endpoints/meta.ts
+++ b/src/api/endpoints/meta.ts
@@ -1,6 +1,7 @@
 /**
  * Module dependencies
  */
+import * as os from 'os';
 import version from '../../version';
 import config from '../../conf';
 
@@ -41,6 +42,9 @@ module.exports = (params) => new Promise(async (res, rej) => {
 	res({
 		maintainer: config.maintainer,
 		version: version,
-		secure: config.https.enable
+		secure: config.https.enable,
+		cpu: {
+			cores: os.cpus().length
+		}
 	});
 });

From 1d9f1d1aa3959caa69ad74184965176b5bfc4403 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Sat, 10 Jun 2017 03:19:56 +0900
Subject: [PATCH 33/75] [Client] Improve server home widget

---
 .../app/desktop/tags/home-widgets/server.tag  | 220 ++++++++++++++----
 1 file changed, 179 insertions(+), 41 deletions(-)

diff --git a/src/web/app/desktop/tags/home-widgets/server.tag b/src/web/app/desktop/tags/home-widgets/server.tag
index 44249352e..3e1f1e5f2 100644
--- a/src/web/app/desktop/tags/home-widgets/server.tag
+++ b/src/web/app/desktop/tags/home-widgets/server.tag
@@ -3,8 +3,10 @@
 	<button onclick={ toggle } title="%i18n:desktop.tags.mk-server-home-widget.toggle%"><i class="fa fa-sort"></i></button>
 	<p class="initializing" if={ initializing }><i class="fa fa-spinner fa-pulse fa-fw"></i>%i18n:common.loading%<mk-ellipsis/></p>
 	<mk-server-home-widget-cpu-and-memory-usage if={ !initializing } show={ view == 0 } connection={ connection }/>
-	<mk-server-home-widget-disk-usage if={ !initializing } show={ view == 1 } connection={ connection }/>
-	<mk-server-home-widget-info if={ !initializing } show={ view == 2 } connection={ connection } meta={ meta }/>
+	<mk-server-home-widget-cpu if={ !initializing } show={ view == 1 } connection={ connection } meta={ meta }/>
+	<mk-server-home-widget-memory if={ !initializing } show={ view == 2 } connection={ connection }/>
+	<mk-server-home-widget-disk if={ !initializing } show={ view == 3 } connection={ connection }/>
+	<mk-server-home-widget-info if={ !initializing } show={ view == 4 } connection={ connection } meta={ meta }/>
 	<style>
 		:scope
 			display block
@@ -74,7 +76,7 @@
 
 		this.toggle = () => {
 			this.view++;
-			if (this.view == 3) this.view = 0;
+			if (this.view == 5) this.view = 0;
 		};
 	</script>
 </mk-server-home-widget>
@@ -177,47 +179,137 @@
 	</script>
 </mk-server-home-widget-cpu-and-memory-usage>
 
-<mk-server-home-widget-disk-usage>
-	<svg viewBox="0 0 1 1" preserveAspectRatio="none">
-		<circle
-			riot-r={ r }
-			cx="50%" cy="50%"
-			fill="none"
-			stroke-width="0.1"
-			stroke="rgba(0, 0, 0, 0.05)"/>
-		<circle
-			riot-r={ r }
-			cx="50%" cy="50%"
-			riot-stroke-dasharray={ Math.PI * (r * 2) }
-			riot-stroke-dashoffset={ strokeDashoffset }
-			fill="none"
-			stroke-width="0.1"
-			riot-stroke={ color }/>
-		<text x="50%" y="50%" dy="0.05" text-anchor="middle">{ p }%</text>
-	</svg>
+<mk-server-home-widget-cpu>
+	<mk-server-home-widget-pie ref="pie"/>
 	<div>
-		<p>Storage</p>
-		<p>Total: { bytesToSize(total) }</p>
-		<p>Available: { bytesToSize(available) }</p>
-		<p>Used: { bytesToSize(used) }</p>
+		<p>CPU</p>
+		<p>{ cores } Cores</p>
 	</div>
 	<style>
 		:scope
 			display block
 
-			> svg
-				display block
+			> mk-server-home-widget-pie
 				padding 10px
 				height 100px
 				float left
 
-				> circle
-					transform-origin center
-					transform rotate(-90deg)
+			> div
+				float left
+				width calc(100% - 100px)
+				padding 10px 10px 10px 0
 
-				> text
-					font-size 0.15px
-					fill rgba(0, 0, 0, 0.6)
+				> p
+					margin 0
+					font-size 12px
+					color #505050
+
+					&:first-child
+						font-weight bold
+
+			&:after
+				content ""
+				display block
+				clear both
+
+	</style>
+	<script>
+		this.cores = this.opts.meta.cpu.cores;
+		this.connection = this.opts.connection;
+
+		this.on('mount', () => {
+			this.connection.on('stats', this.onStats);
+		});
+
+		this.on('unmount', () => {
+			this.connection.off('stats', this.onStats);
+		});
+
+		this.onStats = stats => {
+			this.refs.pie.render(stats.cpu_usage);
+		};
+	</script>
+</mk-server-home-widget-cpu>
+
+<mk-server-home-widget-memory>
+	<mk-server-home-widget-pie ref="pie"/>
+	<div>
+		<p>Memory</p>
+		<p>Total: { bytesToSize(total) }</p>
+		<p>Used: { bytesToSize(used) }</p>
+		<p>Free: { bytesToSize(free) }</p>
+	</div>
+	<style>
+		:scope
+			display block
+
+			> mk-server-home-widget-pie
+				padding 10px
+				height 100px
+				float left
+
+			> div
+				float left
+				width calc(100% - 100px)
+				padding 10px 10px 10px 0
+
+				> p
+					margin 0
+					font-size 12px
+					color #505050
+
+					&:first-child
+						font-weight bold
+
+			&:after
+				content ""
+				display block
+				clear both
+
+	</style>
+	<script>
+		import bytesToSize from '../../../common/scripts/bytes-to-size';
+
+		this.connection = this.opts.connection;
+		this.bytesToSize = bytesToSize;
+
+		this.on('mount', () => {
+			this.connection.on('stats', this.onStats);
+		});
+
+		this.on('unmount', () => {
+			this.connection.off('stats', this.onStats);
+		});
+
+		this.onStats = stats => {
+			stats.mem.used = stats.mem.total - stats.mem.free;
+			this.refs.pie.render(stats.mem.used / stats.mem.total);
+
+			this.update({
+				total: stats.mem.total,
+				used: stats.mem.used,
+				free: stats.mem.free
+			});
+		};
+	</script>
+</mk-server-home-widget-memory>
+
+<mk-server-home-widget-disk>
+	<mk-server-home-widget-pie ref="pie"/>
+	<div>
+		<p>Storage</p>
+		<p>Total: { bytesToSize(total) }</p>
+		<p>Available: { bytesToSize(available) }</p>
+		<p>Used: { bytesToSize(used) }</p>
+	</div>
+	<style>
+		:scope
+			display block
+
+			> mk-server-home-widget-pie
+				padding 10px
+				height 100px
+				float left
 
 			> div
 				float left
@@ -241,7 +333,6 @@
 	<script>
 		import bytesToSize from '../../../common/scripts/bytes-to-size';
 
-		this.r = 0.4;
 		this.connection = this.opts.connection;
 		this.bytesToSize = bytesToSize;
 
@@ -256,21 +347,16 @@
 		this.onStats = stats => {
 			stats.disk.used = stats.disk.total - stats.disk.free;
 
-			const color = `hsl(${180 - (stats.disk.used / stats.disk.total * 180)}, 80%, 70%)`;
-			const p = (stats.disk.used / stats.disk.total * 100).toFixed(0);
-			const strokeDashoffset = (1 - (stats.disk.used / stats.disk.total)) * (Math.PI * (this.r * 2));
+			this.refs.pie.render(stats.disk.used / stats.disk.total);
 
 			this.update({
-				color,
-				p,
-				strokeDashoffset,
 				total: stats.disk.total,
 				used: stats.disk.used,
 				available: stats.disk.available
 			});
 		};
 	</script>
-</mk-server-home-widget-disk-usage>
+</mk-server-home-widget-disk>
 
 <mk-server-home-widget-info>
 	<p>Maintainer: { meta.maintainer }</p>
@@ -283,3 +369,55 @@
 		this.meta = this.opts.meta;
 	</script>
 </mk-server-home-widget-info>
+
+<mk-server-home-widget-pie>
+	<svg viewBox="0 0 1 1" preserveAspectRatio="none">
+		<circle
+			riot-r={ r }
+			cx="50%" cy="50%"
+			fill="none"
+			stroke-width="0.1"
+			stroke="rgba(0, 0, 0, 0.05)"/>
+		<circle
+			riot-r={ r }
+			cx="50%" cy="50%"
+			riot-stroke-dasharray={ Math.PI * (r * 2) }
+			riot-stroke-dashoffset={ strokeDashoffset }
+			fill="none"
+			stroke-width="0.1"
+			riot-stroke={ color }/>
+		<text x="50%" y="50%" dy="0.05" text-anchor="middle">{ (p * 100).toFixed(0) }%</text>
+	</svg>
+	<style>
+		:scope
+			display block
+
+			> svg
+				display block
+				height 100%
+
+				> circle
+					transform-origin center
+					transform rotate(-90deg)
+					transition stroke-dashoffset 0.5s ease
+
+				> text
+					font-size 0.15px
+					fill rgba(0, 0, 0, 0.6)
+
+	</style>
+	<script>
+		this.r = 0.4;
+
+		this.render = p => {
+			const color = `hsl(${180 - (p * 180)}, 80%, 70%)`;
+			const strokeDashoffset = (1 - p) * (Math.PI * (this.r * 2));
+
+			this.update({
+				p,
+				color,
+				strokeDashoffset
+			});
+		};
+	</script>
+</mk-server-home-widget-pie>

From dc6b736be6bcbd48ee0ba29939220e6edb309796 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Sat, 10 Jun 2017 03:20:19 +0900
Subject: [PATCH 34/75] v2064

---
 package.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/package.json b/package.json
index 5a811fcc6..837e9a04e 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
 {
   "name": "misskey",
   "author": "syuilo <i@syuilo.com>",
-  "version": "0.0.2060",
+  "version": "0.0.2064",
   "license": "MIT",
   "description": "A miniblog-based SNS",
   "bugs": "https://github.com/syuilo/misskey/issues",

From 5b1d2ec442a825ce555be22b1a8abff27cef9f32 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Sat, 10 Jun 2017 05:25:57 +0900
Subject: [PATCH 35/75] [API] Improve /meta

---
 src/api/endpoints/meta.ts | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/src/api/endpoints/meta.ts b/src/api/endpoints/meta.ts
index be0daba75..7ab72ae8f 100644
--- a/src/api/endpoints/meta.ts
+++ b/src/api/endpoints/meta.ts
@@ -43,6 +43,12 @@ module.exports = (params) => new Promise(async (res, rej) => {
 		maintainer: config.maintainer,
 		version: version,
 		secure: config.https.enable,
+		machine: os.hostname(),
+		node: {
+			release: (process as any).release.name,
+			lts: (process as any).release.lts,
+			version: process.version
+		},
 		cpu: {
 			cores: os.cpus().length
 		}

From 6af6084b6548b65c69daafe3f4373b43bc59c43f Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Sat, 10 Jun 2017 05:26:08 +0900
Subject: [PATCH 36/75] [Client] Improve server home widget

---
 src/web/app/desktop/tags/home-widgets/server.tag | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/src/web/app/desktop/tags/home-widgets/server.tag b/src/web/app/desktop/tags/home-widgets/server.tag
index 3e1f1e5f2..5be0e2aaf 100644
--- a/src/web/app/desktop/tags/home-widgets/server.tag
+++ b/src/web/app/desktop/tags/home-widgets/server.tag
@@ -359,11 +359,19 @@
 </mk-server-home-widget-disk>
 
 <mk-server-home-widget-info>
-	<p>Maintainer: { meta.maintainer }</p>
+	<p>Maintainer: <b>{ meta.maintainer }</b></p>
+	<p>Machine: { meta.machine }</p>
+	<p>Node: { meta.node.version } { meta.node.release }</p>
 	<style>
 		:scope
 			display block
 			padding 10px
+
+			> p
+				margin 0
+				font-size 12px
+				color #505050
+
 	</style>
 	<script>
 		this.meta = this.opts.meta;

From e17649a38c75130523b8718c65c705121ef9fbe3 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Sat, 10 Jun 2017 05:27:09 +0900
Subject: [PATCH 37/75] Update .gitignore

---
 .gitignore | 1 +
 1 file changed, 1 insertion(+)

diff --git a/.gitignore b/.gitignore
index 6573bcb1e..42b1bde94 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,3 +7,4 @@ npm-debug.log
 *.pem
 run.bat
 api-docs.json
+package-lock.json

From fa966cdda6677ff80c706690c65ed9552e594858 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Sat, 10 Jun 2017 05:29:03 +0900
Subject: [PATCH 38/75] [CI] Use node 8.x

---
 .travis.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.travis.yml b/.travis.yml
index 91e124443..ea7660119 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -4,7 +4,7 @@
 language: node_js
 
 node_js:
-  - 7.10.0
+  - 8.1.0
 
 env:
   - CXX=g++-4.8 NODE_ENV=production

From 0004928bad5ffdfeeaa1eb84b447beb1703cc6e1 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Sat, 10 Jun 2017 05:29:28 +0900
Subject: [PATCH 39/75] v2069

---
 package.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/package.json b/package.json
index 837e9a04e..507af3040 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
 {
   "name": "misskey",
   "author": "syuilo <i@syuilo.com>",
-  "version": "0.0.2064",
+  "version": "0.0.2069",
   "license": "MIT",
   "description": "A miniblog-based SNS",
   "bugs": "https://github.com/syuilo/misskey/issues",

From bfde5dd48ad51f874dc31d8307311ccf616e340a Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Sat, 10 Jun 2017 05:38:53 +0900
Subject: [PATCH 40/75] Update font-awesome

---
 src/web/app/base.pug | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/web/app/base.pug b/src/web/app/base.pug
index f7da0574b..b1ca80deb 100644
--- a/src/web/app/base.pug
+++ b/src/web/app/base.pug
@@ -20,7 +20,7 @@ html
 		script
 			include ./../../../built/web/assets/safe.js
 
-		script(src='https://use.fontawesome.com/22aba0df4f.js' async)
+		script(src='https://use.fontawesome.com/db921426cb.js' async)
 
 	body
 		noscript: p

From 0c52e5b93f58785fc25a2647e0e45bc13937ac40 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Sat, 10 Jun 2017 05:39:09 +0900
Subject: [PATCH 41/75] [Client] :v:

---
 .../app/desktop/tags/home-widgets/server.tag    | 17 +++++++++++++----
 1 file changed, 13 insertions(+), 4 deletions(-)

diff --git a/src/web/app/desktop/tags/home-widgets/server.tag b/src/web/app/desktop/tags/home-widgets/server.tag
index 5be0e2aaf..7c2850239 100644
--- a/src/web/app/desktop/tags/home-widgets/server.tag
+++ b/src/web/app/desktop/tags/home-widgets/server.tag
@@ -182,7 +182,7 @@
 <mk-server-home-widget-cpu>
 	<mk-server-home-widget-pie ref="pie"/>
 	<div>
-		<p>CPU</p>
+		<p><i class="fa fa-microchip"></i>CPU</p>
 		<p>{ cores } Cores</p>
 	</div>
 	<style>
@@ -207,6 +207,9 @@
 					&:first-child
 						font-weight bold
 
+						> i
+							margin-right 4px
+
 			&:after
 				content ""
 				display block
@@ -234,7 +237,7 @@
 <mk-server-home-widget-memory>
 	<mk-server-home-widget-pie ref="pie"/>
 	<div>
-		<p>Memory</p>
+		<p><i class="fa fa-flask"></i>Memory</p>
 		<p>Total: { bytesToSize(total) }</p>
 		<p>Used: { bytesToSize(used) }</p>
 		<p>Free: { bytesToSize(free) }</p>
@@ -261,6 +264,9 @@
 					&:first-child
 						font-weight bold
 
+						> i
+							margin-right 4px
+
 			&:after
 				content ""
 				display block
@@ -297,7 +303,7 @@
 <mk-server-home-widget-disk>
 	<mk-server-home-widget-pie ref="pie"/>
 	<div>
-		<p>Storage</p>
+		<p><i class="fa fa-hdd-o"></i>Storage</p>
 		<p>Total: { bytesToSize(total) }</p>
 		<p>Available: { bytesToSize(available) }</p>
 		<p>Used: { bytesToSize(used) }</p>
@@ -324,6 +330,9 @@
 					&:first-child
 						font-weight bold
 
+						> i
+							margin-right 4px
+
 			&:after
 				content ""
 				display block
@@ -361,7 +370,7 @@
 <mk-server-home-widget-info>
 	<p>Maintainer: <b>{ meta.maintainer }</b></p>
 	<p>Machine: { meta.machine }</p>
-	<p>Node: { meta.node.version } { meta.node.release }</p>
+	<p>Node: { meta.node.version }</p>
 	<style>
 		:scope
 			display block

From 580b92c10203653934b28688783632d74b4e049f Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Sat, 10 Jun 2017 05:40:29 +0900
Subject: [PATCH 42/75] Revert "[CI] Use node 8.x"

This reverts commit fa966cdda6677ff80c706690c65ed9552e594858.
---
 .travis.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.travis.yml b/.travis.yml
index ea7660119..91e124443 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -4,7 +4,7 @@
 language: node_js
 
 node_js:
-  - 8.1.0
+  - 7.10.0
 
 env:
   - CXX=g++-4.8 NODE_ENV=production

From 8066aeff89d2409bf9e6605edc604e08412e397b Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Sat, 10 Jun 2017 05:40:40 +0900
Subject: [PATCH 43/75] v2073

---
 package.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/package.json b/package.json
index 507af3040..cf4b7145a 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
 {
   "name": "misskey",
   "author": "syuilo <i@syuilo.com>",
-  "version": "0.0.2069",
+  "version": "0.0.2073",
   "license": "MIT",
   "description": "A miniblog-based SNS",
   "bugs": "https://github.com/syuilo/misskey/issues",

From ea57992e6a255c5d204960b7ff5ee3c60e2964ee Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Sun, 11 Jun 2017 05:41:38 +0900
Subject: [PATCH 44/75] [Client] Improve graph rendering of the server home
 widget

---
 .../app/desktop/tags/home-widgets/server.tag  | 69 ++++++++++++-------
 1 file changed, 46 insertions(+), 23 deletions(-)

diff --git a/src/web/app/desktop/tags/home-widgets/server.tag b/src/web/app/desktop/tags/home-widgets/server.tag
index 7c2850239..5652f1be7 100644
--- a/src/web/app/desktop/tags/home-widgets/server.tag
+++ b/src/web/app/desktop/tags/home-widgets/server.tag
@@ -83,27 +83,49 @@
 
 <mk-server-home-widget-cpu-and-memory-usage>
 	<svg riot-viewBox="0 0 { viewBoxX } { viewBoxY }" preserveAspectRatio="none">
-		<polygon
-			riot-points={ cpuPolygonPoints }
-			riot-fill={ cpuColor }
-			fill-opacity="0.5"/>
-		<polyline
-			riot-points={ cpuPolylinePoints }
-			fill="none"
-			stroke-width="1"
-			riot-stroke={ cpuColor }/>
+		<defs>
+			<linearGradient id={ cpuGradientId } x1="0" x2="0" y1="1" y2="0">
+				<stop offset="0%" stop-color="hsl(180, 80%, 70%)"></stop>
+				<stop offset="33%" stop-color="hsl(120, 80%, 70%)"></stop>
+				<stop offset="66%" stop-color="hsl(60, 80%, 70%)"></stop>
+				<stop offset="100%" stop-color="hsl(0, 80%, 70%)"></stop>
+			</linearGradient>
+			<mask id={ cpuMaskId } x="0" y="0" riot-width={ viewBoxX } riot-height={ viewBoxY }>
+				<polygon
+					riot-points={ cpuPolygonPoints }
+					fill="#fff"
+					fill-opacity="0.5"/>
+				<polyline
+					riot-points={ cpuPolylinePoints }
+					fill="none"
+					stroke="#fff"
+					stroke-width="1"/>
+			</mask>
+		</defs>
+		<rect x="0" y="0" riot-width={ viewBoxX } riot-height={ viewBoxY } style="stroke: none; fill: url(#{ cpuGradientId }); mask: url(#{ cpuMaskId })"/>
 		<text x="1" y="5">CPU <tspan>{ cpuP }%</tspan></text>
 	</svg>
 	<svg riot-viewBox="0 0 { viewBoxX } { viewBoxY }" preserveAspectRatio="none">
-		<polygon
-			riot-points={ memPolygonPoints }
-			riot-fill={ memColor }
-			fill-opacity="0.5"/>
-		<polyline
-			riot-points={ memPolylinePoints }
-			fill="none"
-			stroke-width="1"
-			riot-stroke={ memColor }/>
+		<defs>
+			<linearGradient id={ memGradientId } x1="0" x2="0" y1="1" y2="0">
+				<stop offset="0%" stop-color="hsl(180, 80%, 70%)"></stop>
+				<stop offset="33%" stop-color="hsl(120, 80%, 70%)"></stop>
+				<stop offset="66%" stop-color="hsl(60, 80%, 70%)"></stop>
+				<stop offset="100%" stop-color="hsl(0, 80%, 70%)"></stop>
+			</linearGradient>
+			<mask id={ memMaskId } x="0" y="0" riot-width={ viewBoxX } riot-height={ viewBoxY }>
+				<polygon
+					riot-points={ memPolygonPoints }
+					fill="#fff"
+					fill-opacity="0.5"/>
+				<polyline
+					riot-points={ memPolylinePoints }
+					fill="none"
+					stroke="#fff"
+					stroke-width="1"/>
+			</mask>
+		</defs>
+		<rect x="0" y="0" riot-width={ viewBoxX } riot-height={ viewBoxY } style="stroke: none; fill: url(#{ memGradientId }); mask: url(#{ memMaskId })"/>
 		<text x="1" y="5">MEM <tspan>{ memP }%</tspan></text>
 	</svg>
 	<style>
@@ -135,10 +157,16 @@
 				clear both
 	</style>
 	<script>
+		import uuid from '../../../common/scripts/uuid';
+
 		this.viewBoxX = 50;
 		this.viewBoxY = 30;
 		this.stats = [];
 		this.connection = this.opts.connection;
+		this.cpuGradientId = uuid();
+		this.cpuMaskId = uuid();
+		this.memGradientId = uuid();
+		this.memMaskId = uuid();
 
 		this.on('mount', () => {
 			this.connection.on('stats', this.onStats);
@@ -159,9 +187,6 @@
 			const cpuPolygonPoints = `${this.viewBoxX - (this.stats.length - 1)},${ this.viewBoxY } ${ cpuPolylinePoints } ${ this.viewBoxX },${ this.viewBoxY }`;
 			const memPolygonPoints = `${this.viewBoxX - (this.stats.length - 1)},${ this.viewBoxY } ${ memPolylinePoints } ${ this.viewBoxX },${ this.viewBoxY }`;
 
-			const cpuColor = `hsl(${180 - (stats.cpu_usage * 180)}, 80%, 70%)`;
-			const memColor = `hsl(${180 - (stats.mem.used / stats.mem.total * 180)}, 80%, 70%)`;
-
 			const cpuP = (stats.cpu_usage * 100).toFixed(0);
 			const memP = (stats.mem.used / stats.mem.total * 100).toFixed(0);
 
@@ -170,8 +195,6 @@
 				memPolylinePoints,
 				cpuPolygonPoints,
 				memPolygonPoints,
-				cpuColor,
-				memColor,
 				cpuP,
 				memP
 			});

From 31b1717bd606c88ec014846d1db5bd7ae6f57e08 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Sun, 11 Jun 2017 05:42:28 +0900
Subject: [PATCH 45/75] v2075

---
 package.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/package.json b/package.json
index cf4b7145a..2cb440f05 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
 {
   "name": "misskey",
   "author": "syuilo <i@syuilo.com>",
-  "version": "0.0.2073",
+  "version": "0.0.2075",
   "license": "MIT",
   "description": "A miniblog-based SNS",
   "bugs": "https://github.com/syuilo/misskey/issues",

From 6c0c5fbe9e39317e4261170cff189d29fb9252a6 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Sun, 11 Jun 2017 05:57:24 +0900
Subject: [PATCH 46/75] [Client] Improve graph rendering of the server home
 widget

---
 src/web/app/desktop/tags/home-widgets/server.tag | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/src/web/app/desktop/tags/home-widgets/server.tag b/src/web/app/desktop/tags/home-widgets/server.tag
index 5652f1be7..cd6f6ac6a 100644
--- a/src/web/app/desktop/tags/home-widgets/server.tag
+++ b/src/web/app/desktop/tags/home-widgets/server.tag
@@ -102,7 +102,10 @@
 					stroke-width="1"/>
 			</mask>
 		</defs>
-		<rect x="0" y="0" riot-width={ viewBoxX } riot-height={ viewBoxY } style="stroke: none; fill: url(#{ cpuGradientId }); mask: url(#{ cpuMaskId })"/>
+		<rect
+			x="-1" y="-1"
+			riot-width={ viewBoxX + 2 } riot-height={ viewBoxY + 2 }
+			style="stroke: none; fill: url(#{ cpuGradientId }); mask: url(#{ cpuMaskId })"/>
 		<text x="1" y="5">CPU <tspan>{ cpuP }%</tspan></text>
 	</svg>
 	<svg riot-viewBox="0 0 { viewBoxX } { viewBoxY }" preserveAspectRatio="none">
@@ -125,7 +128,10 @@
 					stroke-width="1"/>
 			</mask>
 		</defs>
-		<rect x="0" y="0" riot-width={ viewBoxX } riot-height={ viewBoxY } style="stroke: none; fill: url(#{ memGradientId }); mask: url(#{ memMaskId })"/>
+		<rect
+			x="-1" y="-1"
+			riot-width={ viewBoxX + 2 } riot-height={ viewBoxY + 2 }
+			style="stroke: none; fill: url(#{ memGradientId }); mask: url(#{ memMaskId })"/>
 		<text x="1" y="5">MEM <tspan>{ memP }%</tspan></text>
 	</svg>
 	<style>

From 7c86b866bff9b604ee84194872ee04e3470f1281 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Sun, 11 Jun 2017 06:08:44 +0900
Subject: [PATCH 47/75] [Client] Improve bytes-to-size function

Add the digits argument
---
 src/web/app/common/scripts/bytes-to-size.js | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/web/app/common/scripts/bytes-to-size.js b/src/web/app/common/scripts/bytes-to-size.js
index e14338714..af0268dbd 100644
--- a/src/web/app/common/scripts/bytes-to-size.js
+++ b/src/web/app/common/scripts/bytes-to-size.js
@@ -1,6 +1,6 @@
-export default bytes => {
+export default (bytes, digits = 0) => {
 	var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
 	if (bytes == 0) return '0Byte';
 	var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)));
-	return Math.round(bytes / Math.pow(1024, i), 2) + sizes[i];
+	return (bytes / Math.pow(1024, i)).toFixed(digits).replace(/\.0+$/, '') + sizes[i];
 };

From e971d5bd3ede2fc30d6c22b060591c17e0db01dd Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Sun, 11 Jun 2017 06:09:05 +0900
Subject: [PATCH 48/75] [Client] Improve server home widget

---
 src/web/app/desktop/tags/home-widgets/server.tag | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/src/web/app/desktop/tags/home-widgets/server.tag b/src/web/app/desktop/tags/home-widgets/server.tag
index cd6f6ac6a..8b2006cfe 100644
--- a/src/web/app/desktop/tags/home-widgets/server.tag
+++ b/src/web/app/desktop/tags/home-widgets/server.tag
@@ -267,9 +267,9 @@
 	<mk-server-home-widget-pie ref="pie"/>
 	<div>
 		<p><i class="fa fa-flask"></i>Memory</p>
-		<p>Total: { bytesToSize(total) }</p>
-		<p>Used: { bytesToSize(used) }</p>
-		<p>Free: { bytesToSize(free) }</p>
+		<p>Total: { bytesToSize(total, 1) }</p>
+		<p>Used: { bytesToSize(used, 1) }</p>
+		<p>Free: { bytesToSize(free, 1) }</p>
 	</div>
 	<style>
 		:scope
@@ -333,9 +333,9 @@
 	<mk-server-home-widget-pie ref="pie"/>
 	<div>
 		<p><i class="fa fa-hdd-o"></i>Storage</p>
-		<p>Total: { bytesToSize(total) }</p>
-		<p>Available: { bytesToSize(available) }</p>
-		<p>Used: { bytesToSize(used) }</p>
+		<p>Total: { bytesToSize(total, 1) }</p>
+		<p>Available: { bytesToSize(available, 1) }</p>
+		<p>Used: { bytesToSize(used, 1) }</p>
 	</div>
 	<style>
 		:scope

From 339028472498a7c1ad41466fef436a332cf5caa1 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Sun, 11 Jun 2017 06:09:34 +0900
Subject: [PATCH 49/75] [Client] :art:

---
 src/web/app/desktop/tags/home-widgets/server.tag | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/web/app/desktop/tags/home-widgets/server.tag b/src/web/app/desktop/tags/home-widgets/server.tag
index 8b2006cfe..87525c395 100644
--- a/src/web/app/desktop/tags/home-widgets/server.tag
+++ b/src/web/app/desktop/tags/home-widgets/server.tag
@@ -403,7 +403,7 @@
 	<style>
 		:scope
 			display block
-			padding 10px
+			padding 10px 14px
 
 			> p
 				margin 0

From 9e94f6097e2392d90d7cebc4e48cc5b752eb3a20 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Sun, 11 Jun 2017 06:09:44 +0900
Subject: [PATCH 50/75] v2080

---
 package.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/package.json b/package.json
index 2cb440f05..ff0eaae66 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
 {
   "name": "misskey",
   "author": "syuilo <i@syuilo.com>",
-  "version": "0.0.2075",
+  "version": "0.0.2080",
   "license": "MIT",
   "description": "A miniblog-based SNS",
   "bugs": "https://github.com/syuilo/misskey/issues",

From 6a715c97ca06b4b7e946e7571e1ac00a38dfd782 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Sun, 11 Jun 2017 21:26:09 +0900
Subject: [PATCH 51/75] [Client] Improve error screen rendering

---
 locales/en.yml                    |  2 +-
 src/web/app/common/tags/error.tag | 35 ++++++++++++++++---------------
 src/web/app/init.js               |  8 ++++---
 3 files changed, 24 insertions(+), 21 deletions(-)

diff --git a/locales/en.yml b/locales/en.yml
index e15a7c9ae..9cc5df317 100644
--- a/locales/en.yml
+++ b/locales/en.yml
@@ -52,7 +52,7 @@ common:
       no-apps: "No apps"
 
     mk-error:
-      title: "Cannot connect to the server"
+      title: "Unable to connect to the server"
       description: "インターネット回線に問題があるか、サーバーがダウンまたはメンテナンスしている可能性があります。しばらくしてから再度お試しください。"
       thanks: "Thank you for using Misskey."
 
diff --git a/src/web/app/common/tags/error.tag b/src/web/app/common/tags/error.tag
index c7491d1c4..bbf796232 100644
--- a/src/web/app/common/tags/error.tag
+++ b/src/web/app/common/tags/error.tag
@@ -1,36 +1,25 @@
 <mk-error>
-	<!--i: i.fa.fa-times-circle-->
 	<img src="/assets/error.jpg" alt=""/>
 	<h1>%i18n:common.tags.mk-error.title%</h1>
 	<p class="text">%i18n:common.tags.mk-error.description%</p>
 	<p class="thanks">%i18n:common.tags.mk-error.thanks%</p>
 	<style>
 		:scope
-			position fixed
-			z-index 100000
-			top 0
-			left 0
+			display block
 			width 100%
-			height 100%
+			padding 32px 18px
 			text-align center
-			background #f8f8f8
-
-			> i
-				display block
-				margin-top 64px
-				font-size 5em
-				color #6998a0
 
 			> img
 				display block
 				height 200px
-				margin 64px auto 0 auto
+				margin 0 auto
 				pointer-events none
 				user-select none
 
 			> h1
 				display block
-				margin 32px auto 16px auto
+				margin 1.25em auto 0.65em auto
 				font-size 1.5em
 				color #555
 
@@ -43,13 +32,25 @@
 
 			> .thanks
 				display block
-				margin 32px auto 0 auto
-				padding 32px 0 32px 0
+				margin 2em auto 0 auto
+				padding 2em 0 0 0
 				max-width 600px
 				font-size 0.9em
 				font-style oblique
 				color #aaa
 				border-top solid 1px #eee
 
+			@media (max-width 500px)
+				padding 24px 18px
+				font-size 80%
+
+				> img
+					height 150px
+
 	</style>
+	<script>
+		this.on('mount', () => {
+			document.documentElement.style.background = '#f8f8f8';
+		});
+	</script>
 </mk-error>
diff --git a/src/web/app/init.js b/src/web/app/init.js
index b442d36a1..825bbc77e 100644
--- a/src/web/app/init.js
+++ b/src/web/app/init.js
@@ -9,6 +9,7 @@ import api from './common/scripts/api';
 import signout from './common/scripts/signout';
 import checkForUpdate from './common/scripts/check-for-update';
 import Connection from './common/scripts/home-stream';
+import Progress from './common/scripts/loading';
 import mixin from './common/mixins';
 import generateDefaultUserdata from './common/scripts/generate-default-userdata';
 import CONFIG from './common/scripts/config';
@@ -147,9 +148,10 @@ function fetchme(token, cb) {
 			me.data ? done() : init();
 		});
 	}, () => { // When failure
-		// Display error screen
-		riot.mount(document.body.appendChild(
-			document.createElement('mk-error')));
+		// Render the error screen
+		document.body.innerHTML = '<mk-error />';
+		riot.mount('*');
+		Progress.done();
 	});
 
 	function done() {

From f2236c3c3b38f7994b97789af0c77859830d3538 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Mon, 12 Jun 2017 01:21:28 +0900
Subject: [PATCH 52/75] [Client] Oops!

---
 src/web/app/common/tags/error.tag | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/web/app/common/tags/error.tag b/src/web/app/common/tags/error.tag
index bbf796232..e4e0272a4 100644
--- a/src/web/app/common/tags/error.tag
+++ b/src/web/app/common/tags/error.tag
@@ -50,6 +50,7 @@
 	</style>
 	<script>
 		this.on('mount', () => {
+			document.title = 'Oops!';
 			document.documentElement.style.background = '#f8f8f8';
 		});
 	</script>

From d9c9a7f236fcfe8595181dce4ca4814138b78201 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Mon, 12 Jun 2017 02:05:23 +0900
Subject: [PATCH 53/75] :v:

---
 src/api/endpoints/meta.ts                     |  8 ++--
 src/utils/stats.ts                            |  4 +-
 .../app/desktop/tags/home-widgets/server.tag  | 47 +++++++++++++++++--
 3 files changed, 50 insertions(+), 9 deletions(-)

diff --git a/src/api/endpoints/meta.ts b/src/api/endpoints/meta.ts
index 7ab72ae8f..a3f1d5032 100644
--- a/src/api/endpoints/meta.ts
+++ b/src/api/endpoints/meta.ts
@@ -44,12 +44,10 @@ module.exports = (params) => new Promise(async (res, rej) => {
 		version: version,
 		secure: config.https.enable,
 		machine: os.hostname(),
-		node: {
-			release: (process as any).release.name,
-			lts: (process as any).release.lts,
-			version: process.version
-		},
+		os: os.platform(),
+		node: process.version,
 		cpu: {
+			model: os.cpus()[0].model,
 			cores: os.cpus().length
 		}
 	});
diff --git a/src/utils/stats.ts b/src/utils/stats.ts
index 161526831..cfb710f5e 100644
--- a/src/utils/stats.ts
+++ b/src/utils/stats.ts
@@ -18,7 +18,9 @@ export default function() {
 					total: os.totalmem(),
 					free: os.freemem()
 				},
-				disk
+				disk,
+				os_uptime: os.uptime(),
+				process_uptime: process.uptime()
 			});
 		});
 	}, 1000);
diff --git a/src/web/app/desktop/tags/home-widgets/server.tag b/src/web/app/desktop/tags/home-widgets/server.tag
index 87525c395..040ca1232 100644
--- a/src/web/app/desktop/tags/home-widgets/server.tag
+++ b/src/web/app/desktop/tags/home-widgets/server.tag
@@ -6,7 +6,8 @@
 	<mk-server-home-widget-cpu if={ !initializing } show={ view == 1 } connection={ connection } meta={ meta }/>
 	<mk-server-home-widget-memory if={ !initializing } show={ view == 2 } connection={ connection }/>
 	<mk-server-home-widget-disk if={ !initializing } show={ view == 3 } connection={ connection }/>
-	<mk-server-home-widget-info if={ !initializing } show={ view == 4 } connection={ connection } meta={ meta }/>
+	<mk-server-home-widget-uptimes if={ !initializing } show={ view == 4 } connection={ connection }/>
+	<mk-server-home-widget-info if={ !initializing } show={ view == 5 } connection={ connection } meta={ meta }/>
 	<style>
 		:scope
 			display block
@@ -76,7 +77,7 @@
 
 		this.toggle = () => {
 			this.view++;
-			if (this.view == 5) this.view = 0;
+			if (this.view == 6) this.view = 0;
 		};
 	</script>
 </mk-server-home-widget>
@@ -213,6 +214,7 @@
 	<div>
 		<p><i class="fa fa-microchip"></i>CPU</p>
 		<p>{ cores } Cores</p>
+		<p>{ model }</p>
 	</div>
 	<style>
 		:scope
@@ -247,6 +249,7 @@
 	</style>
 	<script>
 		this.cores = this.opts.meta.cpu.cores;
+		this.model = this.opts.meta.cpu.model;
 		this.connection = this.opts.connection;
 
 		this.on('mount', () => {
@@ -396,10 +399,48 @@
 	</script>
 </mk-server-home-widget-disk>
 
+<mk-server-home-widget-uptimes>
+	<p>Uptimes</p>
+	<p>Process: { process.toFixed(0) }s</p>
+	<p>OS: { os.toFixed(0) }s</p>
+	<style>
+		:scope
+			display block
+			padding 10px 14px
+
+			> p
+				margin 0
+				font-size 12px
+				color #505050
+
+				&:first-child
+					font-weight bold
+
+	</style>
+	<script>
+		this.connection = this.opts.connection;
+
+		this.on('mount', () => {
+			this.connection.on('stats', this.onStats);
+		});
+
+		this.on('unmount', () => {
+			this.connection.off('stats', this.onStats);
+		});
+
+		this.onStats = stats => {
+			this.update({
+				process: stats.process_uptime,
+				os: stats.os_uptime
+			});
+		};
+	</script>
+</mk-server-home-widget-uptimes>
+
 <mk-server-home-widget-info>
 	<p>Maintainer: <b>{ meta.maintainer }</b></p>
 	<p>Machine: { meta.machine }</p>
-	<p>Node: { meta.node.version }</p>
+	<p>Node: { meta.node }</p>
 	<style>
 		:scope
 			display block

From c7af2bc88545b52bc0664ca38218bf4be6b98fd8 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Mon, 12 Jun 2017 04:26:32 +0900
Subject: [PATCH 54/75] [Client] Fix bug

---
 src/web/app/desktop/tags/home-widgets/server.tag | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/web/app/desktop/tags/home-widgets/server.tag b/src/web/app/desktop/tags/home-widgets/server.tag
index 040ca1232..bc8f313d5 100644
--- a/src/web/app/desktop/tags/home-widgets/server.tag
+++ b/src/web/app/desktop/tags/home-widgets/server.tag
@@ -401,8 +401,8 @@
 
 <mk-server-home-widget-uptimes>
 	<p>Uptimes</p>
-	<p>Process: { process.toFixed(0) }s</p>
-	<p>OS: { os.toFixed(0) }s</p>
+	<p>Process: { process ? process.toFixed(0) : '---' }s</p>
+	<p>OS: { os ? os.toFixed(0) : '---' }s</p>
 	<style>
 		:scope
 			display block

From 0039f42a0924abd1ab977c3c20d342bfffb6bcea Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Mon, 12 Jun 2017 04:27:20 +0900
Subject: [PATCH 55/75] [Client] Implement more messages fetching of messaging

---
 locales/en.yml                             |  2 +
 locales/ja.yml                             |  2 +
 src/web/app/common/tags/messaging/room.tag | 63 ++++++++++++++++++++--
 3 files changed, 63 insertions(+), 4 deletions(-)

diff --git a/locales/en.yml b/locales/en.yml
index 9cc5df317..b4c94ad47 100644
--- a/locales/en.yml
+++ b/locales/en.yml
@@ -45,6 +45,8 @@ common:
 
     mk-messaging-room:
       empty: "No conversations"
+      more: "More"
+      no-history: "There is no more history"
       resize-form: "Drag to resize"
       new-message: "New message"
 
diff --git a/locales/ja.yml b/locales/ja.yml
index 5ed1d4f1e..db247f8e5 100644
--- a/locales/ja.yml
+++ b/locales/ja.yml
@@ -45,6 +45,8 @@ common:
 
     mk-messaging-room:
       empty: "このユーザーと話したことはありません"
+      more: "もっと読む"
+      no-history: "これより過去の履歴はありません"
       resize-form: "ドラッグしてフォームの広さを調整"
       new-message: "新しいメッセージがあります"
 
diff --git a/src/web/app/common/tags/messaging/room.tag b/src/web/app/common/tags/messaging/room.tag
index 93f07f2ce..5c7f76936 100644
--- a/src/web/app/common/tags/messaging/room.tag
+++ b/src/web/app/common/tags/messaging/room.tag
@@ -2,6 +2,10 @@
 	<div class="stream">
 		<p class="init" if={ init }><i class="fa fa-spinner fa-spin"></i>%i18n:common.loading%</p>
 		<p class="empty" if={ !init && messages.length == 0 }><i class="fa fa-info-circle"></i>%i18n:common.tags.mk-messaging-room.empty%</p>
+		<p class="no-history" if={ !init && messages.length > 0 && !moreMessagesIsInStock }><i class="fa fa-flag"></i>%i18n:common.tags.mk-messaging-room.no-history%</p>
+		<button class="more { fetching: fetchingMoreMessages }" if={ moreMessagesIsInStock } onclick={ fetchMoreMessages } disabled={ fetchingMoreMessages }>
+			<i class="fa fa-spinner fa-pulse fa-fw" if={ fetchingMoreMessages }></i>{ fetchingMoreMessages ? '%i18n:common.loading%' : '%i18n:common.tags.mk-messaging-room.more%' }
+		</button>
 		<virtual each={ message, i in messages }>
 			<mk-messaging-message message={ message }/>
 			<p class="date" if={ i != messages.length - 1 && message._date != messages[i + 1]._date }><span>{ messages[i + 1]._datetext }</span></p>
@@ -42,6 +46,27 @@
 					i
 						margin-right 4px
 
+				> .more
+					display block
+					margin 16px auto
+					padding 0 12px
+					line-height 24px
+					color #fff
+					background rgba(0, 0, 0, 0.3)
+					border-radius 12px
+
+					&:hover
+						background rgba(0, 0, 0, 0.4)
+
+					&:active
+						background rgba(0, 0, 0, 0.5)
+
+					&.fetching
+						cursor wait
+
+					> i
+						margin-right 4px
+
 				> .message
 					// something
 
@@ -142,11 +167,8 @@
 
 			document.addEventListener('visibilitychange', this.onVisibilitychange);
 
-			this.api('messaging/messages', {
-				user_id: this.user.id
-			}).then(messages => {
+			this.fetchMessages().then(() => {
 				this.init = false;
-				this.messages = messages.reverse();
 				this.update();
 				this.scrollToBottom();
 			});
@@ -201,6 +223,39 @@
 			});
 		};
 
+		this.fetchMoreMessages = () => {
+			this.update({
+				fetchingMoreMessages: true
+			});
+			this.fetchMessages().then(() => {
+				this.update({
+					fetchingMoreMessages: false
+				});
+			});
+		};
+
+		this.fetchMessages = () => new Promise((resolve, reject) => {
+			const max = this.moreMessagesIsInStock ? 20 : 10;
+
+			this.api('messaging/messages', {
+				user_id: this.user.id,
+				limit: max + 1,
+				max_id: this.moreMessagesIsInStock ? this.messages[0].id : undefined
+			}).then(messages => {
+				if (messages.length == max + 1) {
+					this.moreMessagesIsInStock = true;
+					messages.pop();
+				} else {
+					this.moreMessagesIsInStock = false;
+				}
+
+				this.messages.unshift.apply(this.messages, messages.reverse());
+				this.update();
+
+				resolve();
+			});
+		});
+
 		this.isBottom = () => {
 			const asobi = 32;
 			const current = this.isNaked

From 17182266fc7e11e6aed4601f5c7e45290a0e8a16 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Mon, 12 Jun 2017 04:28:15 +0900
Subject: [PATCH 56/75] v2086

---
 package.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/package.json b/package.json
index ff0eaae66..60891c255 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
 {
   "name": "misskey",
   "author": "syuilo <i@syuilo.com>",
-  "version": "0.0.2080",
+  "version": "0.0.2086",
   "license": "MIT",
   "description": "A miniblog-based SNS",
   "bugs": "https://github.com/syuilo/misskey/issues",

From 5880b90f599cfc7eeab714d61f3fc8b9f3be7e0f Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Tue, 13 Jun 2017 02:12:30 +0900
Subject: [PATCH 57/75] [API] Refactor and Bug fix

---
 src/api/common/read-messaging-message.ts | 64 ++++++++++++++++++++++++
 src/api/endpoints/messaging/messages.ts  | 31 +-----------
 src/api/models/messaging-message.ts      |  5 ++
 src/api/stream/messaging.ts              | 41 ++-------------
 4 files changed, 74 insertions(+), 67 deletions(-)
 create mode 100644 src/api/common/read-messaging-message.ts

diff --git a/src/api/common/read-messaging-message.ts b/src/api/common/read-messaging-message.ts
new file mode 100644
index 000000000..3257ec8b0
--- /dev/null
+++ b/src/api/common/read-messaging-message.ts
@@ -0,0 +1,64 @@
+import * as mongo from 'mongodb';
+import Message from '../models/messaging-message';
+import { IMessagingMessage as IMessage } from '../models/messaging-message';
+import publishUserStream from '../event';
+import { publishMessagingStream } from '../event';
+
+/**
+ * Mark as read message(s)
+ */
+export default (
+	user: string | mongo.ObjectID,
+	otherparty: string | mongo.ObjectID,
+	message: string | string[] | IMessage | IMessage[] | mongo.ObjectID | mongo.ObjectID[]
+) => new Promise<any>(async (resolve, reject) => {
+
+	const userId = mongo.ObjectID.prototype.isPrototypeOf(user)
+		? user
+		: new mongo.ObjectID(user);
+
+	const otherpartyId = mongo.ObjectID.prototype.isPrototypeOf(otherparty)
+		? otherparty
+		: new mongo.ObjectID(otherparty);
+
+	const ids: mongo.ObjectID[] = Array.isArray(message)
+		? mongo.ObjectID.prototype.isPrototypeOf(message[0])
+			? (message as mongo.ObjectID[])
+			: typeof message[0] === 'string'
+				? (message as string[]).map(m => new mongo.ObjectID(m))
+				: (message as IMessage[]).map(m => m._id)
+		: mongo.ObjectID.prototype.isPrototypeOf(message)
+			? [(message as mongo.ObjectID)]
+			: typeof message === 'string'
+				? [new mongo.ObjectID(message)]
+				: [(message as IMessage)._id];
+
+	// Update documents
+	await Message.update({
+		_id: { $in: ids },
+		user_id: otherpartyId,
+		recipient_id: userId,
+		is_read: false
+	}, {
+		$set: {
+			is_read: true
+		}
+	}, {
+		multi: true
+	});
+
+	// Publish event
+	publishMessagingStream(otherpartyId, userId, 'read', ids.map(id => id.toString()));
+
+	// Calc count of my unread messages
+	const count = await Message
+		.count({
+			recipient_id: userId,
+			is_read: false
+		});
+
+	if (count == 0) {
+		// 全ての(いままで未読だった)自分宛てのメッセージを(これで)読みましたよというイベントを発行
+		publishUserStream(userId, 'read_all_messaging_messages');
+	}
+});
diff --git a/src/api/endpoints/messaging/messages.ts b/src/api/endpoints/messaging/messages.ts
index b3a5c57f6..7b270924e 100644
--- a/src/api/endpoints/messaging/messages.ts
+++ b/src/api/endpoints/messaging/messages.ts
@@ -5,8 +5,7 @@ import $ from 'cafy';
 import Message from '../../models/messaging-message';
 import User from '../../models/user';
 import serialize from '../../serializers/messaging-message';
-import publishUserStream from '../../event';
-import { publishMessagingStream } from '../../event';
+import read from '../../common/read-messaging-message';
 
 /**
  * Get messages
@@ -98,32 +97,6 @@ module.exports = (params, user) => new Promise(async (res, rej) => {
 
 	// Mark as read all
 	if (markAsRead) {
-		const ids = messages
-			.filter(m => m.is_read == false)
-			.filter(m => m.recipient_id.equals(user._id))
-			.map(m => m._id);
-
-		// Update documents
-		await Message.update({
-			_id: { $in: ids }
-		}, {
-			$set: { is_read: true }
-		}, {
-			multi: true
-		});
-
-		// Publish event
-		publishMessagingStream(recipient._id, user._id, 'read', ids.map(id => id.toString()));
-
-		const count = await Message
-			.count({
-				recipient_id: user._id,
-				is_read: false
-			});
-
-		if (count == 0) {
-			// 全ての(いままで未読だった)メッセージを(これで)読みましたよというイベントを発行
-			publishUserStream(user._id, 'read_all_messaging_messages');
-		}
+		read(user._id, recipient._id, messages);
 	}
 });
diff --git a/src/api/models/messaging-message.ts b/src/api/models/messaging-message.ts
index 81aee2cab..18afa57e4 100644
--- a/src/api/models/messaging-message.ts
+++ b/src/api/models/messaging-message.ts
@@ -1,7 +1,12 @@
+import * as mongo from 'mongodb';
 import db from '../../db/mongodb';
 
 export default db.get('messaging_messages') as any; // fuck type definition
 
+export interface IMessagingMessage {
+	_id: mongo.ObjectID;
+}
+
 export function isValidText(text: string): boolean {
 	return text.length <= 1000 && text.trim() != '';
 }
diff --git a/src/api/stream/messaging.ts b/src/api/stream/messaging.ts
index 71bf7a34c..f94625d8e 100644
--- a/src/api/stream/messaging.ts
+++ b/src/api/stream/messaging.ts
@@ -1,8 +1,7 @@
 import * as mongodb from 'mongodb';
 import * as websocket from 'websocket';
 import * as redis from 'redis';
-import Message from '../models/messaging-message';
-import { publishMessagingStream } from '../event';
+import read from '../common/read-messaging-message';
 
 export default function messagingStream(request: websocket.request, connection: websocket.connection, subscriber: redis.RedisClient, user: any): void {
 	const otherparty = request.resourceURL.query.otherparty;
@@ -18,42 +17,8 @@ export default function messagingStream(request: websocket.request, connection:
 
 		switch (msg.type) {
 			case 'read':
-				if (!msg.id) {
-					return;
-				}
-
-				const id = new mongodb.ObjectID(msg.id);
-
-				// Fetch message
-				// SELECT _id, user_id, is_read
-				const message = await Message.findOne({
-					_id: id,
-					recipient_id: user._id
-				}, {
-					fields: {
-						_id: true,
-						user_id: true,
-						is_read: true
-					}
-				});
-
-				if (message == null) {
-					return;
-				}
-
-				if (message.is_read) {
-					return;
-				}
-
-				// Update documents
-				await Message.update({
-					_id: id
-				}, {
-					$set: { is_read: true }
-				});
-
-				// Publish event
-				publishMessagingStream(message.user_id, user._id, 'read', id.toString());
+				if (!msg.id) return;
+				read(user._id, otherparty, msg.id);
 				break;
 		}
 	});

From fe808b5609c4afc10adfb12f593275328aa03d53 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Tue, 13 Jun 2017 02:54:45 +0900
Subject: [PATCH 58/75] :art:

---
 src/web/app/common/tags/messaging/room.tag | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/src/web/app/common/tags/messaging/room.tag b/src/web/app/common/tags/messaging/room.tag
index 5c7f76936..1baf468c2 100644
--- a/src/web/app/common/tags/messaging/room.tag
+++ b/src/web/app/common/tags/messaging/room.tag
@@ -24,6 +24,17 @@
 				max-width 600px
 				margin 0 auto
 
+				> .init
+					width 100%
+					margin 0
+					padding 16px 8px 8px 8px
+					text-align center
+					font-size 0.8em
+					color rgba(0, 0, 0, 0.4)
+
+					i
+						margin-right 4px
+
 				> .empty
 					width 100%
 					margin 0

From 72b7c8413934c424cd5ff897081931f5629c807e Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Tue, 13 Jun 2017 03:20:43 +0900
Subject: [PATCH 59/75] [Client] Improve error screen rendering

---
 src/web/app/base.styl | 7 -------
 src/web/app/init.js   | 1 +
 2 files changed, 1 insertion(+), 7 deletions(-)

diff --git a/src/web/app/base.styl b/src/web/app/base.styl
index 9fdb39a16..81c039f0a 100644
--- a/src/web/app/base.styl
+++ b/src/web/app/base.styl
@@ -30,14 +30,7 @@ html
 			cursor progress !important
 
 #error
-	position fixed
-	z-index 32768
-	top 0
-	left 0
-	width 100%
-	height 100%
 	padding 32px
-	background #1269e2
 	color #fff
 
 	hr
diff --git a/src/web/app/init.js b/src/web/app/init.js
index 825bbc77e..44391b8fc 100644
--- a/src/web/app/init.js
+++ b/src/web/app/init.js
@@ -175,6 +175,7 @@ function panic(e) {
 	console.error(e);
 
 	// Display blue screen
+	document.documentElement.style.background = '#1269e2';
 	document.body.innerHTML =
 		'<div id="error">'
 			+ '<h1>:( 致命的な問題が発生しました。</h1>'

From 7466ec3b0f2f920cefa4d2d9843cf7026936d785 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Tue, 13 Jun 2017 03:42:24 +0900
Subject: [PATCH 60/75] #271

---
 src/web/app/mobile/tags/ui-header.tag | 41 ++++++++++++++++++++
 src/web/app/mobile/tags/ui-nav.tag    | 55 +++++++++++++++++++++++----
 2 files changed, 88 insertions(+), 8 deletions(-)

diff --git a/src/web/app/mobile/tags/ui-header.tag b/src/web/app/mobile/tags/ui-header.tag
index 264fa022d..10b44b215 100644
--- a/src/web/app/mobile/tags/ui-header.tag
+++ b/src/web/app/mobile/tags/ui-header.tag
@@ -4,6 +4,7 @@
 		<div class="backdrop"></div>
 		<div class="content">
 			<button class="nav" onclick={ parent.toggleDrawer }><i class="fa fa-bars"></i></button>
+			<i class="fa fa-circle" if={ hasUnreadMessagingMessages }></i>
 			<h1 ref="title">Misskey</h1>
 			<button if={ func } onclick={ func }><i class="fa fa-{ funcIcon }"></i></button>
 		</div>
@@ -74,6 +75,14 @@
 						> i
 							transition all 0.2s ease
 
+					> i
+						position absolute
+						top 8px
+						left 8px
+						pointer-events none
+						font-size 10px
+						color $theme-color
+
 					> button:last-child
 						display block
 						position absolute
@@ -90,14 +99,46 @@
 	<script>
 		import ui from '../scripts/ui-event';
 
+		this.mixin('api');
+		this.mixin('stream');
+
 		this.func = null;
 		this.funcIcon = null;
 
+		this.on('mount', () => {
+			this.stream.on('read_all_messaging_messages', this.onReadAllMessagingMessages);
+			this.stream.on('unread_messaging_message', this.onUnreadMessagingMessage);
+
+			// Fetch count of unread messaging messages
+			this.api('messaging/unread').then(res => {
+				if (res.count > 0) {
+					this.update({
+						hasUnreadMessagingMessages: true
+					});
+				}
+			});
+		});
+
 		this.on('unmount', () => {
+			this.stream.off('read_all_messaging_messages', this.onReadAllMessagingMessages);
+			this.stream.off('unread_messaging_message', this.onUnreadMessagingMessage);
+
 			ui.off('title', this.setTitle);
 			ui.off('func', this.setFunc);
 		});
 
+		this.onReadAllMessagingMessages = () => {
+			this.update({
+				hasUnreadMessagingMessages: false
+			});
+		};
+
+		this.onUnreadMessagingMessage = () => {
+			this.update({
+				hasUnreadMessagingMessages: true
+			});
+		};
+
 		this.setTitle = title => {
 			this.refs.title.innerHTML = title;
 		};
diff --git a/src/web/app/mobile/tags/ui-nav.tag b/src/web/app/mobile/tags/ui-nav.tag
index 964eab9a5..76c43ade6 100644
--- a/src/web/app/mobile/tags/ui-nav.tag
+++ b/src/web/app/mobile/tags/ui-nav.tag
@@ -7,18 +7,18 @@
 		</a>
 		<div class="links">
 			<ul>
-				<li class="home"><a href="/"><i class="icon fa fa-home"></i>%i18n:mobile.tags.mk-ui-nav.home%<i class="angle fa fa-angle-right"></i></a></li>
-				<li class="notifications"><a href="/i/notifications"><i class="icon fa fa-bell-o"></i>%i18n:mobile.tags.mk-ui-nav.notifications%<i class="angle fa fa-angle-right"></i></a></li>
-				<li class="messaging"><a href="/i/messaging"><i class="icon fa fa-comments-o"></i>%i18n:mobile.tags.mk-ui-nav.messaging%<i class="angle fa fa-angle-right"></i></a></li>
+				<li><a href="/"><i class="fa fa-home"></i>%i18n:mobile.tags.mk-ui-nav.home%<i class="fa fa-angle-right"></i></a></li>
+				<li><a href="/i/notifications"><i class="fa fa-bell-o"></i>%i18n:mobile.tags.mk-ui-nav.notifications%<i class="fa fa-angle-right"></i></a></li>
+				<li><a href="/i/messaging"><i class="fa fa-comments-o"></i>%i18n:mobile.tags.mk-ui-nav.messaging%<i class="i fa fa-circle" if={ hasUnreadMessagingMessages }></i><i class="fa fa-angle-right"></i></a></li>
 			</ul>
 			<ul>
-				<li class="settings"><a onclick={ search }><i class="icon fa fa-search"></i>%i18n:mobile.tags.mk-ui-nav.search%<i class="angle fa fa-angle-right"></i></a></li>
+				<li><a onclick={ search }><i class="fa fa-search"></i>%i18n:mobile.tags.mk-ui-nav.search%<i class="fa fa-angle-right"></i></a></li>
 			</ul>
 			<ul>
-				<li class="settings"><a href="/i/drive"><i class="icon fa fa-cloud"></i>%i18n:mobile.tags.mk-ui-nav.drive%<i class="angle fa fa-angle-right"></i></a></li>
+				<li><a href="/i/drive"><i class="fa fa-cloud"></i>%i18n:mobile.tags.mk-ui-nav.drive%<i class="fa fa-angle-right"></i></a></li>
 			</ul>
 			<ul>
-				<li class="settings"><a href="/i/settings"><i class="icon fa fa-cog"></i>%i18n:mobile.tags.mk-ui-nav.settings%<i class="angle fa fa-angle-right"></i></a></li>
+				<li><a href="/i/settings"><i class="fa fa-cog"></i>%i18n:mobile.tags.mk-ui-nav.settings%<i class="fa fa-angle-right"></i></a></li>
 			</ul>
 		</div>
 		<a href={ CONFIG.aboutUrl }><p class="about">%i18n:mobile.tags.mk-ui-nav.about%</p></a>
@@ -94,10 +94,16 @@
 						color #777
 						text-decoration none
 
-						> .icon
+						> i:first-child
 							margin-right 0.5em
 
-						> .angle
+						> .i
+							margin-left 6px
+							vertical-align super
+							font-size 10px
+							color $theme-color
+
+						> i:last-child
 							position absolute
 							top 0
 							right 0
@@ -120,6 +126,39 @@
 	<script>
 		this.mixin('i');
 		this.mixin('page');
+		this.mixin('api');
+		this.mixin('stream');
+
+		this.on('mount', () => {
+			this.stream.on('read_all_messaging_messages', this.onReadAllMessagingMessages);
+			this.stream.on('unread_messaging_message', this.onUnreadMessagingMessage);
+
+			// Fetch count of unread messaging messages
+			this.api('messaging/unread').then(res => {
+				if (res.count > 0) {
+					this.update({
+						hasUnreadMessagingMessages: true
+					});
+				}
+			});
+		});
+
+		this.on('unmount', () => {
+			this.stream.off('read_all_messaging_messages', this.onReadAllMessagingMessages);
+			this.stream.off('unread_messaging_message', this.onUnreadMessagingMessage);
+		});
+
+		this.onReadAllMessagingMessages = () => {
+			this.update({
+				hasUnreadMessagingMessages: false
+			});
+		};
+
+		this.onUnreadMessagingMessage = () => {
+			this.update({
+				hasUnreadMessagingMessages: true
+			});
+		};
 
 		this.search = () => {
 			const query = window.prompt('%i18n:mobile.tags.mk-ui-nav.search%');

From fb20197d6eba271f99c5e1eecba171311df83461 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Tue, 13 Jun 2017 03:42:45 +0900
Subject: [PATCH 61/75] :v:

---
 src/api/endpoints/messaging/messages/create.ts | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/api/endpoints/messaging/messages/create.ts b/src/api/endpoints/messaging/messages/create.ts
index 05f9cda4c..8af55d850 100644
--- a/src/api/endpoints/messaging/messages/create.ts
+++ b/src/api/endpoints/messaging/messages/create.ts
@@ -93,13 +93,13 @@ module.exports = (params, user) => new Promise(async (res, rej) => {
 	publishMessagingStream(message.recipient_id, message.user_id, 'message', messageObj);
 	publishUserStream(message.recipient_id, 'messaging_message', messageObj);
 
-	// 5秒経っても(今回作成した)メッセージが既読にならなかったら「未読のメッセージがありますよ」イベントを発行する
+	// 3秒経っても(今回作成した)メッセージが既読にならなかったら「未読のメッセージがありますよ」イベントを発行する
 	setTimeout(async () => {
 		const freshMessage = await Message.findOne({ _id: message._id }, { is_read: true });
 		if (!freshMessage.is_read) {
 			publishUserStream(message.recipient_id, 'unread_messaging_message', messageObj);
 		}
-	}, 5000);
+	}, 3000);
 
 	// Register to search database
 	if (message.text && config.elasticsearch.enable) {

From 25b3b3f1d1012a8d09105102e1cb4900f8c34d8d Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Tue, 13 Jun 2017 03:43:10 +0900
Subject: [PATCH 62/75] =?UTF-8?q?[Client]=20=E3=82=A2=E3=82=A4=E3=82=B3?=
 =?UTF-8?q?=E3=83=B3=E3=81=AE=E8=A7=A3=E5=83=8F=E5=BA=A6=E3=82=A2=E3=83=83?=
 =?UTF-8?q?=E3=83=97?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/web/app/common/tags/messaging/message.tag | 5 ++++-
 src/web/app/mobile/tags/user.tag              | 2 +-
 2 files changed, 5 insertions(+), 2 deletions(-)

diff --git a/src/web/app/common/tags/messaging/message.tag b/src/web/app/common/tags/messaging/message.tag
index ec8138da0..d6db9070e 100644
--- a/src/web/app/common/tags/messaging/message.tag
+++ b/src/web/app/common/tags/messaging/message.tag
@@ -1,4 +1,7 @@
-<mk-messaging-message data-is-me={ message.is_me }><a class="avatar-anchor" href={ '/' + message.user.username } title={ message.user.username } target="_blank"><img class="avatar" src={ message.user.avatar_url + '?thumbnail&size=64' } alt=""/></a>
+<mk-messaging-message data-is-me={ message.is_me }>
+	<a class="avatar-anchor" href={ '/' + message.user.username } title={ message.user.username } target="_blank">
+		<img class="avatar" src={ message.user.avatar_url + '?thumbnail&size=80' } alt=""/>
+	</a>
 	<div class="content-container">
 		<div class="balloon">
 			<p class="read" if={ message.is_me && message.is_read }>%i18n:common.tags.mk-messaging-message.is-read%</p>
diff --git a/src/web/app/mobile/tags/user.tag b/src/web/app/mobile/tags/user.tag
index 9f36e6074..81eb6ba2e 100644
--- a/src/web/app/mobile/tags/user.tag
+++ b/src/web/app/mobile/tags/user.tag
@@ -5,7 +5,7 @@
 			<div class="body">
 				<div class="top">
 					<a class="avatar">
-						<img src={ user.avatar_url + '?thumbnail&size=160' } alt="avatar"/>
+						<img src={ user.avatar_url + '?thumbnail&size=200' } alt="avatar"/>
 					</a>
 					<mk-follow-button if={ SIGNIN && I.id != user.id } user={ user }/>
 				</div>

From 703a32d8457d0eed8478b1881ded845132440e02 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Tue, 13 Jun 2017 03:44:00 +0900
Subject: [PATCH 63/75] v2093

---
 package.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/package.json b/package.json
index 60891c255..8f49de707 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
 {
   "name": "misskey",
   "author": "syuilo <i@syuilo.com>",
-  "version": "0.0.2086",
+  "version": "0.0.2093",
   "license": "MIT",
   "description": "A miniblog-based SNS",
   "bugs": "https://github.com/syuilo/misskey/issues",

From ac94cf91df8142a4d927f19a944393bf243863e0 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Tue, 13 Jun 2017 06:27:32 +0900
Subject: [PATCH 64/75] [Client] :v:

---
 locales/en.yml                         |  2 +-
 locales/ja.yml                         |  2 +-
 src/web/app/common/scripts/get-cao.js  |  5 +++++
 src/web/app/common/scripts/get-cat.js  |  1 -
 src/web/app/desktop/tags/post-form.tag | 10 +++++-----
 src/web/app/mobile/tags/post-form.tag  | 10 +++++-----
 6 files changed, 17 insertions(+), 13 deletions(-)
 create mode 100644 src/web/app/common/scripts/get-cao.js
 delete mode 100644 src/web/app/common/scripts/get-cat.js

diff --git a/locales/en.yml b/locales/en.yml
index b4c94ad47..a01392809 100644
--- a/locales/en.yml
+++ b/locales/en.yml
@@ -221,7 +221,7 @@ desktop:
       attach-media-from-local: "Attach media from your pc"
       attach-media-from-drive: "Attach media from the drive"
       attach-cancel: "Cancel attachment"
-      insert-the-cat: "Insert a cat"
+      insert-a-kao: "v(‘ω’)v"
       create-poll: "Create a poll"
       text-remain: "{} chars remaining"
 
diff --git a/locales/ja.yml b/locales/ja.yml
index db247f8e5..d55e690e9 100644
--- a/locales/ja.yml
+++ b/locales/ja.yml
@@ -221,7 +221,7 @@ desktop:
       attach-media-from-local: "PCからメディアを添付"
       attach-media-from-drive: "ドライブからメディアを添付"
       attach-cancel: "添付取り消し"
-      insert-the-cat: "猫挿入"
+      insert-a-kao: "v(‘ω’)v"
       create-poll: "投票を作成"
       text-remain: "のこり{}文字"
 
diff --git a/src/web/app/common/scripts/get-cao.js b/src/web/app/common/scripts/get-cao.js
new file mode 100644
index 000000000..0b77ee285
--- /dev/null
+++ b/src/web/app/common/scripts/get-cao.js
@@ -0,0 +1,5 @@
+export default () => [
+	'(=^・・^=)',
+	'v(‘ω’)v',
+	'🐡( '-' 🐡 )フグパンチ!!!!'
+][Math.floor(Math.random() * 3)];
diff --git a/src/web/app/common/scripts/get-cat.js b/src/web/app/common/scripts/get-cat.js
deleted file mode 100644
index cad42c88c..000000000
--- a/src/web/app/common/scripts/get-cat.js
+++ /dev/null
@@ -1 +0,0 @@
-export default () => '(=^・・^=)';
diff --git a/src/web/app/desktop/tags/post-form.tag b/src/web/app/desktop/tags/post-form.tag
index 024cd095e..6a363d67c 100644
--- a/src/web/app/desktop/tags/post-form.tag
+++ b/src/web/app/desktop/tags/post-form.tag
@@ -16,7 +16,7 @@
 	<mk-uploader ref="uploader"/>
 	<button ref="upload" title="%i18n:desktop.tags.mk-post-form.attach-media-from-local%" onclick={ selectFile }><i class="fa fa-upload"></i></button>
 	<button ref="drive" title="%i18n:desktop.tags.mk-post-form.attach-media-from-drive%" onclick={ selectFileFromDrive }><i class="fa fa-cloud"></i></button>
-	<button class="cat" title="%i18n:desktop.tags.mk-post-form.insert-the-cat%" onclick={ cat }><i class="fa fa-smile-o"></i></button>
+	<button class="kao" title="%i18n:desktop.tags.mk-post-form.insert-a-kao%" onclick={ kao }><i class="fa fa-smile-o"></i></button>
 	<button class="poll" title="%i18n:desktop.tags.mk-post-form.create-poll%" onclick={ addPoll }><i class="fa fa-pie-chart"></i></button>
 	<p class="text-count { over: refs.text.value.length > 1000 }">{ '%i18n:desktop.tags.mk-post-form.text-remain%'.replace('{}', 1000 - refs.text.value.length) }</p>
 	<button class={ wait: wait } ref="submit" disabled={ wait || (refs.text.value.length == 0 && files.length == 0 && !poll && !repost) } onclick={ post }>
@@ -258,7 +258,7 @@
 
 			[ref='upload']
 			[ref='drive']
-			.cat
+			.kao
 			.poll
 				display inline-block
 				cursor pointer
@@ -306,7 +306,7 @@
 
 	</style>
 	<script>
-		import getCat from '../../common/scripts/get-cat';
+		import getKao from '../../common/scripts/get-kao';
 		import notify from '../scripts/notify';
 		import Autocomplete from '../scripts/autocomplete';
 
@@ -500,8 +500,8 @@
 			});
 		};
 
-		this.cat = () => {
-			this.refs.text.value += getCat();
+		this.kao = () => {
+			this.refs.text.value += getKao();
 		};
 
 		this.on('update', () => {
diff --git a/src/web/app/mobile/tags/post-form.tag b/src/web/app/mobile/tags/post-form.tag
index 239473703..28c779684 100644
--- a/src/web/app/mobile/tags/post-form.tag
+++ b/src/web/app/mobile/tags/post-form.tag
@@ -23,7 +23,7 @@
 		<mk-uploader ref="uploader"/>
 		<button ref="upload" onclick={ selectFile }><i class="fa fa-upload"></i></button>
 		<button ref="drive" onclick={ selectFileFromDrive }><i class="fa fa-cloud"></i></button>
-		<button class="cat" onclick={ cat }><i class="fa fa-smile-o"></i></button>
+		<button class="kao" onclick={ kao }><i class="fa fa-smile-o"></i></button>
 		<button class="poll" onclick={ addPoll }><i class="fa fa-pie-chart"></i></button>
 		<input ref="file" type="file" accept="image/*" multiple="multiple" onchange={ changeFile }/>
 	</div>
@@ -165,7 +165,7 @@
 
 				> [ref='upload']
 				> [ref='drive']
-				.cat
+				.kao
 				.poll
 					display inline-block
 					padding 0
@@ -182,7 +182,7 @@
 
 	</style>
 	<script>
-		import getCat from '../../common/scripts/get-cat';
+		import getKao from '../../common/scripts/get-kao';
 
 		this.mixin('api');
 
@@ -285,8 +285,8 @@
 			this.unmount();
 		};
 
-		this.cat = () => {
-			this.refs.text.value += getCat();
+		this.kao = () => {
+			this.refs.text.value += getKao();
 		};
 	</script>
 </mk-post-form>

From 3e83ee9404b805f579ae136e7b1f951ab2897147 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Wed, 14 Jun 2017 01:24:11 +0900
Subject: [PATCH 65/75] Fix #551

---
 src/web/app/common/scripts/text-compiler.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/web/app/common/scripts/text-compiler.js b/src/web/app/common/scripts/text-compiler.js
index 20ed1877d..0a9b8022d 100644
--- a/src/web/app/common/scripts/text-compiler.js
+++ b/src/web/app/common/scripts/text-compiler.js
@@ -22,7 +22,7 @@ export default (tokens, shouldBreak) => {
 			case 'bold':
 				return `<strong>${escape(token.bold)}</strong>`;
 			case 'url':
-				return `<mk-url href="${escape(token.content)}" target="_blank"/>`;
+				return `<mk-url href="${escape(token.content)}" target="_blank"></mk-url>`;
 			case 'link':
 				return `<a class="link" href="${escape(token.url)}" target="_blank" title="${escape(token.url)}">${escape(token.title)}</a>`;
 			case 'mention':

From e99e2a7ac11e67229b279d27d27fdff996f94e52 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Wed, 14 Jun 2017 01:24:32 +0900
Subject: [PATCH 66/75] Fix bug

---
 src/web/app/common/scripts/{get-cao.js => get-kao.js} | 0
 1 file changed, 0 insertions(+), 0 deletions(-)
 rename src/web/app/common/scripts/{get-cao.js => get-kao.js} (100%)

diff --git a/src/web/app/common/scripts/get-cao.js b/src/web/app/common/scripts/get-kao.js
similarity index 100%
rename from src/web/app/common/scripts/get-cao.js
rename to src/web/app/common/scripts/get-kao.js

From c6ee5226608bf398c0b0511d46c9a29aaaa531bd Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Wed, 14 Jun 2017 01:26:04 +0900
Subject: [PATCH 67/75] v2097

---
 package.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/package.json b/package.json
index 8f49de707..e95c77399 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
 {
   "name": "misskey",
   "author": "syuilo <i@syuilo.com>",
-  "version": "0.0.2093",
+  "version": "0.0.2097",
   "license": "MIT",
   "description": "A miniblog-based SNS",
   "bugs": "https://github.com/syuilo/misskey/issues",

From 011742fdc9669efdc0b31a4c055c1f498b932c11 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Wed, 14 Jun 2017 01:42:20 +0900
Subject: [PATCH 68/75] [Client] Fix bug

---
 src/web/app/common/tags/messaging/room.tag | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/src/web/app/common/tags/messaging/room.tag b/src/web/app/common/tags/messaging/room.tag
index 1baf468c2..b1082e26b 100644
--- a/src/web/app/common/tags/messaging/room.tag
+++ b/src/web/app/common/tags/messaging/room.tag
@@ -126,6 +126,9 @@
 					padding 8px 0
 					text-align center
 
+					&:empty
+						display none
+
 					> p
 						display inline-block
 						margin 0

From e7a7ebc1af252dd5fcb26eaa419aec2bf05dea0b Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Wed, 14 Jun 2017 03:42:16 +0900
Subject: [PATCH 69/75] [API] Improve inteligence

---
 src/api/endpoints/users/search.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/api/endpoints/users/search.ts b/src/api/endpoints/users/search.ts
index a3f2fb796..73a5db47e 100644
--- a/src/api/endpoints/users/search.ts
+++ b/src/api/endpoints/users/search.ts
@@ -42,7 +42,7 @@ async function byNative(res, rej, me, query, offset, max) {
 	const users = await User
 		.find({
 			$or: [{
-				username_lower: new RegExp(escapedQuery.toLowerCase())
+				username_lower: new RegExp(escapedQuery.replace('@', '').toLowerCase())
 			}, {
 				name: new RegExp(escapedQuery)
 			}]

From c574b130930d835ef9c671e20d6194726d214fba Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Wed, 14 Jun 2017 04:14:26 +0900
Subject: [PATCH 70/75] Clean up

---
 src/api/stream/messaging.ts | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/api/stream/messaging.ts b/src/api/stream/messaging.ts
index f94625d8e..3f505cfaf 100644
--- a/src/api/stream/messaging.ts
+++ b/src/api/stream/messaging.ts
@@ -1,4 +1,3 @@
-import * as mongodb from 'mongodb';
 import * as websocket from 'websocket';
 import * as redis from 'redis';
 import read from '../common/read-messaging-message';

From 969cfe8a08a7fe3f2503ad96638496545513d683 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Wed, 14 Jun 2017 04:16:58 +0900
Subject: [PATCH 71/75] [Client:Desktop] Show a notification when received a
 new message

---
 src/web/app/desktop/script.js | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/src/web/app/desktop/script.js b/src/web/app/desktop/script.js
index f9f0896dd..2e8114794 100644
--- a/src/web/app/desktop/script.js
+++ b/src/web/app/desktop/script.js
@@ -74,4 +74,18 @@ function registerNotifications(stream) {
 		});
 		setTimeout(n.close.bind(n), 6000);
 	});
+
+	stream.on('unread_messaging_message', message => {
+		const n = new Notification(`${message.user.name}さんからメッセージ:`, {
+			body: message.text, // TODO: getMessagingMessageSummary(message),
+			icon: message.user.avatar_url + '?thumbnail&size=64'
+		});
+		n.onclick = () => {
+			n.close();
+			riot.mount(document.body.appendChild(document.createElement('mk-messaging-room-window')), {
+				user: message.user
+			});
+		};
+		setTimeout(n.close.bind(n), 7000);
+	});
 }

From 13ec5c2f18bb7dcbd7d691fde5df28e204c88637 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=93=E3=81=B4=E3=81=AA=E3=81=9F=E3=81=BF=E3=81=BD?=
 <Syuilotan@yahoo.co.jp>
Date: Wed, 14 Jun 2017 09:22:03 +0900
Subject: [PATCH 72/75] [Client] Fix bug

---
 src/web/app/mobile/tags/page/drive.tag | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/web/app/mobile/tags/page/drive.tag b/src/web/app/mobile/tags/page/drive.tag
index a5ebf480d..1169e3b9e 100644
--- a/src/web/app/mobile/tags/page/drive.tag
+++ b/src/web/app/mobile/tags/page/drive.tag
@@ -63,7 +63,7 @@
 
 				document.title = title;
 				// TODO: escape html characters in file.name
-				ui.trigger('title', '<mk-file-type-icon class="icon"/>' + file.name);
+				ui.trigger('title', '<mk-file-type-icon class="icon"></mk-file-type-icon>' + file.name);
 				riot.mount('mk-file-type-icon', {
 					type: file.type
 				});

From f028b99433a990a38a27b5eb960469d085d1e4d3 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Wed, 14 Jun 2017 20:55:13 +0900
Subject: [PATCH 73/75] [Client] Fix bug

---
 src/web/app/mobile/tags/drive.tag | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/web/app/mobile/tags/drive.tag b/src/web/app/mobile/tags/drive.tag
index bafa5a4fc..e19325091 100644
--- a/src/web/app/mobile/tags/drive.tag
+++ b/src/web/app/mobile/tags/drive.tag
@@ -39,7 +39,7 @@
 			</button>
 		</div>
 		<div class="empty" if={ files.length == 0 && folders.length == 0 && !fetching }>
-			<p if={ !folder == null }>%i18n:mobile.tags.mk-drive.nothing-in-drive%</p>
+			<p if={ folder == null }>%i18n:mobile.tags.mk-drive.nothing-in-drive%</p>
 			<p if={ folder != null }>%i18n:mobile.tags.mk-drive.folder-is-empty%</p>
 		</div>
 	</div>

From c287088455f7243bb3dddaa777f6ef7471167c48 Mon Sep 17 00:00:00 2001
From: "greenkeeper[bot]" <greenkeeper[bot]@users.noreply.github.com>
Date: Wed, 14 Jun 2017 12:14:26 +0000
Subject: [PATCH 74/75] chore(package): update @types/node to version 7.0.31

---
 package.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/package.json b/package.json
index e95c77399..db0989c18 100644
--- a/package.json
+++ b/package.json
@@ -52,7 +52,7 @@
     "@types/morgan": "1.7.32",
     "@types/ms": "0.7.29",
     "@types/multer": "0.0.34",
-    "@types/node": "7.0.29",
+    "@types/node": "7.0.31",
     "@types/ratelimiter": "2.1.28",
     "@types/redis": "2.6.0",
     "@types/request": "0.0.43",

From 2dbca6541631b9f93a2d57fd7c3e3a70f97f215a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=93=E3=81=B4=E3=81=AA=E3=81=9F=E3=81=BF=E3=81=BD?=
 <Syuilotan@yahoo.co.jp>
Date: Wed, 14 Jun 2017 21:17:57 +0900
Subject: [PATCH 75/75] Create README.md

---
 README.md | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/README.md b/README.md
index 2192eff51..9d2d38149 100644
--- a/README.md
+++ b/README.md
@@ -16,10 +16,10 @@ Key features
 ----------------------------------------------------------------
 * Automatically updated timeline
 * Private messages
-* Free 1GB storage
+* Free 1GB storage for each all users
 * Mobile device support (smartphone, tablet, etc)
 * Web API for third-party applications
-* Twitter integration
+* No ads
 
 and more! You can touch with your own eyes at https://misskey.xyz/.