This commit is contained in:
syuilo 2017-01-12 05:55:38 +09:00
parent 8b273e215f
commit 520299c2b4
169 changed files with 14582 additions and 14865 deletions

View file

@ -189,231 +189,6 @@ gulp.task('build:client:scripts', done => {
.transform(ls) .transform(ls)
.transform(aliasify, aliasifyConfig) .transform(aliasify, aliasifyConfig)
.transform(transformify((source, file) => {
if (file.substr(-4) !== '.tag') return source;
gutil.log('Build Tag: ' + file);
return source;
}))
// tagの{}の''を不要にする (その代わりスタイルの記法は使えなくなるけど)
.transform(transformify((source, file) => {
if (file.substr(-4) !== '.tag') return source;
const tag = new Tag(source);
const html = tag.sections.filter(s => s.name == 'html')[0];
html.lines = html.lines.map(line => {
if (line.replace(/\t/g, '')[0] === '|') {
return line;
} else {
return line.replace(/([+=])\s?\{(.+?)\}/g, '$1"{$2}"');
}
});
const styles = tag.sections.filter(s => s.name == 'style');
if (styles.length == 0) {
return tag.compile();
}
styles.forEach(style => {
let head = style.lines.shift();
head = head.replace(/([+=])\s?\{(.+?)\}/g, '$1"{$2}"');
style.lines.unshift(head);
});
return tag.compile();
}))
// tagの@hogeをref='hoge'にする
.transform(transformify((source, file) => {
if (file.substr(-4) !== '.tag') return source;
const tag = new Tag(source);
const html = tag.sections.filter(s => s.name == 'html')[0];
html.lines = html.lines.map(line => {
if (line.indexOf('@') === -1) {
return line;
} else if (line.replace(/\t/g, '')[0] === '|') {
return line;
} else {
while (line.match(/[^\s']@[a-z-]+/) !== null) {
const match = line.match(/@[a-z-]+/);
let name = match[0];
if (line[line.indexOf(name) + name.length] === '(') {
line = line.replace(name + '(', '(ref=\'' + camelCase(name.substr(1)) + '\',');
} else {
line = line.replace(name, '(ref=\'' + camelCase(name.substr(1)) + '\')');
}
}
return line;
}
});
return tag.compile();
function camelCase(str): string {
return str.replace(/-([^\s])/g, (match, group1) => {
return group1.toUpperCase();
});
}
}))
// tagのchain-caseをcamelCaseにする
.transform(transformify((source, file) => {
if (file.substr(-4) !== '.tag') return source;
const tag = new Tag(source);
const html = tag.sections.filter(s => s.name == 'html')[0];
html.lines = html.lines.map(line => {
(line.match(/\{.+?\}/g) || []).forEach(x => {
line = line.replace(x, camelCase(x));
});
return line;
});
return tag.compile();
function camelCase(str): string {
str = str.replace(/([a-z\-]+):/g, (match, group1) => {
return group1.replace(/\-/g, '###') + ':';
});
str = str.replace(/'(.+?)'/g, (match, group1) => {
return "'" + group1.replace(/\-/g, '###') + "'";
});
str = str.replace(/-([^\s0-9])/g, (match, group1) => {
return group1.toUpperCase();
});
str = str.replace(/###/g, '-');
return str;
}
}))
// tagのstyleの属性
.transform(transformify((source, file) => {
if (file.substr(-4) !== '.tag') return source;
const tag = new Tag(source);
const styles = tag.sections.filter(s => s.name == 'style');
if (styles.length == 0) {
return tag.compile();
}
styles.forEach(style => {
let head = style.lines.shift();
if (style.attr) {
style.attr = style.attr + ', type=\'stylus\', scoped';
} else {
style.attr = 'type=\'stylus\', scoped';
}
style.lines.unshift(head);
});
return tag.compile();
}))
// tagのstyleの定数
.transform(transformify((source, file) => {
if (file.substr(-4) !== '.tag') return source;
const tag = new Tag(source);
const styles = tag.sections.filter(s => s.name == 'style');
if (styles.length == 0) {
return tag.compile();
}
styles.forEach(style => {
const head = style.lines.shift();
style.lines.unshift('$theme-color = ' + config.themeColor);
style.lines.unshift('$theme-color-foreground = #fff');
style.lines.unshift(head);
});
return tag.compile();
}))
// tagのstyleを暗黙的に:scopeにする
.transform(transformify((source, file) => {
if (file.substr(-4) !== '.tag') return source;
const tag = new Tag(source);
const styles = tag.sections.filter(s => s.name == 'style');
if (styles.length == 0) {
return tag.compile();
}
styles.forEach((style, i) => {
if (i != 0) {
return;
}
const head = style.lines.shift();
style.lines = style.lines.map(line => {
return '\t' + line;
});
style.lines.unshift(':scope');
style.lines.unshift(head);
});
return tag.compile();
}))
// tagのtheme styleのパース
.transform(transformify((source, file) => {
if (file.substr(-4) !== '.tag') return source;
const tag = new Tag(source);
const styles = tag.sections.filter(s => s.name == 'style');
if (styles.length == 0) {
return tag.compile();
}
styles.forEach((style, i) => {
if (i == 0) {
return;
} else if (style.attr.substr(0, 6) != 'theme=') {
return;
}
const head = style.lines.shift();
style.lines = style.lines.map(line => {
return '\t' + line;
});
style.lines.unshift(':scope');
style.lines = style.lines.map(line => {
return '\t' + line;
});
style.lines.unshift('html[data-' + style.attr.match(/theme='(.+?)'/)[0] + ']');
style.lines.unshift(head);
});
return tag.compile();
}))
// tagのstyleおよびscriptのインデントを不要にする
.transform(transformify((source, file) => {
if (file.substr(-4) !== '.tag') return source;
const tag = new Tag(source);
tag.sections = tag.sections.map(section => {
if (section.name != 'html') {
section.indent++;
}
return section;
});
return tag.compile();
}))
// スペースでインデントされてないとエラーが出る // スペースでインデントされてないとエラーが出る
.transform(transformify((source, file) => { .transform(transformify((source, file) => {
if (file.substr(-4) !== '.tag') return source; if (file.substr(-4) !== '.tag') return source;
@ -423,6 +198,8 @@ gulp.task('build:client:scripts', done => {
.transform(transformify((source, file) => { .transform(transformify((source, file) => {
return source return source
.replace(/VERSION/g, `'${commit ? commit.hash : 'null'}'`) .replace(/VERSION/g, `'${commit ? commit.hash : 'null'}'`)
.replace(/\$theme\-color\-foreground/g, '#fff')
.replace(/\$theme\-color/g, config.themeColor)
.replace(/CONFIG\.theme-color/g, `'${config.themeColor}'`) .replace(/CONFIG\.theme-color/g, `'${config.themeColor}'`)
.replace(/CONFIG\.themeColor/g, `'${config.themeColor}'`) .replace(/CONFIG\.themeColor/g, `'${config.themeColor}'`)
.replace(/CONFIG\.api\.url/g, `'${config.scheme}://api.${config.host}'`) .replace(/CONFIG\.api\.url/g, `'${config.scheme}://api.${config.host}'`)
@ -435,7 +212,6 @@ gulp.task('build:client:scripts', done => {
})) }))
.transform(riotify, { .transform(riotify, {
template: 'pug',
type: 'livescript', type: 'livescript',
expr: false, expr: false,
compact: true, compact: true,
@ -446,17 +222,6 @@ gulp.task('build:client:scripts', done => {
} }
} }
}) })
// Riotが謎の空白を挿入する
.transform(transformify((source, file) => {
if (file.substr(-4) !== '.tag') return source;
return source.replace(/\s<mk\-ellipsis>/g, '<mk-ellipsis>');
}))
/*
// LiveScruptがHTMLクラスのショートカットを変な風に生成するのでそれを修正
.transform(transformify((source, file) => {
if (file.substr(-4) !== '.tag') return source;
return source.replace(/class="\{\(\{(.+?)\}\)\}"/g, 'class="{$1}"');
}))*/
.bundle() .bundle()
.pipe(source(entry.replace('./src/web/app/', './').replace('.ls', '.js'))); .pipe(source(entry.replace('./src/web/app/', './').replace('.ls', '.js')));
@ -531,87 +296,3 @@ gulp.task('build:client:pug', [
})) }))
.pipe(gulp.dest('./built/web/app/')); .pipe(gulp.dest('./built/web/app/'));
}); });
class Tag {
sections: {
name: string;
attr?: string;
indent: number;
lines: string[];
}[];
constructor(source) {
this.sections = [];
source = source
.replace(/\r\n/g, '\n')
.replace(/\n(\t+?)\n/g, '\n')
.replace(/\n+/g, '\n');
const html = {
name: 'html',
indent: 0,
lines: []
};
let flag = false;
source.split('\n').forEach((line, i) => {
const indent = line.lastIndexOf('\t') + 1;
if (i != 0 && indent == 0) {
flag = true;
}
if (!flag) {
source = source.replace(/^.*?\n/, '');
html.lines.push(i == 0 ? line : line.substr(1));
}
});
this.sections.push(html);
while (source != '') {
const line = source.substr(0, source.indexOf('\n'));
const root = line.match(/^\t*([a-z]+)(\.|\()?/)[1];
const beginIndent = line.lastIndexOf('\t') + 1;
flag = false;
const section = {
name: root,
attr: (line.match(/\((.+?)\)/) || [null, null])[1],
indent: beginIndent,
lines: []
};
source.split('\n').forEach((line, i) => {
const currentIndent = line.lastIndexOf('\t') + 1;
if (i != 0 && (currentIndent == beginIndent || currentIndent == 0)) {
flag = true;
}
if (!flag) {
if (i == 0 && line[line.length - 1] == '.') {
line = line.substr(0, line.length - 1);
}
if (i == 0 && line.indexOf('(') != -1) {
line = line.substr(0, line.indexOf('('));
}
source = source.replace(/^.*?\n/, '');
section.lines.push(i == 0 ? line.substr(beginIndent) : line.substr(beginIndent + 1));
}
});
this.sections.push(section);
}
}
compile(): string {
let dist = '';
this.sections.forEach((section, j) => {
dist += section.lines.map((line, i) => {
if (i == 0) {
const attr = section.attr != null ? '(' + section.attr + ')' : '';
const tail = j != 0 ? '.' : '';
return '\t'.repeat(section.indent) + line + attr + tail;
} else {
return '\t'.repeat(section.indent + 1) + line;
}
}).join('\n') + '\n';
});
return dist;
}
}

View file

@ -1,126 +1,126 @@
mk-form <mk-form>
header <header>
h1 <h1><i>{ app.name }</i>があなたの<b>アカウント</b>に<b>アクセス</b>することを<b>許可</b>しますか?</h1><img src="{ app.icon_url + '?thumbnail&amp;size=64' }"/>
i { app.name } </header>
| があなたの <div class="app">
b アカウント <section>
| に <h2>{ app.name }</h2>
b アクセス <p class="nid">{ app.name_id }</p>
| することを <p class="description">{ app.description }</p>
b 許可 </section>
| しますか? <section>
img(src={ app.icon_url + '?thumbnail&size=64' }) <h2>このアプリは次の権限を要求しています:</h2>
div.app <ul>
section <virtual each="{ p in app.permission }">
h2 { app.name } <li if="{ p == 'account-read' }">アカウントの情報を見る。</li>
p.nid { app.name_id } <li if="{ p == 'account-write' }">アカウントの情報を操作する。</li>
p.description { app.description } <li if="{ p == 'post-write' }">投稿する。</li>
section <li if="{ p == 'like-write' }">いいねしたりいいね解除する。</li>
h2 このアプリは次の権限を要求しています: <li if="{ p == 'following-write' }">フォローしたりフォロー解除する。</li>
ul <li if="{ p == 'drive-read' }">ドライブを見る。</li>
virtual(each={ p in app.permission }) <li if="{ p == 'drive-write' }">ドライブを操作する。</li>
li(if={ p == 'account-read' }) アカウントの情報を見る。 <li if="{ p == 'notification-read' }">通知を見る。</li>
li(if={ p == 'account-write' }) アカウントの情報を操作する。 <li if="{ p == 'notification-write' }">通知を操作する。</li>
li(if={ p == 'post-write' }) 投稿する。 </virtual>
li(if={ p == 'like-write' }) いいねしたりいいね解除する。 </ul>
li(if={ p == 'following-write' }) フォローしたりフォロー解除する。 </section>
li(if={ p == 'drive-read' }) ドライブを見る。 </div>
li(if={ p == 'drive-write' }) ドライブを操作する。 <div class="action">
li(if={ p == 'notification-read' }) 通知を見る。 <button onclick="{ cancel }">キャンセル</button>
li(if={ p == 'notification-write' }) 通知を操作する。 <button onclick="{ accept }">アクセスを許可</button>
</div>
<style type="stylus">
:scope
display block
div.action > header
button(onclick={ cancel }) キャンセル > h1
button(onclick={ accept }) アクセスを許可 margin 0
padding 32px 32px 20px 32px
font-size 24px
font-weight normal
color #777
style. i
display block color #77aeca
> header &:before
> h1 content '「'
margin 0
padding 32px 32px 20px 32px
font-size 24px
font-weight normal
color #777
i &:after
color #77aeca content '」'
&:before b
content '「' color #666
> img
display block
z-index 1
width 84px
height 84px
margin 0 auto -38px auto
border solid 5px #fff
border-radius 100%
box-shadow 0 2px 2px rgba(0, 0, 0, 0.1)
> .app
padding 44px 16px 0 16px
color #555
background #eee
box-shadow 0 2px 2px rgba(0, 0, 0, 0.1) inset
&:after &:after
content '」' content ''
display block
clear both
b > section
color #666 float left
width 50%
padding 8px
text-align left
> img > h2
display block margin 0
z-index 1 font-size 16px
width 84px color #777
height 84px
margin 0 auto -38px auto
border solid 5px #fff
border-radius 100%
box-shadow 0 2px 2px rgba(0, 0, 0, 0.1)
> .app > .action
padding 44px 16px 0 16px padding 16px
color #555
background #eee
box-shadow 0 2px 2px rgba(0, 0, 0, 0.1) inset
&:after > button
content '' margin 0 8px
display block
clear both
> section @media (max-width 600px)
float left > header
width 50% > img
padding 8px box-shadow none
text-align left
> h2 > .app
margin 0 box-shadow none
font-size 16px
color #777
> .action @media (max-width 500px)
padding 16px > header
> h1
font-size 16px
> button </style>
margin 0 8px <script>
@mixin \api
@media (max-width 600px) @session = @opts.session
> header @app = @session.app
> img
box-shadow none
> .app @cancel = ~>
box-shadow none @api \auth/deny do
token: @session.token
.then ~>
@trigger \denied
@media (max-width 500px) @accept = ~>
> header @api \auth/accept do
> h1 token: @session.token
font-size 16px .then ~>
@trigger \accepted
script. </script>
@mixin \api </mk-form>
@session = @opts.session
@app = @session.app
@cancel = ~>
@api \auth/deny do
token: @session.token
.then ~>
@trigger \denied
@accept = ~>
@api \auth/accept do
token: @session.token
.then ~>
@trigger \accepted

View file

@ -1,129 +1,136 @@
mk-index <mk-index>
main(if={ SIGNIN }) <main if="{ SIGNIN }">
p.fetching(if={ fetching }) <p class="fetching" if="{ fetching }">読み込み中
| 読み込み中 <mk-ellipsis></mk-ellipsis>
mk-ellipsis </p>
mk-form@form(if={ state == null && !fetching }, session={ session }) <mk-form ref="form" if="{ state == null &amp;&amp; !fetching }" session="{ session }"></mk-form>
div.denied(if={ state == 'denied' }) <div class="denied" if="{ state == 'denied' }">
h1 アプリケーションの連携をキャンセルしました。 <h1>アプリケーションの連携をキャンセルしました。</h1>
p このアプリがあなたのアカウントにアクセスすることはありません。 <p>このアプリがあなたのアカウントにアクセスすることはありません。</p>
div.accepted(if={ state == 'accepted' }) </div>
h1 { session.app.is_authorized ? 'このアプリは既に連携済みです' : 'アプリケーションの連携を許可しました'} <div class="accepted" if="{ state == 'accepted' }">
p(if={ session.app.callback_url }) <h1>{ session.app.is_authorized ? 'このアプリは既に連携済みです' : 'アプリケーションの連携を許可しました'}</h1>
| アプリケーションに戻っています <p if="{ session.app.callback_url }">アプリケーションに戻っています
mk-ellipsis <mk-ellipsis></mk-ellipsis>
p(if={ !session.app.callback_url }) アプリケーションに戻って、やっていってください。 </p>
div.error(if={ state == 'fetch-session-error' }) <p if="{ !session.app.callback_url }">アプリケーションに戻って、やっていってください。</p>
p セッションが存在しません。 </div>
main.signin(if={ !SIGNIN }) <div class="error" if="{ state == 'fetch-session-error' }">
h1 サインインしてください <p>セッションが存在しません。</p>
mk-signin </div>
footer </main>
img(src='/_/resources/auth/logo.svg', alt='Misskey') <main class="signin" if="{ !SIGNIN }">
<h1>サインインしてください</h1>
style. <mk-signin></mk-signin>
display block </main>
<footer><img src="/_/resources/auth/logo.svg" alt="Misskey"/></footer>
> main <style type="stylus">
width 100% :scope
max-width 500px
margin 0 auto
text-align center
background #fff
box-shadow 0px 4px 16px rgba(0, 0, 0, 0.2)
> .fetching
margin 0
padding 32px
color #555
> div
padding 64px
> h1
margin 0 0 8px 0
padding 0
font-size 20px
font-weight normal
> p
margin 0
color #555
&.denied > h1
color #e65050
&.accepted > h1
color #50bbe6
&.signin
padding 32px 32px 16px 32px
> h1
margin 0 0 22px 0
padding 0
font-size 20px
font-weight normal
color #555
@media (max-width 600px)
max-width none
box-shadow none
@media (max-width 500px)
> div
> h1
font-size 16px
> footer
> img
display block display block
width 64px
height 64px
margin 0 auto
script. > main
@mixin \i width 100%
@mixin \api max-width 500px
margin 0 auto
text-align center
background #fff
box-shadow 0px 4px 16px rgba(0, 0, 0, 0.2)
@state = null > .fetching
@fetching = true margin 0
padding 32px
color #555
@token = window.location.href.split \/ .pop! > div
padding 64px
@on \mount ~> > h1
if not @SIGNIN then return margin 0 0 8px 0
padding 0
font-size 20px
font-weight normal
# Fetch session > p
@api \auth/session/show do margin 0
token: @token color #555
.then (session) ~>
@session = session
@fetching = false
# 既に連携していた場合 &.denied > h1
if @session.app.is_authorized color #e65050
@api \auth/accept do
token: @session.token
.then ~>
@accepted!
else
@update!
@refs.form.on \denied ~> &.accepted > h1
@state = \denied color #50bbe6
&.signin
padding 32px 32px 16px 32px
> h1
margin 0 0 22px 0
padding 0
font-size 20px
font-weight normal
color #555
@media (max-width 600px)
max-width none
box-shadow none
@media (max-width 500px)
> div
> h1
font-size 16px
> footer
> img
display block
width 64px
height 64px
margin 0 auto
</style>
<script>
@mixin \i
@mixin \api
@state = null
@fetching = true
@token = window.location.href.split \/ .pop!
@on \mount ~>
if not @SIGNIN then return
# Fetch session
@api \auth/session/show do
token: @token
.then (session) ~>
@session = session
@fetching = false
# 既に連携していた場合
if @session.app.is_authorized
@api \auth/accept do
token: @session.token
.then ~>
@accepted!
else
@update! @update!
@refs.form.on \accepted @accepted @refs.form.on \denied ~>
@state = \denied
@update!
.catch (error) ~> @refs.form.on \accepted @accepted
@fetching = false
@state = \fetch-session-error .catch (error) ~>
@fetching = false
@state = \fetch-session-error
@update!
@accepted = ~>
@state = \accepted
@update! @update!
@accepted = ~> if @session.app.callback_url
@state = \accepted location.href = @session.app.callback_url + '?token=' + @session.token
@update! </script>
</mk-index>
if @session.app.callback_url
location.href = @session.app.callback_url + '?token=' + @session.token

View file

@ -1,5 +1,11 @@
mk-copyright <mk-copyright><span>(c) syuilo 2014-2017</span>
span (c) syuilo 2014-2017 <style type="stylus">
:scope
display block
style.
display block
</style>
</mk-copyright>

View file

@ -1,63 +1,64 @@
mk-core-error <mk-core-error>
//i: i.fa.fa-times-circle <!--i: i.fa.fa-times-circle--><img src="/_/resources/error.jpg" alt=""/>
img(src='/_/resources/error.jpg', alt='') <h1>
h1: mk-ripple-string サーバーに接続できません <mk-ripple-string>サーバーに接続できません</mk-ripple-string>
p.text </h1>
| インターネット回線に問題があるか、サーバーがダウンまたはメンテナンスしている可能性があります。しばらくしてから <p class="text">インターネット回線に問題があるか、サーバーがダウンまたはメンテナンスしている可能性があります。しばらくしてから<a onclick="{ retry }">再度お試し</a>ください。</p>
a(onclick={ retry }) 再度お試し <p class="thanks">いつもMisskeyをご利用いただきありがとうございます。</p>
| ください。 <style type="stylus">
p.thanks いつもMisskeyをご利用いただきありがとうございます。 :scope
position fixed
z-index 16385
top 0
left 0
width 100%
height 100%
text-align center
background #f8f8f8
style. > i
position fixed display block
z-index 16385 margin-top 64px
top 0 font-size 5em
left 0 color #6998a0
width 100%
height 100%
text-align center
background #f8f8f8
> i > img
display block display block
margin-top 64px height 200px
font-size 5em margin 64px auto 0 auto
color #6998a0 pointer-events none
-ms-user-select none
-moz-user-select none
-webkit-user-select none
user-select none
> img > h1
display block display block
height 200px margin 32px auto 16px auto
margin 64px auto 0 auto font-size 1.5em
pointer-events none color #555
-ms-user-select none
-moz-user-select none
-webkit-user-select none
user-select none
> h1 > .text
display block display block
margin 32px auto 16px auto margin 0 auto
font-size 1.5em max-width 600px
color #555 font-size 1em
color #666
> .text > .thanks
display block display block
margin 0 auto margin 32px auto 0 auto
max-width 600px padding 32px 0 32px 0
font-size 1em max-width 600px
color #666 font-size 0.9em
font-style oblique
color #aaa
border-top solid 1px #eee
> .thanks </style>
display block <script>
margin 32px auto 0 auto @retry = ~>
padding 32px 0 32px 0 @unmount!
max-width 600px @opts.retry!
font-size 0.9em </script>
font-style oblique </mk-core-error>
color #aaa
border-top solid 1px #eee
script.
@retry = ~>
@unmount!
@opts.retry!

View file

@ -1,25 +1,29 @@
mk-ellipsis <mk-ellipsis><span>.</span><span>.</span><span>.</span>
span . <style type="stylus">
span . :scope
span . display inline
style. > span
display inline animation ellipsis 1.4s infinite ease-in-out both
> span &:nth-child(1)
animation ellipsis 1.4s infinite ease-in-out both animation-delay 0s
&:nth-child(1) &:nth-child(2)
animation-delay 0s animation-delay 0.16s
&:nth-child(2) &:nth-child(3)
animation-delay 0.16s animation-delay 0.32s
&:nth-child(3) @keyframes ellipsis
animation-delay 0.32s 0%, 80%, 100%
opacity 1
40%
opacity 0
@keyframes ellipsis
0%, 80%, 100%
opacity 1
40%
opacity 0 </style>
</mk-ellipsis>

View file

@ -1,9 +1,11 @@
mk-file-type-icon <mk-file-type-icon><i class="fa fa-file-image-o" if="{ kind == 'image' }"></i>
i.fa.fa-file-image-o(if={ kind == 'image' }) <style type="stylus">
:scope
display inline
style. </style>
display inline <script>
@file = @opts.file
script. @kind = @file.type.split \/ .0
@file = @opts.file </script>
@kind = @file.type.split \/ .0 </mk-file-type-icon>

View file

@ -1,37 +1,44 @@
mk-forkit <mk-forkit><a href="https://github.com/syuilo/misskey" target="_blank" title="View source on Github" aria-label="View source on Github">
a(href='https://github.com/syuilo/misskey', target='_blank', title='View source on Github', aria-label='View source on Github') <svg width="80" height="80" viewBox="0 0 250 250" aria-hidden="aria-hidden">
svg(width='80', height='80', viewBox='0 0 250 250', aria-hidden) <path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path>
path(d='M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z') <path class="octo-arm" d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor"></path>
path.octo-arm(d='M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2', fill='currentColor') <path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor"></path>
path(d='M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z', fill='currentColor') </svg></a>
<style type="stylus">
style. :scope
display block
position absolute
top 0
right 0
> a
display block
> svg
display block display block
//fill #151513 position absolute
//color #fff top 0
fill $theme-color right 0
color $theme-color-foreground
.octo-arm > a
transform-origin 130px 106px display block
&:hover > svg
.octo-arm display block
animation octocat-wave 560ms ease-in-out //fill #151513
//color #fff
fill $theme-color
color $theme-color-foreground
@keyframes octocat-wave .octo-arm
0%, 100% transform-origin 130px 106px
transform rotate(0)
20%, 60% &:hover
transform rotate(-25deg) .octo-arm
40%, 80% animation octocat-wave 560ms ease-in-out
transform rotate(10deg)
@keyframes octocat-wave
0%, 100%
transform rotate(0)
20%, 60%
transform rotate(-25deg)
40%, 80%
transform rotate(10deg)
</style>
</mk-forkit>

View file

@ -1,22 +1,29 @@
mk-introduction <mk-introduction>
article <article>
h1 Misskeyとは <h1>Misskeyとは</h1><p><ruby>Misskey<rt>みすきー</rt></ruby>は、<a href="http://syuilo.com" target="_blank">syuilo</a>が2014年くらいから<a href="https://github.com/syuilo/misskey" target="_blank">オープンソースで</a>開発・運営を行っている、ミニブログベースのSNSです。</p>
<p><ruby>Misskey<rt>みすきー</rt></ruby>は、<a href="http://syuilo.com" target="_blank">syuilo</a>が2014年くらいから<a href="https://github.com/syuilo/misskey" target="_blank">オープンソースで</a>開発・運営を行っている、ミニブログベースのSNSです。</p> <p>Twitter, Facebook, LINE, Google+ などを<del>パクって</del><i>参考にして</i>います。</p>
<p>Twitter, Facebook, LINE, Google+ などを<del>パクって</del><i>参考にして</i>います。</p> <p>無料で誰でも利用でき、広告は一切掲載していません。</p>
<p>無料で誰でも利用でき、広告は一切掲載していません。</p> <p><a href="{ CONFIG.urls.about }" target="_blank">もっと知りたい方はこちら</a></p>
<p><a href={ CONFIG.urls.about } target="_blank">もっと知りたい方はこちら</a></p> </article>
<style type="stylus">
:scope
display block
style. h1
display block margin 0
text-align center
font-size 1.2em
h1 p
margin 0 margin 16px 0
text-align center
font-size 1.2em
p &:last-child
margin 16px 0 margin 0
text-align center
&:last-child
margin 0
text-align center
</style>
</mk-introduction>

View file

@ -1,15 +1,18 @@
mk-number <mk-number>
<style type="stylus">
:scope
display inline
style. </style>
display inline <script>
@on \mount ~>
# バグ? https://github.com/riot/riot/issues/2103
#value = @opts.value
value = @opts.riot-value
max = @opts.max
script. if max? then if value > max then value = max
@on \mount ~>
# バグ? https://github.com/riot/riot/issues/2103
#value = @opts.value
value = @opts.riot-value
max = @opts.max
if max? then if value > max then value = max @root.innerHTML = value.to-locale-string!
</script>
@root.innerHTML = value.to-locale-string! </mk-number>

View file

@ -1,7 +1,8 @@
mk-raw <mk-raw>
<style type="stylus">
:scope
display inline
style. </style>
display inline <script>@root.innerHTML = @opts.content</script>
</mk-raw>
script.
@root.innerHTML = @opts.content

View file

@ -1,24 +1,26 @@
mk-ripple-string <mk-ripple-string><yield/>
<yield/> <style type="stylus">
:scope
display inline
style. > span
display inline animation ripple-string 5s infinite ease-in-out both
> span @keyframes ripple-string
animation ripple-string 5s infinite ease-in-out both 0%, 50%, 100%
opacity 1
25%
opacity 0.5
@keyframes ripple-string </style>
0%, 50%, 100% <script>
opacity 1 @on \mount ~>
25% text = @root.innerHTML
opacity 0.5 @root.innerHTML = ''
(text.split '').for-each (c, i) ~>
script. ce = document.create-element \span
@on \mount ~> ce.innerHTML = c
text = @root.innerHTML ce.style.animation-delay = (i / 10) + 's'
@root.innerHTML = '' @root.append-child ce
(text.split '').for-each (c, i) ~> </script>
ce = document.create-element \span </mk-ripple-string>
ce.innerHTML = c
ce.style.animation-delay = (i / 10) + 's'
@root.append-child ce

View file

@ -1,136 +1,131 @@
mk-signin <mk-signin>
form(onsubmit={ onsubmit }, class={ signing: signing }) <form class="{ signing: signing }" onsubmit="{ onsubmit }">
label.user-name <label class="user-name">
input@username( <input ref="username" type="text" pattern="^[a-zA-Z0-9-]+$" placeholder="ユーザー名" autofocus="autofocus" required="required" oninput="{ oninput }"/><i class="fa fa-at"></i>
type='text' </label>
pattern='^[a-zA-Z0-9\-]+$' <label class="password">
placeholder='ユーザー名' <input ref="password" type="password" placeholder="パスワード" required="required"/><i class="fa fa-lock"></i>
autofocus </label>
required <button type="submit" disabled="{ signing }">{ signing ? 'やっています...' : 'サインイン' }</button>
oninput={ oninput }) </form>
i.fa.fa-at <style type="stylus">
label.password :scope
input@password(
type='password'
placeholder='パスワード'
required)
i.fa.fa-lock
button(type='submit', disabled={ signing }) { signing ? 'やっています...' : 'サインイン' }
style.
display block
> form
display block
z-index 2
&.signing
&, *
cursor wait !important
label
display block display block
margin 12px 0
i > form
display block display block
pointer-events none z-index 2
position absolute
bottom 0
top 0
left 0
z-index 1
margin auto
padding 0 16px
height 1em
color #898786
input[type=text] &.signing
input[type=password] &, *
user-select text cursor wait !important
display inline-block
cursor auto
padding 0 0 0 38px
margin 0
width 100%
line-height 44px
font-size 1em
color rgba(0, 0, 0, 0.7)
background #fff
outline none
border solid 1px #eee
border-radius 4px
&:hover label
background rgba(255, 255, 255, 0.7) display block
border-color #ddd margin 12px 0
& + i i
color #797776 display block
pointer-events none
position absolute
bottom 0
top 0
left 0
z-index 1
margin auto
padding 0 16px
height 1em
color #898786
&:focus input[type=text]
background #fff input[type=password]
border-color #ccc user-select text
display inline-block
cursor auto
padding 0 0 0 38px
margin 0
width 100%
line-height 44px
font-size 1em
color rgba(0, 0, 0, 0.7)
background #fff
outline none
border solid 1px #eee
border-radius 4px
& + i &:hover
color #797776 background rgba(255, 255, 255, 0.7)
border-color #ddd
[type=submit] & + i
cursor pointer color #797776
padding 16px
margin -6px 0 0 0
width 100%
font-size 1.2em
color rgba(0, 0, 0, 0.5)
outline none
border none
border-radius 0
background transparent
transition all .5s ease
&:hover &:focus
color $theme-color background #fff
transition all .2s ease border-color #ccc
&:focus & + i
color $theme-color color #797776
transition all .2s ease
&:active [type=submit]
color darken($theme-color, 30%) cursor pointer
transition all .2s ease padding 16px
margin -6px 0 0 0
width 100%
font-size 1.2em
color rgba(0, 0, 0, 0.5)
outline none
border none
border-radius 0
background transparent
transition all .5s ease
&:disabled &:hover
opacity 0.7 color $theme-color
transition all .2s ease
script. &:focus
@mixin \api color $theme-color
transition all .2s ease
@user = null &:active
@signing = false color darken($theme-color, 30%)
transition all .2s ease
@oninput = ~> &:disabled
@api \users/show do opacity 0.7
username: @refs.username.value
.then (user) ~> </style>
@user = user <script>
@trigger \user user @mixin \api
@user = null
@signing = false
@oninput = ~>
@api \users/show do
username: @refs.username.value
.then (user) ~>
@user = user
@trigger \user user
@update!
@onsubmit = (e) ~>
e.prevent-default!
@signing = true
@update! @update!
@onsubmit = (e) ~> @api \signin do
e.prevent-default! username: @refs.username.value
password: @refs.password.value
.then ~>
location.reload!
.catch ~>
alert 'something happened'
@signing = false
@update!
@signing = true false
@update! </script>
</mk-signin>
@api \signin do
username: @refs.username.value
password: @refs.password.value
.then ~>
location.reload!
.catch ~>
alert 'something happened'
@signing = false
@update!
false

View file

@ -1,352 +1,293 @@
mk-signup <mk-signup>
form(onsubmit={ onsubmit }, autocomplete='off') <form onsubmit="{ onsubmit }" autocomplete="off">
label.username <label class="username">
p.caption <p class="caption"><i class="fa fa-at"></i>ユーザー名</p>
i.fa.fa-at <input ref="username" type="text" pattern="^[a-zA-Z0-9-]{3,20}$" placeholder="a~z、A~Z、0~9、-" autocomplete="off" required="required" onkeyup="{ onChangeUsername }"/>
| ユーザー名 <p class="profile-page-url-preview" if="{ refs.username.value != '' &amp;&amp; username-state != 'invalidFormat' &amp;&amp; username-state != 'minRange' &amp;&amp; username-state != 'maxRange' }">{ CONFIG.url + '/' + refs.username.value }</p>
input@username( <p class="info" if="{ usernameState == 'wait' }" style="color:#999"><i class="fa fa-fw fa-spinner fa-pulse"></i>確認しています...</p>
type='text' <p class="info" if="{ usernameState == 'ok' }" style="color:#3CB7B5"><i class="fa fa-fw fa-check"></i>利用できます</p>
pattern='^[a-zA-Z0-9\-]{3,20}$' <p class="info" if="{ usernameState == 'unavailable' }" style="color:#FF1161"><i class="fa fa-fw fa-exclamation-triangle"></i>既に利用されています</p>
placeholder='a~z、A~Z、0~9、-' <p class="info" if="{ usernameState == 'error' }" style="color:#FF1161"><i class="fa fa-fw fa-exclamation-triangle"></i>通信エラー</p>
autocomplete='off' <p class="info" if="{ usernameState == 'invalid-format' }" style="color:#FF1161"><i class="fa fa-fw fa-exclamation-triangle"></i>a~z、A~Z、0~9、-(ハイフン)が使えます</p>
required <p class="info" if="{ usernameState == 'min-range' }" style="color:#FF1161"><i class="fa fa-fw fa-exclamation-triangle"></i>3文字以上でお願いします</p>
onkeyup={ on-change-username }) <p class="info" if="{ usernameState == 'max-range' }" style="color:#FF1161"><i class="fa fa-fw fa-exclamation-triangle"></i>20文字以内でお願いします</p>
</label>
p.profile-page-url-preview(if={ refs.username.value != '' && username-state != 'invalid-format' && username-state != 'min-range' && username-state != 'max-range' }) { CONFIG.url + '/' + refs.username.value } <label class="password">
<p class="caption"><i class="fa fa-lock"></i>パスワード</p>
p.info(if={ username-state == 'wait' }, style='color:#999') <input ref="password" type="password" placeholder="8文字以上を推奨します" autocomplete="off" required="required" onkeyup="{ onChangePassword }"/>
i.fa.fa-fw.fa-spinner.fa-pulse <div class="meter" if="{ passwordStrength != '' }" data-strength="{ passwordStrength }">
| 確認しています... <div class="value" ref="passwordMetar"></div>
p.info(if={ username-state == 'ok' }, style='color:#3CB7B5') </div>
i.fa.fa-fw.fa-check <p class="info" if="{ passwordStrength == 'low' }" style="color:#FF1161"><i class="fa fa-fw fa-exclamation-triangle"></i>弱いパスワード</p>
| 利用できます <p class="info" if="{ passwordStrength == 'medium' }" style="color:#3CB7B5"><i class="fa fa-fw fa-check"></i>まあまあのパスワード</p>
p.info(if={ username-state == 'unavailable' }, style='color:#FF1161') <p class="info" if="{ passwordStrength == 'high' }" style="color:#3CB7B5"><i class="fa fa-fw fa-check"></i>強いパスワード</p>
i.fa.fa-fw.fa-exclamation-triangle </label>
| 既に利用されています <label class="retype-password">
p.info(if={ username-state == 'error' }, style='color:#FF1161') <p class="caption"><i class="fa fa-lock"></i>パスワード(再入力)</p>
i.fa.fa-fw.fa-exclamation-triangle <input ref="passwordRetype" type="password" placeholder="確認のため再入力してください" autocomplete="off" required="required" onkeyup="{ onChangePasswordRetype }"/>
| 通信エラー <p class="info" if="{ passwordRetypeState == 'match' }" style="color:#3CB7B5"><i class="fa fa-fw fa-check"></i>確認されました</p>
p.info(if={ username-state == 'invalid-format' }, style='color:#FF1161') <p class="info" if="{ passwordRetypeState == 'not-match' }" style="color:#FF1161"><i class="fa fa-fw fa-exclamation-triangle"></i>一致していません</p>
i.fa.fa-fw.fa-exclamation-triangle </label>
| a~z、A~Z、0~9、-(ハイフン)が使えます <label class="recaptcha">
p.info(if={ username-state == 'min-range' }, style='color:#FF1161') <p class="caption"><i class="fa fa-toggle-on" if="{ recaptchaed }"></i><i class="fa fa-toggle-off" if="{ !recaptchaed }"></i>認証</p>
i.fa.fa-fw.fa-exclamation-triangle <div class="g-recaptcha" data-callback="onRecaptchaed" data-expired-callback="onRecaptchaExpired" data-sitekey="{ CONFIG.recaptcha.siteKey }"></div>
| 3文字以上でお願いします </label>
p.info(if={ username-state == 'max-range' }, style='color:#FF1161') <label class="agree-tou">
i.fa.fa-fw.fa-exclamation-triangle <input name="agree-tou" type="checkbox" autocomplete="off" required="required"/>
| 20文字以内でお願いします <p><a href="{ CONFIG.urls.about + '/tou' }" target="_blank">利用規約</a>に同意する</p>
</label>
label.password <button onclick="{ onsubmit }">アカウント作成</button>
p.caption </form>
i.fa.fa-lock <style type="stylus">
| パスワード :scope
input@password(
type='password'
placeholder='8文字以上を推奨します'
autocomplete='off'
required
onkeyup={ on-change-password })
div.meter(if={ password-strength != '' }, data-strength={ password-strength })
div.value@password-metar
p.info(if={ password-strength == 'low' }, style='color:#FF1161')
i.fa.fa-fw.fa-exclamation-triangle
| 弱いパスワード
p.info(if={ password-strength == 'medium' }, style='color:#3CB7B5')
i.fa.fa-fw.fa-check
| まあまあのパスワード
p.info(if={ password-strength == 'high' }, style='color:#3CB7B5')
i.fa.fa-fw.fa-check
| 強いパスワード
label.retype-password
p.caption
i.fa.fa-lock
| パスワード(再入力)
input@password-retype(
type='password'
placeholder='確認のため再入力してください'
autocomplete='off'
required
onkeyup={ on-change-password-retype })
p.info(if={ password-retype-state == 'match' }, style='color:#3CB7B5')
i.fa.fa-fw.fa-check
| 確認されました
p.info(if={ password-retype-state == 'not-match' }, style='color:#FF1161')
i.fa.fa-fw.fa-exclamation-triangle
| 一致していません
label.recaptcha
p.caption
i.fa.fa-toggle-on(if={ recaptchaed })
i.fa.fa-toggle-off(if={ !recaptchaed })
| 認証
div.g-recaptcha(
data-callback='onRecaptchaed'
data-expired-callback='onRecaptchaExpired'
data-sitekey={ CONFIG.recaptcha.site-key })
label.agree-tou
input(
name='agree-tou',
type='checkbox',
autocomplete='off',
required)
p
a(href={ CONFIG.urls.about + '/tou' }, target='_blank') 利用規約
| に同意する
button(onclick={ onsubmit })
| アカウント作成
style.
display block
min-width 302px
overflow hidden
> form
label
display block display block
margin 16px 0 min-width 302px
overflow hidden
> .caption > form
margin 0 0 4px 0
color #828888
font-size 0.95em
> i label
margin-right 0.25em
color #96adac
> .info
display block
margin 4px 0
font-size 0.8em
> i
margin-right 0.3em
&.username
.profile-page-url-preview
display block display block
margin 4px 8px 0 4px margin 16px 0
font-size 0.8em
color #888
&:empty > .caption
display none margin 0 0 4px 0
color #828888
font-size 0.95em
&:not(:empty) + .info > i
margin-top 0 margin-right 0.25em
color #96adac
&.password > .info
.meter
display block
margin-top 8px
width 100%
height 8px
&[data-strength='']
display none
&[data-strength='low']
> .value
background #d73612
&[data-strength='medium']
> .value
background #d7ca12
&[data-strength='high']
> .value
background #61bb22
> .value
display block display block
width 0% margin 4px 0
height 100% font-size 0.8em
background transparent
border-radius 4px
transition all 0.1s ease
[type=text], [type=password] > i
user-select text margin-right 0.3em
display inline-block
cursor auto
padding 0 12px
margin 0
width 100%
line-height 44px
font-size 1em
color #333 !important
background #fff !important
outline none
border solid 1px rgba(0, 0, 0, 0.1)
border-radius 4px
box-shadow 0 0 0 114514px #fff inset
transition all .3s ease
&:hover &.username
border-color rgba(0, 0, 0, 0.2) .profile-page-url-preview
transition all .1s ease display block
margin 4px 8px 0 4px
font-size 0.8em
color #888
&:focus &:empty
color $theme-color !important display none
border-color $theme-color
box-shadow 0 0 0 1024px #fff inset, 0 0 0 4px rgba($theme-color, 10%)
transition all 0s ease
&:disabled &:not(:empty) + .info
opacity 0.5 margin-top 0
.agree-tou &.password
padding 4px .meter
border-radius 4px display block
margin-top 8px
width 100%
height 8px
&:hover &[data-strength='']
background #f4f4f4 display none
&:active &[data-strength='low']
background #eee > .value
background #d73612
&, * &[data-strength='medium']
cursor pointer > .value
background #d7ca12
p &[data-strength='high']
display inline > .value
color #555 background #61bb22
button > .value
margin 0 0 32px 0 display block
padding 16px width 0%
width 100% height 100%
font-size 1em background transparent
color #fff border-radius 4px
background $theme-color transition all 0.1s ease
border-radius 3px
&:hover [type=text], [type=password]
background lighten($theme-color, 5%) user-select text
display inline-block
cursor auto
padding 0 12px
margin 0
width 100%
line-height 44px
font-size 1em
color #333 !important
background #fff !important
outline none
border solid 1px rgba(0, 0, 0, 0.1)
border-radius 4px
box-shadow 0 0 0 114514px #fff inset
transition all .3s ease
&:active &:hover
background darken($theme-color, 5%) border-color rgba(0, 0, 0, 0.2)
transition all .1s ease
script. &:focus
@mixin \api color $theme-color !important
@mixin \get-password-strength border-color $theme-color
box-shadow 0 0 0 1024px #fff inset, 0 0 0 4px rgba($theme-color, 10%)
transition all 0s ease
@username-state = null &:disabled
@password-strength = '' opacity 0.5
@password-retype-state = null
@recaptchaed = false
window.on-recaptchaed = ~> .agree-tou
@recaptchaed = true padding 4px
@update! border-radius 4px
window.on-recaptcha-expired = ~> &:hover
background #f4f4f4
&:active
background #eee
&, *
cursor pointer
p
display inline
color #555
button
margin 0 0 32px 0
padding 16px
width 100%
font-size 1em
color #fff
background $theme-color
border-radius 3px
&:hover
background lighten($theme-color, 5%)
&:active
background darken($theme-color, 5%)
</style>
<script>
@mixin \api
@mixin \get-password-strength
@username-state = null
@password-strength = ''
@password-retype-state = null
@recaptchaed = false @recaptchaed = false
@update!
@on \mount ~> window.on-recaptchaed = ~>
head = (document.get-elements-by-tag-name \head).0 @recaptchaed = true
script = document.create-element \script
..set-attribute \src \https://www.google.com/recaptcha/api.js
head.append-child script
@on-change-username = ~>
username = @refs.username.value
if username == ''
@username-state = null
@update!
return
err = switch
| not username.match /^[a-zA-Z0-9\-]+$/ => \invalid-format
| username.length < 3chars => \min-range
| username.length > 20chars => \max-range
| _ => null
if err?
@username-state = err
@update!
else
@username-state = \wait
@update! @update!
@api \username/available do window.on-recaptcha-expired = ~>
username: username @recaptchaed = false
.then (result) ~> @update!
if result.available
@username-state = \ok @on \mount ~>
else head = (document.get-elements-by-tag-name \head).0
@username-state = \unavailable script = document.create-element \script
..set-attribute \src \https://www.google.com/recaptcha/api.js
head.append-child script
@on-change-username = ~>
username = @refs.username.value
if username == ''
@username-state = null
@update! @update!
.catch (err) ~> return
@username-state = \error
err = switch
| not username.match /^[a-zA-Z0-9\-]+$/ => \invalid-format
| username.length < 3chars => \min-range
| username.length > 20chars => \max-range
| _ => null
if err?
@username-state = err
@update!
else
@username-state = \wait
@update! @update!
@on-change-password = ~> @api \username/available do
password = @refs.password.value username: username
.then (result) ~>
if result.available
@username-state = \ok
else
@username-state = \unavailable
@update!
.catch (err) ~>
@username-state = \error
@update!
if password == '' @on-change-password = ~>
@password-strength = '' password = @refs.password.value
return
strength = @get-password-strength password if password == ''
@password-strength = ''
return
if strength > 0.3 strength = @get-password-strength password
@password-strength = \medium
if strength > 0.7
@password-strength = \high
else
@password-strength = \low
@update! if strength > 0.3
@password-strength = \medium
if strength > 0.7
@password-strength = \high
else
@password-strength = \low
@refs.password-metar.style.width = (strength * 100) + \% @update!
@on-change-password-retype = ~> @refs.password-metar.style.width = (strength * 100) + \%
password = @refs.password.value
retyped-password = @refs.password-retype.value
if retyped-password == '' @on-change-password-retype = ~>
@password-retype-state = null password = @refs.password.value
return retyped-password = @refs.password-retype.value
if password == retyped-password if retyped-password == ''
@password-retype-state = \match @password-retype-state = null
else return
@password-retype-state = \not-match
@onsubmit = (e) ~> if password == retyped-password
e.prevent-default! @password-retype-state = \match
else
@password-retype-state = \not-match
username = @refs.username.value @onsubmit = (e) ~>
password = @refs.password.value e.prevent-default!
locker = document.body.append-child document.create-element \mk-locker username = @refs.username.value
password = @refs.password.value
@api \signup do locker = document.body.append-child document.create-element \mk-locker
username: username
password: password @api \signup do
'g-recaptcha-response': grecaptcha.get-response!
.then ~>
@api \signin do
username: username username: username
password: password password: password
'g-recaptcha-response': grecaptcha.get-response!
.then ~> .then ~>
location.href = CONFIG.url @api \signin do
.catch ~> username: username
alert '何らかの原因によりアカウントの作成に失敗しました。再度お試しください。' password: password
.then ~>
location.href = CONFIG.url
.catch ~>
alert '何らかの原因によりアカウントの作成に失敗しました。再度お試しください。'
grecaptcha.reset! grecaptcha.reset!
@recaptchaed = false @recaptchaed = false
locker.parent-node.remove-child locker locker.parent-node.remove-child locker
false false
</script>
</mk-signup>

View file

@ -1,24 +1,27 @@
mk-special-message <mk-special-message>
p(if={ m == 1 && d == 1 }) Happy New Year! <p if="{ m == 1 &amp;&amp; d == 1 }">Happy New Year! </p>
p(if={ m == 12 && d == 25 }) Merry Christmas! <p if="{ m == 12 &amp;&amp; d == 25 }">Merry Christmas!</p>
<style type="stylus">
:scope
display block
style. &:empty
display block display none
&:empty > p
display none margin 0
padding 4px
text-align center
font-size 14px
font-weight bold
text-transform uppercase
color #fff
background #ff1036
> p </style>
margin 0 <script>
padding 4px now = new Date!
text-align center @d = now.get-date!
font-size 14px @m = now.get-month! + 1
font-weight bold </script>
text-transform uppercase </mk-special-message>
color #fff
background #ff1036
script.
now = new Date!
@d = now.get-date!
@m = now.get-month! + 1

View file

@ -1,43 +1,41 @@
mk-time <mk-time>
time(datetime={ opts.time }) <time datetime="{ opts.time }"><span if="{ mode == 'relative' }">{ relative }</span><span if="{ mode == 'absolute' }">{ absolute }</span><span if="{ mode == 'detail' }">{ absolute } ({ relative })</span></time>
span(if={ mode == 'relative' }) { relative } <script>
span(if={ mode == 'absolute' }) { absolute } @time = new Date @opts.time
span(if={ mode == 'detail' }) { absolute } ({ relative }) @mode = @opts.mode || \relative
@tickid = null
script. @absolute =
@time = new Date @opts.time @time.get-full-year! + \年 +
@mode = @opts.mode || \relative @time.get-month! + 1 + \月 +
@tickid = null @time.get-date! + \日 +
' ' +
@time.get-hours! + \時 +
@time.get-minutes! + \分
@absolute = @on \mount ~>
@time.get-full-year! + \年 + if @mode == \relative or @mode == \detail
@time.get-month! + 1 + \月 + @tick!
@time.get-date! + \日 + @tickid = set-interval @tick, 1000ms
' ' +
@time.get-hours! + \時 +
@time.get-minutes! + \分
@on \mount ~> @on \unmount ~>
if @mode == \relative or @mode == \detail if @mode == \relative or @mode == \detail
@tick! clear-interval @tickid
@tickid = set-interval @tick, 1000ms
@on \unmount ~> @tick = ~>
if @mode == \relative or @mode == \detail now = new Date!
clear-interval @tickid ago = (now - @time) / 1000ms
@relative = switch
@tick = ~> | ago >= 31536000s => ~~(ago / 31536000s) + '年前'
now = new Date! | ago >= 2592000s => ~~(ago / 2592000s) + 'ヶ月前'
ago = (now - @time) / 1000ms | ago >= 604800s => ~~(ago / 604800s) + '週間前'
@relative = switch | ago >= 86400s => ~~(ago / 86400s) + '日前'
| ago >= 31536000s => ~~(ago / 31536000s) + '年前' | ago >= 3600s => ~~(ago / 3600s) + '時間前'
| ago >= 2592000s => ~~(ago / 2592000s) + 'ヶ月前' | ago >= 60s => ~~(ago / 60s) + '分前'
| ago >= 604800s => ~~(ago / 604800s) + '週間前' | ago >= 10s => ~~(ago % 60s) + '秒前'
| ago >= 86400s => ~~(ago / 86400s) + '日前' | ago >= 0s => 'たった今'
| ago >= 3600s => ~~(ago / 3600s) + '時間前' | ago < 0s => '未来'
| ago >= 60s => ~~(ago / 60s) + '分前' | _ => 'なぞのじかん'
| ago >= 10s => ~~(ago % 60s) + '秒前' @update!
| ago >= 0s => 'たった今' </script>
| ago < 0s => '未来' </mk-time>
| _ => 'なぞのじかん'
@update!

View file

@ -1,201 +1,195 @@
mk-uploader <mk-uploader>
ol(if={ uploads.length > 0 }) <ol if="{ uploads.length &gt; 0 }">
li(each={ uploads }) <li each="{ uploads }">
div.img(style='background-image: url({ img })') <div class="img" style="background-image: url({ img })"></div>
p.name <p class="name"><i class="fa fa-spinner fa-pulse"></i>{ name }</p>
i.fa.fa-spinner.fa-pulse <p class="status"><span class="initing" if="{ progress == undefined }">待機中
| { name } <mk-ellipsis></mk-ellipsis></span><span class="kb" if="{ progress != undefined }">{ String(Math.floor(progress.value / 1024)).replace(/(\d)(?=(\d\d\d)+(?!\d))/g, '$1,') }<i>KB</i> / { String(Math.floor(progress.max / 1024)).replace(/(\d)(?=(\d\d\d)+(?!\d))/g, '$1,') }<i>KB</i></span><span class="percentage" if="{ progress != undefined }">{ Math.floor((progress.value / progress.max) * 100) }</span></p>
p.status <progress if="{ progress != undefined &amp;&amp; progress.value != progress.max }" value="{ progress.value }" max="{ progress.max }"></progress>
span.initing(if={ progress == undefined }) <div class="progress initing" if="{ progress == undefined }"></div>
| 待機中 <div class="progress waiting" if="{ progress != undefined &amp;&amp; progress.value == progress.max }"></div>
mk-ellipsis </li>
span.kb(if={ progress != undefined }) </ol>
| { String(Math.floor(progress.value / 1024)).replace(/(\d)(?=(\d\d\d)+(?!\d))/g, '$1,') } <style type="stylus">
i KB :scope
= ' / '
| { String(Math.floor(progress.max / 1024)).replace(/(\d)(?=(\d\d\d)+(?!\d))/g, '$1,') }
i KB
span.percentage(if={ progress != undefined }) { Math.floor((progress.value / progress.max) * 100) }
progress(if={ progress != undefined && progress.value != progress.max }, value={ progress.value }, max={ progress.max })
div.progress.initing(if={ progress == undefined })
div.progress.waiting(if={ progress != undefined && progress.value == progress.max })
style.
display block
overflow auto
&:empty
display none
> ol
display block
margin 0
padding 0
list-style none
> li
display block display block
margin 8px 0 0 0 overflow auto
padding 0
height 36px
box-shadow 0 -1px 0 rgba($theme-color, 0.1)
border-top solid 8px transparent
&:first-child &:empty
margin 0 display none
box-shadow none
border-top none
> .img > ol
display block display block
position absolute
top 0
left 0
width 36px
height 36px
background-size cover
background-position center center
> .name
display block
position absolute
top 0
left 44px
margin 0 margin 0
padding 0 padding 0
max-width 256px list-style none
font-size 0.8em
color rgba($theme-color, 0.7)
white-space nowrap
text-overflow ellipsis
overflow hidden
> i > li
margin-right 4px display block
margin 8px 0 0 0
padding 0
height 36px
box-shadow 0 -1px 0 rgba($theme-color, 0.1)
border-top solid 8px transparent
> .status &:first-child
display block margin 0
position absolute box-shadow none
top 0 border-top none
right 0
margin 0
padding 0
font-size 0.8em
> .initing > .img
color rgba($theme-color, 0.5) display block
position absolute
top 0
left 0
width 36px
height 36px
background-size cover
background-position center center
> .kb > .name
color rgba($theme-color, 0.5) display block
position absolute
top 0
left 44px
margin 0
padding 0
max-width 256px
font-size 0.8em
color rgba($theme-color, 0.7)
white-space nowrap
text-overflow ellipsis
overflow hidden
> .percentage > i
display inline-block margin-right 4px
width 48px
text-align right
color rgba($theme-color, 0.7) > .status
display block
position absolute
top 0
right 0
margin 0
padding 0
font-size 0.8em
&:after > .initing
content '%' color rgba($theme-color, 0.5)
> progress > .kb
display block color rgba($theme-color, 0.5)
position absolute
bottom 0
right 0
margin 0
width calc(100% - 44px)
height 8px
background transparent
border none
border-radius 4px
overflow hidden
&::-webkit-progress-value > .percentage
background $theme-color display inline-block
width 48px
text-align right
&::-webkit-progress-bar color rgba($theme-color, 0.7)
background rgba($theme-color, 0.1)
> .progress &:after
display block content '%'
position absolute
bottom 0
right 0
margin 0
width calc(100% - 44px)
height 8px
border none
border-radius 4px
background linear-gradient(
45deg,
lighten($theme-color, 30%) 25%,
$theme-color 25%,
$theme-color 50%,
lighten($theme-color, 30%) 50%,
lighten($theme-color, 30%) 75%,
$theme-color 75%,
$theme-color
)
background-size 32px 32px
animation bg 1.5s linear infinite
&.initing > progress
opacity 0.3 display block
position absolute
bottom 0
right 0
margin 0
width calc(100% - 44px)
height 8px
background transparent
border none
border-radius 4px
overflow hidden
@keyframes bg &::-webkit-progress-value
from {background-position: 0 0;} background $theme-color
to {background-position: -64px 32px;}
script. &::-webkit-progress-bar
@mixin \i background rgba($theme-color, 0.1)
@uploads = [] > .progress
display block
position absolute
bottom 0
right 0
margin 0
width calc(100% - 44px)
height 8px
border none
border-radius 4px
background linear-gradient(
45deg,
lighten($theme-color, 30%) 25%,
$theme-color 25%,
$theme-color 50%,
lighten($theme-color, 30%) 50%,
lighten($theme-color, 30%) 75%,
$theme-color 75%,
$theme-color
)
background-size 32px 32px
animation bg 1.5s linear infinite
&.initing
opacity 0.3
@keyframes bg
from {background-position: 0 0;}
to {background-position: -64px 32px;}
</style>
<script>
@mixin \i
@uploads = []
@upload = (file, folder) ~> @upload = (file, folder) ~>
id = Math.random! id = Math.random!
ctx = ctx =
id: id id: id
name: file.name || \untitled name: file.name || \untitled
progress: undefined progress: undefined
@uploads.push ctx @uploads.push ctx
@trigger \change-uploads @uploads
@update!
reader = new FileReader!
reader.onload = (e) ~>
ctx.img = e.target.result
@update!
reader.read-as-data-URL file
data = new FormData!
data.append \i @I.token
data.append \file file
if folder?
data.append \folder_id folder
xhr = new XMLHttpRequest!
xhr.open \POST CONFIG.api.url + '/drive/files/create' true
xhr.onload = (e) ~>
drive-file = JSON.parse e.target.response
@trigger \uploaded drive-file
@uploads = @uploads.filter (x) -> x.id != id
@trigger \change-uploads @uploads @trigger \change-uploads @uploads
@update! @update!
xhr.upload.onprogress = (e) ~> reader = new FileReader!
if e.length-computable reader.onload = (e) ~>
if ctx.progress == undefined ctx.img = e.target.result
ctx.progress = {} @update!
ctx.progress.max = e.total reader.read-as-data-URL file
ctx.progress.value = e.loaded
data = new FormData!
data.append \i @I.token
data.append \file file
if folder?
data.append \folder_id folder
xhr = new XMLHttpRequest!
xhr.open \POST CONFIG.api.url + '/drive/files/create' true
xhr.onload = (e) ~>
drive-file = JSON.parse e.target.response
@trigger \uploaded drive-file
@uploads = @uploads.filter (x) -> x.id != id
@trigger \change-uploads @uploads
@update! @update!
xhr.send data xhr.upload.onprogress = (e) ~>
if e.length-computable
if ctx.progress == undefined
ctx.progress = {}
ctx.progress.max = e.total
ctx.progress.value = e.loaded
@update!
xhr.send data
</script>
</mk-uploader>

View file

@ -1,105 +1,110 @@
mk-url-preview <mk-url-preview><a href="{ url }" target="_blank" title="{ url }" if="{ !loading }">
a(href={ url }, target='_blank', title={ url }, if={ !loading }) <div class="thumbnail" if="{ thumbnail }" style="{ 'background-image: url(' + thumbnail + ')' }"></div>
div.thumbnail(if={ thumbnail }, style={ 'background-image: url(' + thumbnail + ')' }) <article>
article <header>
header: h1 { title } <h1>{ title }</h1>
p { description } </header>
footer <p>{ description }</p>
img.icon(if={ icon }, src={ icon }) <footer><img class="icon" if="{ icon }" src="{ icon }"/>
p { sitename } <p>{ sitename }</p>
</footer>
</article></a>
<style type="stylus">
:scope
display block
font-size 16px
style. > a
display block display block
font-size 16px border solid 1px #eee
border-radius 4px
overflow hidden
> a &:hover
display block text-decoration none
border solid 1px #eee border-color #ddd
border-radius 4px
overflow hidden
&:hover > article > header > h1
text-decoration none text-decoration underline
border-color #ddd
> article > header > h1 > .thumbnail
text-decoration underline position absolute
width 100px
height 100%
background-position center
background-size cover
> .thumbnail & + article
position absolute left 100px
width 100px width calc(100% - 100px)
height 100%
background-position center
background-size cover
& + article > article
left 100px padding 16px
width calc(100% - 100px)
> article > header
padding 16px margin-bottom 8px
> header > h1
margin-bottom 8px margin 0
font-size 1em
color #555
> h1 > p
margin 0 margin 0
font-size 1em color #777
color #555 font-size 0.8em
> p > footer
margin 0 margin-top 8px
color #777
font-size 0.8em
> footer > img
margin-top 8px display inline-block
width 16px
heigth 16px
margin-right 4px
vertical-align bottom
> img > p
display inline-block display inline-block
width 16px margin 0
heigth 16px color #666
margin-right 4px font-size 0.8em
vertical-align bottom line-height 16px
> p @media (max-width 500px)
display inline-block font-size 8px
margin 0
color #666
font-size 0.8em
line-height 16px
@media (max-width 500px) > a
font-size 8px border none
> a > .thumbnail
border none width 70px
> .thumbnail & + article
width 70px left 70px
width calc(100% - 70px)
& + article > article
left 70px padding 8px
width calc(100% - 70px)
> article </style>
padding 8px <script>
@mixin \api
script. @url = @opts.url
@mixin \api @loading = true
@url = @opts.url @on \mount ~>
@loading = true fetch CONFIG.url + '/api:url?url=' + @url
.then (res) ~>
info <~ res.json!.then
@title = info.title
@description = info.description
@thumbnail = info.thumbnail
@icon = info.icon
@sitename = info.sitename
@on \mount ~> @loading = false
fetch CONFIG.url + '/api:url?url=' + @url @update!
.then (res) ~> </script>
info <~ res.json!.then </mk-url-preview>
@title = info.title
@description = info.description
@thumbnail = info.thumbnail
@icon = info.icon
@sitename = info.sitename
@loading = false
@update!

View file

@ -1,50 +1,46 @@
mk-url <mk-url><a href="{ url }" target="{ opts.target }"><span class="schema">{ schema }//</span><span class="hostname">{ hostname }</span><span class="port" if="{ port != '' }">:{ port }</span><span class="pathname" if="{ pathname != '' }">{ pathname }</span><span class="query">{ query }</span><span class="hash">{ hash }</span></a>
a(href={ url }, target={ opts.target }) <style type="stylus">
span.schema { schema }// :scope
span.hostname { hostname } > a
span.port(if={ port != '' }) :{ port } &:after
span.pathname(if={ pathname != '' }) { pathname } content "\f14c"
span.query { query } display inline-block
span.hash { hash } padding-left 2px
font-family FontAwesome
font-size .9em
font-weight 400
font-style normal
style. > .schema
> a opacity 0.5
&:after
content "\f14c"
display inline-block
padding-left 2px
font-family FontAwesome
font-size .9em
font-weight 400
font-style normal
> .schema > .hostname
opacity 0.5 font-weight bold
> .hostname > .pathname
font-weight bold opacity 0.8
> .pathname > .query
opacity 0.8 opacity 0.5
> .query > .hash
opacity 0.5 font-style italic
> .hash </style>
font-style italic <script>
@url = @opts.href
script. @on \before-mount ~>
@url = @opts.href parser = document.create-element \a
parser.href = @url
@on \before-mount ~> @schema = parser.protocol
parser = document.create-element \a @hostname = parser.hostname
parser.href = @url @port = parser.port
@pathname = parser.pathname
@query = parser.search
@hash = parser.hash
@schema = parser.protocol @update!
@hostname = parser.hostname </script>
@port = parser.port </mk-url>
@pathname = parser.pathname
@query = parser.search
@hash = parser.hash
@update!

View file

@ -1,102 +1,105 @@
mk-analog-clock <mk-analog-clock>
canvas@canvas(width='256', height='256') <canvas ref="canvas" width="256" height="256"></canvas>
<style type="stylus">
:scope
> canvas
display block
width 256px
height 256px
style. </style>
> canvas <script>
display block @on \mount ~>
width 256px @draw!
height 256px @clock = set-interval @draw, 1000ms
script. @on \unmount ~>
@on \mount ~> clear-interval @clock
@draw!
@clock = set-interval @draw, 1000ms
@on \unmount ~> @draw = ~>
clear-interval @clock now = new Date!
s = now.get-seconds!
m = now.get-minutes!
h = now.get-hours!
@draw = ~> vec2 = (x, y) ->
now = new Date! @x = x
s = now.get-seconds! @y = y
m = now.get-minutes!
h = now.get-hours!
vec2 = (x, y) -> ctx = @refs.canvas.get-context \2d
@x = x canv-w = @refs.canvas.width
@y = y canv-h = @refs.canvas.height
ctx.clear-rect 0, 0, canv-w, canv-h
ctx = @refs.canvas.get-context \2d # 背景
canv-w = @refs.canvas.width center = (Math.min (canv-w / 2), (canv-h / 2))
canv-h = @refs.canvas.height line-start = center * 0.90
ctx.clear-rect 0, 0, canv-w, canv-h line-end-short = center * 0.87
line-end-long = center * 0.84
for i from 0 to 59 by 1
angle = Math.PI * i / 30
uv = new vec2 (Math.sin angle), (-Math.cos angle)
ctx.begin-path!
ctx.line-width = 1
ctx.move-to do
(canv-w / 2) + uv.x * line-start
(canv-h / 2) + uv.y * line-start
if i % 5 == 0
ctx.stroke-style = 'rgba(255, 255, 255, 0.2)'
ctx.line-to do
(canv-w / 2) + uv.x * line-end-long
(canv-h / 2) + uv.y * line-end-long
else
ctx.stroke-style = 'rgba(255, 255, 255, 0.1)'
ctx.line-to do
(canv-w / 2) + uv.x * line-end-short
(canv-h / 2) + uv.y * line-end-short
ctx.stroke!
# 背景 # 分
center = (Math.min (canv-w / 2), (canv-h / 2)) angle = Math.PI * (m + s / 60) / 30
line-start = center * 0.90 length = (Math.min canv-w, canv-h) / 2.6
line-end-short = center * 0.87
line-end-long = center * 0.84
for i from 0 to 59 by 1
angle = Math.PI * i / 30
uv = new vec2 (Math.sin angle), (-Math.cos angle) uv = new vec2 (Math.sin angle), (-Math.cos angle)
ctx.begin-path! ctx.begin-path!
ctx.line-width = 1 ctx.stroke-style = \#ffffff
ctx.line-width = 2
ctx.move-to do ctx.move-to do
(canv-w / 2) + uv.x * line-start (canv-w / 2) - uv.x * length / 5
(canv-h / 2) + uv.y * line-start (canv-h / 2) - uv.y * length / 5
if i % 5 == 0 ctx.line-to do
ctx.stroke-style = 'rgba(255, 255, 255, 0.2)' (canv-w / 2) + uv.x * length
ctx.line-to do (canv-h / 2) + uv.y * length
(canv-w / 2) + uv.x * line-end-long
(canv-h / 2) + uv.y * line-end-long
else
ctx.stroke-style = 'rgba(255, 255, 255, 0.1)'
ctx.line-to do
(canv-w / 2) + uv.x * line-end-short
(canv-h / 2) + uv.y * line-end-short
ctx.stroke! ctx.stroke!
# 分 # 時
angle = Math.PI * (m + s / 60) / 30 angle = Math.PI * (h % 12 + m / 60) / 6
length = (Math.min canv-w, canv-h) / 2.6 length = (Math.min canv-w, canv-h) / 4
uv = new vec2 (Math.sin angle), (-Math.cos angle) uv = new vec2 (Math.sin angle), (-Math.cos angle)
ctx.begin-path! ctx.begin-path!
ctx.stroke-style = \#ffffff #ctx.stroke-style = \#ffffff
ctx.line-width = 2 ctx.stroke-style = CONFIG.theme-color
ctx.move-to do ctx.line-width = 2
(canv-w / 2) - uv.x * length / 5 ctx.move-to do
(canv-h / 2) - uv.y * length / 5 (canv-w / 2) - uv.x * length / 5
ctx.line-to do (canv-h / 2) - uv.y * length / 5
(canv-w / 2) + uv.x * length ctx.line-to do
(canv-h / 2) + uv.y * length (canv-w / 2) + uv.x * length
ctx.stroke! (canv-h / 2) + uv.y * length
ctx.stroke!
# 時 # 秒
angle = Math.PI * (h % 12 + m / 60) / 6 angle = Math.PI * s / 30
length = (Math.min canv-w, canv-h) / 4 length = (Math.min canv-w, canv-h) / 2.6
uv = new vec2 (Math.sin angle), (-Math.cos angle) uv = new vec2 (Math.sin angle), (-Math.cos angle)
ctx.begin-path! ctx.begin-path!
#ctx.stroke-style = \#ffffff ctx.stroke-style = 'rgba(255, 255, 255, 0.5)'
ctx.stroke-style = CONFIG.theme-color ctx.line-width = 1
ctx.line-width = 2 ctx.move-to do
ctx.move-to do (canv-w / 2) - uv.x * length / 5
(canv-w / 2) - uv.x * length / 5 (canv-h / 2) - uv.y * length / 5
(canv-h / 2) - uv.y * length / 5 ctx.line-to do
ctx.line-to do (canv-w / 2) + uv.x * length
(canv-w / 2) + uv.x * length (canv-h / 2) + uv.y * length
(canv-h / 2) + uv.y * length ctx.stroke!
ctx.stroke! </script>
</mk-analog-clock>
# 秒
angle = Math.PI * s / 30
length = (Math.min canv-w, canv-h) / 2.6
uv = new vec2 (Math.sin angle), (-Math.cos angle)
ctx.begin-path!
ctx.stroke-style = 'rgba(255, 255, 255, 0.5)'
ctx.line-width = 1
ctx.move-to do
(canv-w / 2) - uv.x * length / 5
(canv-h / 2) - uv.y * length / 5
ctx.line-to do
(canv-w / 2) + uv.x * length
(canv-h / 2) + uv.y * length
ctx.stroke!

View file

@ -1,182 +1,183 @@
mk-autocomplete-suggestion <mk-autocomplete-suggestion>
ol.users@users(if={ users.length > 0 }) <ol class="users" ref="users" if="{ users.length &gt; 0 }">
li(each={ users }, onclick={ parent.on-click }, onkeydown={ parent.on-keydown }, tabindex='-1') <li each="{ users }" onclick="{ parent.onClick }" onkeydown="{ parent.onKeydown }" tabindex="-1"><img class="avatar" src="{ avatar_url + '?thumbnail&amp;size=32' }" alt=""/><span class="name">{ name }</span><span class="username">@{ username }</span></li>
img.avatar(src={ avatar_url + '?thumbnail&size=32' }, alt='') </ol>
span.name { name } <style type="stylus">
span.username @{ username } :scope
style.
display block
position absolute
z-index 65535
margin-top calc(1em + 8px)
overflow hidden
background #fff
border solid 1px rgba(0, 0, 0, 0.1)
border-radius 4px
> .users
display block
margin 0
padding 4px 0
max-height 190px
max-width 500px
overflow auto
list-style none
> li
display block display block
padding 4px 12px position absolute
white-space nowrap z-index 65535
margin-top calc(1em + 8px)
overflow hidden overflow hidden
font-size 0.9em background #fff
color rgba(0, 0, 0, 0.8) border solid 1px rgba(0, 0, 0, 0.1)
cursor default border-radius 4px
&, * > .users
user-select none display block
margin 0
padding 4px 0
max-height 190px
max-width 500px
overflow auto
list-style none
&:hover > li
&[data-selected='true'] display block
color #fff padding 4px 12px
background $theme-color white-space nowrap
overflow hidden
font-size 0.9em
color rgba(0, 0, 0, 0.8)
cursor default
.name &, *
color #fff user-select none
.username &:hover
color #fff &[data-selected='true']
color #fff
background $theme-color
&:active .name
color #fff color #fff
background darken($theme-color, 10%)
.name .username
color #fff color #fff
.username &:active
color #fff color #fff
background darken($theme-color, 10%)
.avatar .name
vertical-align middle color #fff
min-width 28px
min-height 28px
max-width 28px
max-height 28px
margin 0 8px 0 0
border-radius 100%
.name .username
margin 0 8px 0 0 color #fff
/*font-weight bold*/
font-weight normal
color rgba(0, 0, 0, 0.8)
.username .avatar
font-weight normal vertical-align middle
color rgba(0, 0, 0, 0.3) min-width 28px
min-height 28px
max-width 28px
max-height 28px
margin 0 8px 0 0
border-radius 100%
script. .name
@mixin \api margin 0 8px 0 0
/*font-weight bold*/
font-weight normal
color rgba(0, 0, 0, 0.8)
@q = @opts.q .username
@textarea = @opts.textarea font-weight normal
@loading = true color rgba(0, 0, 0, 0.3)
@users = []
@select = -1
@on \mount ~> </style>
@textarea.add-event-listener \keydown @on-keydown <script>
@mixin \api
all = document.query-selector-all 'body *' @q = @opts.q
Array.prototype.for-each.call all, (el) ~> @textarea = @opts.textarea
el.add-event-listener \mousedown @mousedown @loading = true
@users = []
@select = -1
@api \users/search_by_username do @on \mount ~>
query: @q @textarea.add-event-listener \keydown @on-keydown
limit: 30users
.then (users) ~>
@users = users
@loading = false
@update!
.catch (err) ~>
console.error err
@on \unmount ~> all = document.query-selector-all 'body *'
@textarea.remove-event-listener \keydown @on-keydown Array.prototype.for-each.call all, (el) ~>
el.add-event-listener \mousedown @mousedown
all = document.query-selector-all 'body *' @api \users/search_by_username do
Array.prototype.for-each.call all, (el) ~> query: @q
el.remove-event-listener \mousedown @mousedown limit: 30users
.then (users) ~>
@users = users
@loading = false
@update!
.catch (err) ~>
console.error err
@mousedown = (e) ~> @on \unmount ~>
if (!contains @root, e.target) and (@root != e.target) @textarea.remove-event-listener \keydown @on-keydown
@close!
@on-click = (e) ~> all = document.query-selector-all 'body *'
@complete e.item Array.prototype.for-each.call all, (el) ~>
el.remove-event-listener \mousedown @mousedown
@on-keydown = (e) ~> @mousedown = (e) ~>
key = e.which if (!contains @root, e.target) and (@root != e.target)
switch (key)
| 10, 13 => # Key[ENTER]
if @select != -1
e.prevent-default!
e.stop-propagation!
@complete @users[@select]
else
@close!
| 27 => # Key[ESC]
e.prevent-default!
e.stop-propagation!
@close!
| 38 => # Key[↑]
if @select != -1
e.prevent-default!
e.stop-propagation!
@select-prev!
else
@close!
| 9, 40 => # Key[TAB] or Key[↓]
e.prevent-default!
e.stop-propagation!
@select-next!
| _ =>
@close! @close!
@select-next = ~> @on-click = (e) ~>
@select++ @complete e.item
if @select >= @users.length @on-keydown = (e) ~>
@select = 0 key = e.which
switch (key)
| 10, 13 => # Key[ENTER]
if @select != -1
e.prevent-default!
e.stop-propagation!
@complete @users[@select]
else
@close!
| 27 => # Key[ESC]
e.prevent-default!
e.stop-propagation!
@close!
| 38 => # Key[↑]
if @select != -1
e.prevent-default!
e.stop-propagation!
@select-prev!
else
@close!
| 9, 40 => # Key[TAB] or Key[↓]
e.prevent-default!
e.stop-propagation!
@select-next!
| _ =>
@close!
@apply-select! @select-next = ~>
@select++
@select-prev = ~> if @select >= @users.length
@select-- @select = 0
if @select < 0 @apply-select!
@select = @users.length - 1
@apply-select! @select-prev = ~>
@select--
@apply-select = ~> if @select < 0
@refs.users.children.for-each (el) ~> @select = @users.length - 1
el.remove-attribute \data-selected
@refs.users.children[@select].set-attribute \data-selected \true @apply-select!
@refs.users.children[@select].focus!
@complete = (user) ~> @apply-select = ~>
@opts.complete user @refs.users.children.for-each (el) ~>
el.remove-attribute \data-selected
@close = ~> @refs.users.children[@select].set-attribute \data-selected \true
@opts.close! @refs.users.children[@select].focus!
function contains(parent, child) @complete = (user) ~>
node = child.parent-node @opts.complete user
while node?
if node == parent @close = ~>
return true @opts.close!
node = node.parent-node
return false function contains(parent, child)
node = child.parent-node
while node?
if node == parent
return true
node = node.parent-node
return false
</script>
</mk-autocomplete-suggestion>

View file

@ -1,134 +1,127 @@
mk-big-follow-button <mk-big-follow-button>
button(if={ !init }, class={ wait: wait, follow: !user.is_following, unfollow: user.is_following }, <button class="{ wait: wait, follow: !user.is_following, unfollow: user.is_following }" if="{ !init }" onclick="{ onclick }" disabled="{ wait }" title="{ user.is_following ? 'フォロー解除' : 'フォローする' }"><span if="{ !wait &amp;&amp; user.is_following }"><i class="fa fa-minus"></i>フォロー解除</span><span if="{ !wait &amp;&amp; !user.is_following }"><i class="fa fa-plus"></i>フォロー</span><i class="fa fa-spinner fa-pulse fa-fw" if="{ wait }"></i></button>
onclick={ onclick }, <div class="init" if="{ init }"><i class="fa fa-spinner fa-pulse fa-fw"></i></div>
disabled={ wait }, <style type="stylus">
title={ user.is_following ? 'フォロー解除' : 'フォローする' }) :scope
span(if={ !wait && user.is_following }) display block
i.fa.fa-minus
| フォロー解除
span(if={ !wait && !user.is_following })
i.fa.fa-plus
| フォロー
i.fa.fa-spinner.fa-pulse.fa-fw(if={ wait })
div.init(if={ init }): i.fa.fa-spinner.fa-pulse.fa-fw
style. > button
display block > .init
display block
cursor pointer
padding 0
margin 0
width 100%
line-height 38px
font-size 1em
outline none
border-radius 4px
> button *
> .init pointer-events none
display block
cursor pointer
padding 0
margin 0
width 100%
line-height 38px
font-size 1em
outline none
border-radius 4px
* i
pointer-events none margin-right 8px
i &:focus
margin-right 8px &:after
content ""
pointer-events none
position absolute
top -5px
right -5px
bottom -5px
left -5px
border 2px solid rgba($theme-color, 0.3)
border-radius 8px
&:focus &.follow
&:after color #888
content "" background linear-gradient(to bottom, #ffffff 0%, #f5f5f5 100%)
pointer-events none border solid 1px #e2e2e2
position absolute
top -5px
right -5px
bottom -5px
left -5px
border 2px solid rgba($theme-color, 0.3)
border-radius 8px
&.follow &:hover
color #888 background linear-gradient(to bottom, #f9f9f9 0%, #ececec 100%)
background linear-gradient(to bottom, #ffffff 0%, #f5f5f5 100%) border-color #dcdcdc
border solid 1px #e2e2e2
&:hover &:active
background linear-gradient(to bottom, #f9f9f9 0%, #ececec 100%) background #ececec
border-color #dcdcdc border-color #dcdcdc
&:active &.unfollow
background #ececec color $theme-color-foreground
border-color #dcdcdc background linear-gradient(to bottom, lighten($theme-color, 25%) 0%, lighten($theme-color, 10%) 100%)
border solid 1px lighten($theme-color, 15%)
&.unfollow &:not(:disabled)
color $theme-color-foreground font-weight bold
background linear-gradient(to bottom, lighten($theme-color, 25%) 0%, lighten($theme-color, 10%) 100%)
border solid 1px lighten($theme-color, 15%)
&:not(:disabled) &:hover:not(:disabled)
font-weight bold background linear-gradient(to bottom, lighten($theme-color, 8%) 0%, darken($theme-color, 8%) 100%)
border-color $theme-color
&:hover:not(:disabled) &:active:not(:disabled)
background linear-gradient(to bottom, lighten($theme-color, 8%) 0%, darken($theme-color, 8%) 100%) background $theme-color
border-color $theme-color border-color $theme-color
&:active:not(:disabled) &.wait
background $theme-color cursor wait !important
border-color $theme-color opacity 0.7
&.wait </style>
cursor wait !important <script>
opacity 0.7 @mixin \api
@mixin \is-promise
@mixin \stream
script. @user = null
@mixin \api @user-promise = if @is-promise @opts.user then @opts.user else Promise.resolve @opts.user
@mixin \is-promise @init = true
@mixin \stream @wait = false
@user = null @on \mount ~>
@user-promise = if @is-promise @opts.user then @opts.user else Promise.resolve @opts.user @user-promise.then (user) ~>
@init = true @user = user
@wait = false @init = false
@on \mount ~>
@user-promise.then (user) ~>
@user = user
@init = false
@update!
@stream.on \follow @on-stream-follow
@stream.on \unfollow @on-stream-unfollow
@on \unmount ~>
@stream.off \follow @on-stream-follow
@stream.off \unfollow @on-stream-unfollow
@on-stream-follow = (user) ~>
if user.id == @user.id
@user = user
@update!
@on-stream-unfollow = (user) ~>
if user.id == @user.id
@user = user
@update!
@onclick = ~>
@wait = true
if @user.is_following
@api \following/delete do
user_id: @user.id
.then ~>
@user.is_following = false
.catch (err) ->
console.error err
.then ~>
@wait = false
@update! @update!
else @stream.on \follow @on-stream-follow
@api \following/create do @stream.on \unfollow @on-stream-unfollow
user_id: @user.id
.then ~> @on \unmount ~>
@user.is_following = true @stream.off \follow @on-stream-follow
.catch (err) -> @stream.off \unfollow @on-stream-unfollow
console.error err
.then ~> @on-stream-follow = (user) ~>
@wait = false if user.id == @user.id
@user = user
@update! @update!
@on-stream-unfollow = (user) ~>
if user.id == @user.id
@user = user
@update!
@onclick = ~>
@wait = true
if @user.is_following
@api \following/delete do
user_id: @user.id
.then ~>
@user.is_following = false
.catch (err) ->
console.error err
.then ~>
@wait = false
@update!
else
@api \following/create do
user_id: @user.id
.then ~>
@user.is_following = true
.catch (err) ->
console.error err
.then ~>
@wait = false
@update!
</script>
</mk-big-follow-button>

View file

@ -1,138 +1,139 @@
mk-contextmenu <mk-contextmenu><yield />
| <yield /> <style type="stylus">
:scope
$width = 240px
$item-height = 38px
$padding = 10px
style. display none
$width = 240px position fixed
$item-height = 38px top 0
$padding = 10px left 0
z-index 4096
width $width
font-size 0.8em
background #fff
border-radius 0 4px 4px 4px
box-shadow 2px 2px 8px rgba(0, 0, 0, 0.2)
display none ul
position fixed display block
top 0 margin 0
left 0 padding $padding 0
z-index 4096 list-style none
width $width
font-size 0.8em
background #fff
border-radius 0 4px 4px 4px
box-shadow 2px 2px 8px rgba(0, 0, 0, 0.2)
ul li
display block display block
margin 0
padding $padding 0
list-style none
li &.separator
display block margin-top $padding
padding-top $padding
border-top solid 1px #eee
&.separator &.has-child
margin-top $padding > p
padding-top $padding cursor default
border-top solid 1px #eee
&.has-child > i:last-child
> p position absolute
cursor default top 0
right 8px
line-height $item-height
> i:last-child &:hover > ul
position absolute visibility visible
top 0
right 8px
line-height $item-height
&:hover > ul &:active
visibility visible > p, a
background $theme-color
&:active
> p, a > p, a
background $theme-color display block
z-index 1
margin 0
padding 0 32px 0 38px
line-height $item-height
color #868C8C
text-decoration none
cursor pointer
> p, a &:hover
display block text-decoration none
z-index 1
margin 0
padding 0 32px 0 38px
line-height $item-height
color #868C8C
text-decoration none
cursor pointer
&:hover *
text-decoration none pointer-events none
* > i
pointer-events none width 28px
margin-left -28px
text-align center
> i &:hover
width 28px > p, a
margin-left -28px text-decoration none
text-align center background $theme-color
color $theme-color-foreground
&:hover &:active
> p, a > p, a
text-decoration none text-decoration none
background $theme-color background darken($theme-color, 10%)
color $theme-color-foreground color $theme-color-foreground
&:active li > ul
> p, a visibility hidden
text-decoration none position absolute
background darken($theme-color, 10%) top 0
color $theme-color-foreground left $width
margin-top -($padding)
width $width
background #fff
border-radius 0 4px 4px 4px
box-shadow 2px 2px 8px rgba(0, 0, 0, 0.2)
transition visibility 0s linear 0.2s
li > ul </style>
visibility hidden <script>
position absolute @root.add-event-listener \contextmenu (e) ~>
top 0 e.prevent-default!
left $width
margin-top -($padding)
width $width
background #fff
border-radius 0 4px 4px 4px
box-shadow 2px 2px 8px rgba(0, 0, 0, 0.2)
transition visibility 0s linear 0.2s
script. @mousedown = (e) ~>
e.prevent-default!
if (!contains @root, e.target) and (@root != e.target)
@close!
return false
@root.add-event-listener \contextmenu (e) ~> @open = (pos) ~>
e.prevent-default! all = document.query-selector-all 'body *'
Array.prototype.for-each.call all, (el) ~>
el.add-event-listener \mousedown @mousedown
@root.style.display = \block
@root.style.left = pos.x + \px
@root.style.top = pos.y + \px
@mousedown = (e) ~> Velocity @root, \finish true
e.prevent-default! Velocity @root, { opacity: 0 } 0ms
if (!contains @root, e.target) and (@root != e.target) Velocity @root, {
@close! opacity: 1
return false } {
queue: false
duration: 100ms
easing: \linear
}
@open = (pos) ~> @close = ~>
all = document.query-selector-all 'body *' all = document.query-selector-all 'body *'
Array.prototype.for-each.call all, (el) ~> Array.prototype.for-each.call all, (el) ~>
el.add-event-listener \mousedown @mousedown el.remove-event-listener \mousedown @mousedown
@root.style.display = \block @trigger \closed
@root.style.left = pos.x + \px @unmount!
@root.style.top = pos.y + \px
Velocity @root, \finish true function contains(parent, child)
Velocity @root, { opacity: 0 } 0ms node = child.parent-node
Velocity @root, { while (node != null)
opacity: 1 if (node == parent)
} { return true
queue: false node = node.parent-node
duration: 100ms return false
easing: \linear </script>
} </mk-contextmenu>
@close = ~>
all = document.query-selector-all 'body *'
Array.prototype.for-each.call all, (el) ~>
el.remove-event-listener \mousedown @mousedown
@trigger \closed
@unmount!
function contains(parent, child)
node = child.parent-node
while (node != null)
if (node == parent)
return true
node = node.parent-node
return false

View file

@ -1,189 +1,188 @@
mk-crop-window <mk-crop-window>
mk-window@window(is-modal={ true }, width={ '800px' }) <mk-window ref="window" is-modal="{ true }" width="{ '800px' }"><yield to="header"><i class="fa fa-crop"></i>{ parent.title }</yield>
<yield to="header"> <yield to="content">
i.fa.fa-crop <div class="body"><img ref="img" src="{ parent.image.url + '?thumbnail&amp;quality=80' }" alt=""/></div>
| { parent.title } <div class="action">
</yield> <button class="skip" onclick="{ parent.skip }">クロップをスキップ</button>
<yield to="content"> <button class="cancel" onclick="{ parent.cancel }">キャンセル</button>
div.body <button class="ok" onclick="{ parent.ok }">決定</button>
img@img(src={ parent.image.url + '?thumbnail&quality=80' }, alt='') </div></yield>
div.action </mk-window>
button.skip(onclick={ parent.skip }) クロップをスキップ <style type="stylus">
button.cancel(onclick={ parent.cancel }) キャンセル :scope
button.ok(onclick={ parent.ok }) 決定 display block
</yield>
style. > mk-window
display block [data-yield='header']
> i
margin-right 4px
> mk-window [data-yield='content']
[data-yield='header']
> i
margin-right 4px
[data-yield='content'] > .body
> img
width 100%
max-height 400px
> .body .cropper-modal {
> img opacity: 0.8;
width 100% }
max-height 400px
.cropper-modal { .cropper-view-box {
opacity: 0.8; outline-color: $theme-color;
} }
.cropper-view-box { .cropper-line, .cropper-point {
outline-color: $theme-color; background-color: $theme-color;
} }
.cropper-line, .cropper-point { .cropper-bg {
background-color: $theme-color; animation: cropper-bg 0.5s linear infinite;
} }
.cropper-bg { @-webkit-keyframes cropper-bg {
animation: cropper-bg 0.5s linear infinite; 0% {
} background-position: 0 0;
}
@-webkit-keyframes cropper-bg { 100% {
0% { background-position: -8px -8px;
background-position: 0 0; }
} }
100% { @-moz-keyframes cropper-bg {
background-position: -8px -8px; 0% {
} background-position: 0 0;
} }
@-moz-keyframes cropper-bg { 100% {
0% { background-position: -8px -8px;
background-position: 0 0; }
} }
100% { @-ms-keyframes cropper-bg {
background-position: -8px -8px; 0% {
} background-position: 0 0;
} }
@-ms-keyframes cropper-bg { 100% {
0% { background-position: -8px -8px;
background-position: 0 0; }
} }
100% { @keyframes cropper-bg {
background-position: -8px -8px; 0% {
} background-position: 0 0;
} }
@keyframes cropper-bg { 100% {
0% { background-position: -8px -8px;
background-position: 0 0; }
} }
100% { > .action
background-position: -8px -8px; height 72px
} background lighten($theme-color, 95%)
}
> .action .ok
height 72px .cancel
background lighten($theme-color, 95%) .skip
display block
.ok
.cancel
.skip
display block
position absolute
bottom 16px
cursor pointer
padding 0
margin 0
height 40px
font-size 1em
outline none
border-radius 4px
&:focus
&:after
content ""
pointer-events none
position absolute position absolute
top -5px bottom 16px
right -5px cursor pointer
bottom -5px padding 0
left -5px margin 0
border 2px solid rgba($theme-color, 0.3) height 40px
border-radius 8px font-size 1em
outline none
border-radius 4px
&:disabled &:focus
opacity 0.7 &:after
cursor default content ""
pointer-events none
position absolute
top -5px
right -5px
bottom -5px
left -5px
border 2px solid rgba($theme-color, 0.3)
border-radius 8px
.ok &:disabled
.cancel opacity 0.7
width 120px cursor default
.ok .ok
right 16px .cancel
color $theme-color-foreground width 120px
background linear-gradient(to bottom, lighten($theme-color, 25%) 0%, lighten($theme-color, 10%) 100%)
border solid 1px lighten($theme-color, 15%)
&:not(:disabled) .ok
font-weight bold right 16px
color $theme-color-foreground
background linear-gradient(to bottom, lighten($theme-color, 25%) 0%, lighten($theme-color, 10%) 100%)
border solid 1px lighten($theme-color, 15%)
&:hover:not(:disabled) &:not(:disabled)
background linear-gradient(to bottom, lighten($theme-color, 8%) 0%, darken($theme-color, 8%) 100%) font-weight bold
border-color $theme-color
&:active:not(:disabled) &:hover:not(:disabled)
background $theme-color background linear-gradient(to bottom, lighten($theme-color, 8%) 0%, darken($theme-color, 8%) 100%)
border-color $theme-color border-color $theme-color
.cancel &:active:not(:disabled)
.skip background $theme-color
color #888 border-color $theme-color
background linear-gradient(to bottom, #ffffff 0%, #f5f5f5 100%)
border solid 1px #e2e2e2
&:hover .cancel
background linear-gradient(to bottom, #f9f9f9 0%, #ececec 100%) .skip
border-color #dcdcdc color #888
background linear-gradient(to bottom, #ffffff 0%, #f5f5f5 100%)
border solid 1px #e2e2e2
&:active &:hover
background #ececec background linear-gradient(to bottom, #f9f9f9 0%, #ececec 100%)
border-color #dcdcdc border-color #dcdcdc
.cancel &:active
right 148px background #ececec
border-color #dcdcdc
.skip .cancel
left 16px right 148px
width 150px
script. .skip
@mixin \cropper left 16px
width 150px
@image = @opts.file </style>
@title = @opts.title <script>
@aspect-ratio = @opts.aspect-ratio @mixin \cropper
@cropper = null
@on \mount ~> @image = @opts.file
@img = @refs.window.refs.img @title = @opts.title
@cropper = new @Cropper @img, do @aspect-ratio = @opts.aspect-ratio
aspect-ratio: @aspect-ratio @cropper = null
highlight: no
view-mode: 1
@ok = ~> @on \mount ~>
@cropper.get-cropped-canvas!.to-blob (blob) ~> @img = @refs.window.refs.img
@trigger \cropped blob @cropper = new @Cropper @img, do
aspect-ratio: @aspect-ratio
highlight: no
view-mode: 1
@ok = ~>
@cropper.get-cropped-canvas!.to-blob (blob) ~>
@trigger \cropped blob
@refs.window.close!
@skip = ~>
@trigger \skiped
@refs.window.close! @refs.window.close!
@skip = ~> @cancel = ~>
@trigger \skiped @trigger \canceled
@refs.window.close! @refs.window.close!
</script>
@cancel = ~> </mk-crop-window>
@trigger \canceled
@refs.window.close!

View file

@ -1,87 +1,90 @@
mk-debugger <mk-debugger>
mk-window@window(is-modal={ false }, width={ '700px' }, height={ '550px' }) <mk-window ref="window" is-modal="{ false }" width="{ '700px' }" height="{ '550px' }"><yield to="header"><i class="fa fa-wrench"></i>Debugger</yield>
<yield to="header"> <yield to="content">
i.fa.fa-wrench <section class="progress-dialog">
| Debugger <h1>progress-dialog</h1>
</yield> <button class="style-normal" onclick="{ parent.progressDialog }"><i class="fa fa-play"></i></button>
<yield to="content"> <button class="style-normal" onclick="{ parent.progressDialogDestroy }"><i class="fa fa-stop"></i></button>
section.progress-dialog <label>
h1 progress-dialog <p>TITLE:</p>
button.style-normal(onclick={ parent.progress-dialog }): i.fa.fa-play <input ref="progressTitle" value="Title"/>
button.style-normal(onclick={ parent.progress-dialog-destroy }): i.fa.fa-stop </label>
label <label>
p TITLE: <p>VAL:</p>
input@progress-title(value='Title') <input ref="progressValue" type="number" oninput="{ parent.progressChange }" value="0"/>
label </label>
p VAL: <label>
input@progress-value(type='number', oninput={ parent.progress-change }, value=0) <p>MAX:</p>
label <input ref="progressMax" type="number" oninput="{ parent.progressChange }" value="100"/>
p MAX: </label>
input@progress-max(type='number', oninput={ parent.progress-change }, value=100) </section></yield>
</yield> </mk-window>
<style type="stylus">
:scope
> mk-window
[data-yield='header']
> i
margin-right 4px
style. [data-yield='content']
> mk-window overflow auto
[data-yield='header']
> i
margin-right 4px
[data-yield='content'] > section
overflow auto padding 32px
> section // & + section
padding 32px // margin-top 16px
// & + section > h1
// margin-top 16px display block
margin 0
padding 0 0 8px 0
font-size 1em
color #555
border-bottom solid 1px #eee
> h1 > label
display block display block
margin 0
padding 0 0 8px 0
font-size 1em
color #555
border-bottom solid 1px #eee
> label > p
display block display inline
margin 0
> p > .progress-dialog
display inline button
margin 0 display inline-block
margin 8px
> .progress-dialog </style>
button <script>
display inline-block @mixin \open-window
margin 8px
script. @on \mount ~>
@mixin \open-window @progress-title = @tags['mk-window'].progress-title
@progress-value = @tags['mk-window'].progress-value
@progress-max = @tags['mk-window'].progress-max
@on \mount ~> @refs.window.on \closed ~>
@progress-title = @tags['mk-window'].progress-title @unmount!
@progress-value = @tags['mk-window'].progress-value
@progress-max = @tags['mk-window'].progress-max
@refs.window.on \closed ~> ################################
@unmount!
################################ @progress-controller = riot.observable!
@progress-controller = riot.observable! @progress-dialog = ~>
@open-window \mk-progress-dialog do
title: @progress-title.value
value: @progress-value.value
max: @progress-max.value
controller: @progress-controller
@progress-dialog = ~> @progress-change = ~>
@open-window \mk-progress-dialog do @progress-controller.trigger do
title: @progress-title.value \update
value: @progress-value.value @progress-value.value
max: @progress-max.value @progress-max.value
controller: @progress-controller
@progress-change = ~> @progress-dialog-destroy = ~>
@progress-controller.trigger do @progress-controller.trigger \close
\update </script>
@progress-value.value </mk-debugger>
@progress-max.value
@progress-dialog-destroy = ~>
@progress-controller.trigger \close

View file

@ -1,56 +1,60 @@
mk-detect-slow-internet-connection-notice <mk-detect-slow-internet-connection-notice><i><i class="fa fa-exclamation"></i></i>
i: i.fa.fa-exclamation <div>
div: p インターネット回線が遅いようです。 <p>インターネット回線が遅いようです。</p>
</div>
style. <style type="stylus">
display block :scope
pointer-events none
position fixed
z-index 16384
top 64px
right 16px
margin 0
padding 0
width 298px
font-size 0.9em
background #fff
box-shadow 0 1px 4px rgba(0, 0, 0, 0.25)
opacity 0
> i
display block
width 48px
line-height 48px
margin-right 0.25em
text-align center
color $theme-color-foreground
font-size 1.5em
background $theme-color
> div
display block
position absolute
top 0
left 48px
margin 0
width 250px
height 48px
color #666
> p
display block display block
pointer-events none
position fixed
z-index 16384
top 64px
right 16px
margin 0 margin 0
padding 8px padding 0
width 298px
font-size 0.9em
background #fff
box-shadow 0 1px 4px rgba(0, 0, 0, 0.25)
opacity 0
script. > i
@mixin \net display block
width 48px
line-height 48px
margin-right 0.25em
text-align center
color $theme-color-foreground
font-size 1.5em
background $theme-color
@net.on \detected-slow-network ~> > div
Velocity @root, { display block
opacity: 1 position absolute
} 200ms \linear top 0
set-timeout ~> left 48px
margin 0
width 250px
height 48px
color #666
> p
display block
margin 0
padding 8px
</style>
<script>
@mixin \net
@net.on \detected-slow-network ~>
Velocity @root, { Velocity @root, {
opacity: 0 opacity: 1
} 200ms \linear } 200ms \linear
, 10000ms set-timeout ~>
Velocity @root, {
opacity: 0
} 200ms \linear
, 10000ms
</script>
</mk-detect-slow-internet-connection-notice>

View file

@ -1,141 +1,147 @@
mk-dialog <mk-dialog>
div.bg@bg(onclick={ bg-click }) <div class="bg" ref="bg" onclick="{ bgClick }"></div>
div.main@main <div class="main" ref="main">
header@header <header ref="header"></header>
div.body@body <div class="body" ref="body"></div>
div.buttons <div class="buttons">
virtual(each={ opts.buttons }) <virtual each="{ opts.buttons }">
button(onclick={ _onclick }) { text } <button onclick="{ _onclick }">{ text }</button>
</virtual>
</div>
</div>
<style type="stylus">
:scope
display block
style. > .bg
display block display block
position fixed
z-index 8192
top 0
left 0
width 100%
height 100%
background rgba(0, 0, 0, 0.7)
opacity 0
pointer-events none
> .bg > .main
display block display block
position fixed position fixed
z-index 8192 z-index 8192
top 0 top 20%
left 0 left 0
width 100% right 0
height 100% margin 0 auto 0 auto
background rgba(0, 0, 0, 0.7) padding 32px 42px
opacity 0 width 480px
pointer-events none background #fff
> .main > header
display block margin 1em 0
position fixed
z-index 8192
top 20%
left 0
right 0
margin 0 auto 0 auto
padding 32px 42px
width 480px
background #fff
> header
margin 1em 0
color $theme-color
// color #43A4EC
font-weight bold
> i
margin-right 0.5em
> .body
margin 1em 0
color #888
> .buttons
> button
display inline-block
float right
margin 0
padding 10px 10px
font-size 1.1em
font-weight normal
text-decoration none
color #888
background transparent
outline none
border none
border-radius 0
cursor pointer
transition color 0.1s ease
i
margin 0 0.375em
&:hover
color $theme-color color $theme-color
// color #43A4EC
font-weight bold
&:active > i
color darken($theme-color, 10%) margin-right 0.5em
transition color 0s ease
script. > .body
@can-through = if opts.can-through? then opts.can-through else true margin 1em 0
@opts.buttons.for-each (button) ~> color #888
button._onclick = ~>
if button.onclick?
button.onclick!
@close!
@on \mount ~> > .buttons
@refs.header.innerHTML = @opts.title > button
@refs.body.innerHTML = @opts.text display inline-block
float right
margin 0
padding 10px 10px
font-size 1.1em
font-weight normal
text-decoration none
color #888
background transparent
outline none
border none
border-radius 0
cursor pointer
transition color 0.1s ease
@refs.bg.style.pointer-events = \auto i
Velocity @refs.bg, \finish true margin 0 0.375em
Velocity @refs.bg, {
opacity: 1
} {
queue: false
duration: 100ms
easing: \linear
}
Velocity @refs.main, { &:hover
opacity: 0 color $theme-color
scale: 1.2
} {
duration: 0
}
Velocity @refs.main, {
opacity: 1
scale: 1
} {
duration: 300ms
easing: [ 0, 0.5, 0.5, 1 ]
}
@close = ~> &:active
@refs.bg.style.pointer-events = \none color darken($theme-color, 10%)
Velocity @refs.bg, \finish true transition color 0s ease
Velocity @refs.bg, {
opacity: 0
} {
queue: false
duration: 300ms
easing: \linear
}
@refs.main.style.pointer-events = \none </style>
Velocity @refs.main, \finish true <script>
Velocity @refs.main, { @can-through = if opts.can-through? then opts.can-through else true
opacity: 0 @opts.buttons.for-each (button) ~>
scale: 0.8 button._onclick = ~>
} { if button.onclick?
queue: false button.onclick!
duration: 300ms @close!
easing: [ 0.5, -0.5, 1, 0.5 ]
complete: ~>
@unmount!
}
@bg-click = ~> @on \mount ~>
if @can-through @refs.header.innerHTML = @opts.title
if @opts.on-through? @refs.body.innerHTML = @opts.text
@opts.on-through!
@close! @refs.bg.style.pointer-events = \auto
Velocity @refs.bg, \finish true
Velocity @refs.bg, {
opacity: 1
} {
queue: false
duration: 100ms
easing: \linear
}
Velocity @refs.main, {
opacity: 0
scale: 1.2
} {
duration: 0
}
Velocity @refs.main, {
opacity: 1
scale: 1
} {
duration: 300ms
easing: [ 0, 0.5, 0.5, 1 ]
}
@close = ~>
@refs.bg.style.pointer-events = \none
Velocity @refs.bg, \finish true
Velocity @refs.bg, {
opacity: 0
} {
queue: false
duration: 300ms
easing: \linear
}
@refs.main.style.pointer-events = \none
Velocity @refs.main, \finish true
Velocity @refs.main, {
opacity: 0
scale: 0.8
} {
queue: false
duration: 300ms
easing: [ 0.5, -0.5, 1, 0.5 ]
complete: ~>
@unmount!
}
@bg-click = ~>
if @can-through
if @opts.on-through?
@opts.on-through!
@close!
</script>
</mk-dialog>

View file

@ -1,63 +1,68 @@
mk-donation <mk-donation>
button.close(onclick={ close }) 閉じる x <button class="close" onclick="{ close }">閉じる x</button>
div.message <div class="message">
p 利用者の皆さま、 <p>利用者の皆さま、</p>
p <p>
| 今日は、日本の皆さまにお知らせがあります。 今日は、日本の皆さまにお知らせがあります。
| Misskeyの援助をお願いいたします。 Misskeyの援助をお願いいたします。
| 私は独立性を守るため、一切の広告を掲載いたしません。 私は独立性を守るため、一切の広告を掲載いたしません。
| 平均で約¥1,500の寄付をいただき、運営しております。 平均で約¥1,500の寄付をいただき、運営しております。
| 援助をしてくださる利用者はほんの少数です。 援助をしてくださる利用者はほんの少数です。
| お願いいたします。 お願いいたします。
| 今日、利用者の皆さまが¥300ご援助くだされば、募金活動を一時間で終了することができます。 今日、利用者の皆さまが¥300ご援助くだされば、募金活動を一時間で終了することができます。
| コーヒー1杯ほどの金額です。 コーヒー1杯ほどの金額です。
| Misskeyを活用しておられるのでしたら、広告を掲載せずにもう1年活動できるよう、どうか1分だけお時間をください。 Misskeyを活用しておられるのでしたら、広告を掲載せずにもう1年活動できるよう、どうか1分だけお時間をください。
| 私は小さな非営利個人ですが、サーバー、プログラム、人件費など、世界でトップクラスのウェブサイト同等のコストがかかります。 私は小さな非営利個人ですが、サーバー、プログラム、人件費など、世界でトップクラスのウェブサイト同等のコストがかかります。
| 利用者は何億人といますが、他の大きなサイトに比べてほんの少額の費用で運営しているのです。 利用者は何億人といますが、他の大きなサイトに比べてほんの少額の費用で運営しているのです。
| 人間の可能性、自由、そして機会。知識こそ、これらの基盤を成すものです。 人間の可能性、自由、そして機会。知識こそ、これらの基盤を成すものです。
| 私は、誰もが無料かつ制限なく知識に触れられるべきだと信じています。 私は、誰もが無料かつ制限なく知識に触れられるべきだと信じています。
| 募金活動を終了し、Misskeyの改善に戻れるようご援助ください。 募金活動を終了し、Misskeyの改善に戻れるようご援助ください。
| よろしくお願いいたします。 よろしくお願いいたします。
</p>
style. </div>
display block <style type="stylus">
color #fff :scope
background #03072C
> .close
position absolute
top 16px
right 16px
z-index 1
> .message
padding 32px
font-size 1.4em
font-family serif
> p
display block display block
margin 0 auto color #fff
max-width 1200px background #03072C
> p:first-child > .close
margin-bottom 16px position absolute
top 16px
right 16px
z-index 1
script. > .message
@mixin \api padding 32px
@mixin \i font-size 1.4em
font-family serif
@close = (e) ~> > p
e.prevent-default! display block
e.stop-propagation! margin 0 auto
max-width 1200px
@I.data.no_donation = true > p:first-child
@api \i/appdata/set do margin-bottom 16px
data: JSON.stringify do
no_donation: @I.data.no_donation
.then ~>
@update-i!
@unmount! </style>
<script>
@mixin \api
@mixin \i
@parent.parent.set-root-layout! @close = (e) ~>
e.prevent-default!
e.stop-propagation!
@I.data.no_donation = true
@api \i/appdata/set do
data: JSON.stringify do
no_donation: @I.data.no_donation
.then ~>
@update-i!
@unmount!
@parent.parent.set-root-layout!
</script>
</mk-donation>

View file

@ -1,28 +1,31 @@
mk-drive-browser-base-contextmenu <mk-drive-browser-base-contextmenu>
mk-contextmenu@ctx <mk-contextmenu ref="ctx">
ul <ul>
li(onclick={ parent.create-folder }): p <li onclick="{ parent.createFolder }">
i.fa.fa-folder-o <p><i class="fa fa-folder-o"></i>フォルダーを作成</p>
| フォルダーを作成 </li>
li(onclick={ parent.upload }): p <li onclick="{ parent.upload }">
i.fa.fa-upload <p><i class="fa fa-upload"></i>ファイルをアップロード</p>
| ファイルをアップロード </li>
</ul>
</mk-contextmenu>
<script>
@browser = @opts.browser
script. @on \mount ~>
@browser = @opts.browser @refs.ctx.on \closed ~>
@trigger \closed
@unmount!
@on \mount ~> @open = (pos) ~>
@refs.ctx.on \closed ~> @refs.ctx.open pos
@trigger \closed
@unmount!
@open = (pos) ~> @create-folder = ~>
@refs.ctx.open pos @browser.create-folder!
@refs.ctx.close!
@create-folder = ~> @upload = ~>
@browser.create-folder! @browser.select-local-file!
@refs.ctx.close! @refs.ctx.close!
</script>
@upload = ~> </mk-drive-browser-base-contextmenu>
@browser.select-local-file!
@refs.ctx.close!

View file

@ -1,29 +1,28 @@
mk-drive-browser-window <mk-drive-browser-window>
mk-window@window(is-modal={ false }, width={ '800px' }, height={ '500px' }) <mk-window ref="window" is-modal="{ false }" width="{ '800px' }" height="{ '500px' }"><yield to="header"><i class="fa fa-cloud"></i>ドライブ</yield>
<yield to="header"> <yield to="content">
i.fa.fa-cloud <mk-drive-browser multiple="{ true }" folder="{ parent.folder }"></mk-drive-browser></yield>
| ドライブ </mk-window>
</yield> <style type="stylus">
<yield to="content"> :scope
mk-drive-browser(multiple={ true }, folder={ parent.folder }) > mk-window
</yield> [data-yield='header']
> i
margin-right 4px
style. [data-yield='content']
> mk-window > mk-drive-browser
[data-yield='header'] height 100%
> i
margin-right 4px
[data-yield='content'] </style>
> mk-drive-browser <script>
height 100% @folder = if @opts.folder? then @opts.folder else null
script. @on \mount ~>
@folder = if @opts.folder? then @opts.folder else null @refs.window.on \closed ~>
@unmount!
@on \mount ~> @close = ~>
@refs.window.on \closed ~> @refs.window.close!
@unmount! </script>
</mk-drive-browser-window>
@close = ~>
@refs.window.close!

File diff suppressed because it is too large Load diff

View file

@ -1,97 +1,103 @@
mk-drive-browser-file-contextmenu <mk-drive-browser-file-contextmenu>
mk-contextmenu@ctx: ul <mk-contextmenu ref="ctx">
li(onclick={ parent.rename }): p <ul>
i.fa.fa-i-cursor <li onclick="{ parent.rename }">
| 名前を変更 <p><i class="fa fa-i-cursor"></i>名前を変更</p>
li(onclick={ parent.copy-url }): p </li>
i.fa.fa-link <li onclick="{ parent.copyUrl }">
| URLをコピー <p><i class="fa fa-link"></i>URLをコピー</p>
li: a(href={ parent.file.url + '?download' }, download={ parent.file.name }, onclick={ parent.download }) </li>
i.fa.fa-download <li><a href="{ parent.file.url + '?download' }" download="{ parent.file.name }" onclick="{ parent.download }"><i class="fa fa-download"></i>ダウンロード</a></li>
| ダウンロード <li class="separator"></li>
li.separator <li onclick="{ parent.delete }">
li(onclick={ parent.delete }): p <p><i class="fa fa-trash-o"></i>削除</p>
i.fa.fa-trash-o </li>
| 削除 <li class="separator"></li>
li.separator <li class="has-child">
li.has-child <p>その他...<i class="fa fa-caret-right"></i></p>
p <ul>
| その他... <li onclick="{ parent.setAvatar }">
i.fa.fa-caret-right <p>アバターに設定</p>
ul </li>
li(onclick={ parent.set-avatar }): p <li onclick="{ parent.setBanner }">
| アバターに設定 <p>バナーに設定</p>
li(onclick={ parent.set-banner }): p </li>
| バナーに設定 <li onclick="{ parent.setWallpaper }">
li(onclick={ parent.set-wallpaper }): p <p>壁紙に設定</p>
| 壁紙に設定 </li>
li.has-child </ul>
p </li>
| アプリで開く... <li class="has-child">
i.fa.fa-caret-right <p>アプリで開く...<i class="fa fa-caret-right"></i></p>
ul <ul>
li(onclick={ parent.add-app }): p <li onclick="{ parent.addApp }">
| アプリを追加... <p>アプリを追加...</p>
</li>
</ul>
</li>
</ul>
</mk-contextmenu>
<script>
@mixin \api
@mixin \i
@mixin \update-avatar
@mixin \update-banner
@mixin \update-wallpaper
@mixin \input-dialog
@mixin \NotImplementedException
script. @browser = @opts.browser
@mixin \api @file = @opts.file
@mixin \i
@mixin \update-avatar
@mixin \update-banner
@mixin \update-wallpaper
@mixin \input-dialog
@mixin \NotImplementedException
@browser = @opts.browser @on \mount ~>
@file = @opts.file @refs.ctx.on \closed ~>
@trigger \closed
@unmount!
@on \mount ~> @open = (pos) ~>
@refs.ctx.on \closed ~> @refs.ctx.open pos
@trigger \closed
@unmount!
@open = (pos) ~> @rename = ~>
@refs.ctx.open pos @refs.ctx.close!
@rename = ~> name <~ @input-dialog do
@refs.ctx.close! 'ファイル名の変更'
'新しいファイル名を入力してください'
@file.name
name <~ @input-dialog do @api \drive/files/update do
'ファイル名の変更' file_id: @file.id
'新しいファイル名を入力してください' name: name
@file.name .then ~>
# something
.catch (err) ~>
console.error err
@api \drive/files/update do @copy-url = ~>
file_id: @file.id @NotImplementedException!
name: name
.then ~>
# something
.catch (err) ~>
console.error err
@copy-url = ~> @download = ~>
@NotImplementedException! @refs.ctx.close!
@download = ~> @set-avatar = ~>
@refs.ctx.close! @refs.ctx.close!
@update-avatar @I, (i) ~>
@update-i i
, @file
@set-avatar = ~> @set-banner = ~>
@refs.ctx.close! @refs.ctx.close!
@update-avatar @I, (i) ~> @update-banner @I, (i) ~>
@update-i i @update-i i
, @file , @file
@set-banner = ~> @set-wallpaper = ~>
@refs.ctx.close! @refs.ctx.close!
@update-banner @I, (i) ~> @update-wallpaper @I, (i) ~>
@update-i i @update-i i
, @file , @file
@set-wallpaper = ~> @add-app = ~>
@refs.ctx.close! @NotImplementedException!
@update-wallpaper @I, (i) ~> </script>
@update-i i </mk-drive-browser-file-contextmenu>
, @file
@add-app = ~>
@NotImplementedException!

View file

@ -1,207 +1,208 @@
mk-drive-browser-file(data-is-selected={ (file._selected || false).toString() }, data-is-contextmenu-showing={ is-contextmenu-showing.toString() }, onclick={ onclick }, oncontextmenu={ oncontextmenu }, draggable='true', ondragstart={ ondragstart }, ondragend={ ondragend }, title={ title }) <mk-drive-browser-file data-is-selected="{ (file._selected || false).toString() }" data-is-contextmenu-showing="{ isContextmenuShowing.toString() }" onclick="{ onclick }" oncontextmenu="{ oncontextmenu }" draggable="true" ondragstart="{ ondragstart }" ondragend="{ ondragend }" title="{ title }">
div.label(if={ I.avatar_id == file.id }) <div class="label" if="{ I.avatar_id == file.id }"><img src="/_/resources/label.svg"/>
img(src='/_/resources/label.svg') <p>アバター</p>
p アバター </div>
div.label(if={ I.banner_id == file.id }) <div class="label" if="{ I.banner_id == file.id }"><img src="/_/resources/label.svg"/>
img(src='/_/resources/label.svg') <p>バナー</p>
p バナー </div>
div.label(if={ I.data.wallpaper == file.id }) <div class="label" if="{ I.data.wallpaper == file.id }"><img src="/_/resources/label.svg"/>
img(src='/_/resources/label.svg') <p>壁紙</p>
p 壁紙 </div>
div.thumbnail: img(src={ file.url + '?thumbnail&size=128' }, alt='') <div class="thumbnail"><img src="{ file.url + '?thumbnail&amp;size=128' }" alt=""/></div>
p.name <p class="name"><span>{ file.name.lastIndexOf('.') != -1 ? file.name.substr(0, file.name.lastIndexOf('.')) : file.name }</span><span class="ext" if="{ file.name.lastIndexOf('.') != -1 }">{ file.name.substr(file.name.lastIndexOf('.')) }</span></p>
span { file.name.lastIndexOf('.') != -1 ? file.name.substr(0, file.name.lastIndexOf('.')) : file.name } <style type="stylus">
span.ext(if={ file.name.lastIndexOf('.') != -1 }) { file.name.substr(file.name.lastIndexOf('.')) } :scope
display block
style. margin 4px
display block padding 8px 0 0 0
margin 4px width 144px
padding 8px 0 0 0 height 180px
width 144px
height 180px
border-radius 4px
&, *
cursor pointer
&:hover
background rgba(0, 0, 0, 0.05)
> .label
&:before
&:after
background #0b65a5
&:active
background rgba(0, 0, 0, 0.1)
> .label
&:before
&:after
background #0b588c
&[data-is-selected='true']
background $theme-color
&:hover
background lighten($theme-color, 10%)
&:active
background darken($theme-color, 10%)
> .label
&:before
&:after
display none
> .name
color $theme-color-foreground
&[data-is-contextmenu-showing='true']
&:after
content ""
pointer-events none
position absolute
top -4px
right -4px
bottom -4px
left -4px
border 2px dashed rgba($theme-color, 0.3)
border-radius 4px border-radius 4px
> .label &, *
position absolute cursor pointer
top 0
left 0
pointer-events none
&:before &:hover
content "" background rgba(0, 0, 0, 0.05)
display block
position absolute
z-index 1
top 0
left 57px
width 28px
height 8px
background #0c7ac9
&:after > .label
content "" &:before
display block &:after
position absolute background #0b65a5
z-index 1
top 57px
left 0
width 8px
height 28px
background #0c7ac9
> img &:active
position absolute background rgba(0, 0, 0, 0.1)
z-index 2
top 0
left 0
> p > .label
position absolute &:before
z-index 3 &:after
top 19px background #0b588c
left -28px
width 120px
margin 0
text-align center
line-height 28px
color #fff
transform rotate(-45deg)
> .thumbnail &[data-is-selected='true']
width 128px background $theme-color
height 128px
left 8px
> img &:hover
display block background lighten($theme-color, 10%)
position absolute
top 0
left 0
right 0
bottom 0
margin auto
max-width 128px
max-height 128px
pointer-events none
> .name &:active
display block background darken($theme-color, 10%)
margin 4px 0 0 0
font-size 0.8em
text-align center
word-break break-all
color #444
overflow hidden
> .ext > .label
opacity 0.5 &:before
&:after
display none
script. > .name
@mixin \i color $theme-color-foreground
@mixin \bytes-to-size
@file = @opts.file &[data-is-contextmenu-showing='true']
@browser = @parent &:after
content ""
pointer-events none
position absolute
top -4px
right -4px
bottom -4px
left -4px
border 2px dashed rgba($theme-color, 0.3)
border-radius 4px
@title = @file.name + '\n' + @file.type + ' ' + (@bytes-to-size @file.datasize) > .label
position absolute
top 0
left 0
pointer-events none
@is-contextmenu-showing = false &:before
content ""
display block
position absolute
z-index 1
top 0
left 57px
width 28px
height 8px
background #0c7ac9
@onclick = ~> &:after
if @browser.multiple content ""
if @file._selected? display block
@file._selected = !@file._selected position absolute
z-index 1
top 57px
left 0
width 8px
height 28px
background #0c7ac9
> img
position absolute
z-index 2
top 0
left 0
> p
position absolute
z-index 3
top 19px
left -28px
width 120px
margin 0
text-align center
line-height 28px
color #fff
transform rotate(-45deg)
> .thumbnail
width 128px
height 128px
left 8px
> img
display block
position absolute
top 0
left 0
right 0
bottom 0
margin auto
max-width 128px
max-height 128px
pointer-events none
> .name
display block
margin 4px 0 0 0
font-size 0.8em
text-align center
word-break break-all
color #444
overflow hidden
> .ext
opacity 0.5
</style>
<script>
@mixin \i
@mixin \bytes-to-size
@file = @opts.file
@browser = @parent
@title = @file.name + '\n' + @file.type + ' ' + (@bytes-to-size @file.datasize)
@is-contextmenu-showing = false
@onclick = ~>
if @browser.multiple
if @file._selected?
@file._selected = !@file._selected
else
@file._selected = true
@browser.trigger \change-selection @browser.get-selection!
else else
@file._selected = true if @file._selected
@browser.trigger \change-selection @browser.get-selection! @browser.trigger \selected @file
else else
if @file._selected @browser.files.for-each (file) ~>
@browser.trigger \selected @file file._selected = false
else @file._selected = true
@browser.files.for-each (file) ~> @browser.trigger \change-selection @file
file._selected = false
@file._selected = true
@browser.trigger \change-selection @file
@oncontextmenu = (e) ~> @oncontextmenu = (e) ~>
e.prevent-default! e.prevent-default!
e.stop-immediate-propagation! e.stop-immediate-propagation!
@is-contextmenu-showing = true @is-contextmenu-showing = true
@update!
ctx = document.body.append-child document.create-element \mk-drive-browser-file-contextmenu
ctx = riot.mount ctx, do
browser: @browser
file: @file
ctx = ctx.0
ctx.open do
x: e.page-x - window.page-x-offset
y: e.page-y - window.page-y-offset
ctx.on \closed ~>
@is-contextmenu-showing = false
@update! @update!
return false ctx = document.body.append-child document.create-element \mk-drive-browser-file-contextmenu
ctx = riot.mount ctx, do
browser: @browser
file: @file
ctx = ctx.0
ctx.open do
x: e.page-x - window.page-x-offset
y: e.page-y - window.page-y-offset
ctx.on \closed ~>
@is-contextmenu-showing = false
@update!
return false
@ondragstart = (e) ~> @ondragstart = (e) ~>
e.data-transfer.effect-allowed = \move e.data-transfer.effect-allowed = \move
e.data-transfer.set-data 'text' JSON.stringify do e.data-transfer.set-data 'text' JSON.stringify do
type: \file type: \file
id: @file.id id: @file.id
file: @file file: @file
@is-dragging = true @is-dragging = true
# 親ブラウザに対して、ドラッグが開始されたフラグを立てる # 親ブラウザに対して、ドラッグが開始されたフラグを立てる
# (=あなたの子供が、ドラッグを開始しましたよ) # (=あなたの子供が、ドラッグを開始しましたよ)
@browser.is-drag-source = true @browser.is-drag-source = true
@ondragend = (e) ~> @ondragend = (e) ~>
@is-dragging = false @is-dragging = false
@browser.is-drag-source = false @browser.is-drag-source = false
</script>
</mk-drive-browser-file>

View file

@ -1,62 +1,66 @@
mk-drive-browser-folder-contextmenu <mk-drive-browser-folder-contextmenu>
mk-contextmenu@ctx: ul <mk-contextmenu ref="ctx">
li(onclick={ parent.move }): p <ul>
i.fa.fa-arrow-right <li onclick="{ parent.move }">
| このフォルダへ移動 <p><i class="fa fa-arrow-right"></i>このフォルダへ移動</p>
li(onclick={ parent.new-window }): p </li>
i.fa.fa-share-square-o <li onclick="{ parent.newWindow }">
| 新しいウィンドウで表示 <p><i class="fa fa-share-square-o"></i>新しいウィンドウで表示</p>
li.separator </li>
li(onclick={ parent.rename }): p <li class="separator"></li>
i.fa.fa-i-cursor <li onclick="{ parent.rename }">
| 名前を変更 <p><i class="fa fa-i-cursor"></i>名前を変更</p>
li.separator </li>
li(onclick={ parent.delete }): p <li class="separator"></li>
i.fa.fa-trash-o <li onclick="{ parent.delete }">
| 削除 <p><i class="fa fa-trash-o"></i>削除</p>
</li>
</ul>
</mk-contextmenu>
<script>
@mixin \api
@mixin \input-dialog
script. @browser = @opts.browser
@mixin \api @folder = @opts.folder
@mixin \input-dialog
@browser = @opts.browser @open = (pos) ~>
@folder = @opts.folder @refs.ctx.open pos
@open = (pos) ~> @refs.ctx.on \closed ~>
@refs.ctx.open pos @trigger \closed
@unmount!
@refs.ctx.on \closed ~> @move = ~>
@trigger \closed @browser.move @folder.id
@unmount! @refs.ctx.close!
@move = ~> @new-window = ~>
@browser.move @folder.id @browser.new-window @folder.id
@refs.ctx.close! @refs.ctx.close!
@new-window = ~> @create-folder = ~>
@browser.new-window @folder.id @browser.create-folder!
@refs.ctx.close! @refs.ctx.close!
@create-folder = ~> @upload = ~>
@browser.create-folder! @browser.select-lcoal-file!
@refs.ctx.close! @refs.ctx.close!
@upload = ~> @rename = ~>
@browser.select-lcoal-file! @refs.ctx.close!
@refs.ctx.close!
@rename = ~> name <~ @input-dialog do
@refs.ctx.close! 'フォルダ名の変更'
'新しいフォルダ名を入力してください'
@folder.name
name <~ @input-dialog do @api \drive/folders/update do
'フォルダ名の変更' folder_id: @folder.id
'新しいフォルダ名を入力してください' name: name
@folder.name .then ~>
# something
@api \drive/folders/update do .catch (err) ~>
folder_id: @folder.id console.error err
name: name </script>
.then ~> </mk-drive-browser-folder-contextmenu>
# something
.catch (err) ~>
console.error err

View file

@ -1,183 +1,184 @@
mk-drive-browser-folder(data-is-contextmenu-showing={ is-contextmenu-showing.toString() }, data-draghover={ draghover.toString() }, onclick={ onclick }, onmouseover={ onmouseover }, onmouseout={ onmouseout }, ondragover={ ondragover }, ondragenter={ ondragenter }, ondragleave={ ondragleave }, ondrop={ ondrop }, oncontextmenu={ oncontextmenu }, draggable='true', ondragstart={ ondragstart }, ondragend={ ondragend }, title={ title }) <mk-drive-browser-folder data-is-contextmenu-showing="{ isContextmenuShowing.toString() }" data-draghover="{ draghover.toString() }" onclick="{ onclick }" onmouseover="{ onmouseover }" onmouseout="{ onmouseout }" ondragover="{ ondragover }" ondragenter="{ ondragenter }" ondragleave="{ ondragleave }" ondrop="{ ondrop }" oncontextmenu="{ oncontextmenu }" draggable="true" ondragstart="{ ondragstart }" ondragend="{ ondragend }" title="{ title }">
p.name <p class="name"><i class="fa fa-fw { fa-folder-o: !hover, fa-folder-open-o: hover }"></i>{ folder.name }</p>
i.fa.fa-fw(class={ fa-folder-o: !hover, fa-folder-open-o: hover }) <style type="stylus">
| { folder.name } :scope
display block
style. margin 4px
display block padding 8px
margin 4px width 144px
padding 8px height 64px
width 144px background lighten($theme-color, 95%)
height 64px
background lighten($theme-color, 95%)
border-radius 4px
&, *
cursor pointer
*
pointer-events none
&:hover
background lighten($theme-color, 90%)
&:active
background lighten($theme-color, 85%)
&[data-is-contextmenu-showing='true']
&[data-draghover='true']
&:after
content ""
pointer-events none
position absolute
top -4px
right -4px
bottom -4px
left -4px
border 2px dashed rgba($theme-color, 0.3)
border-radius 4px border-radius 4px
&[data-draghover='true'] &, *
background lighten($theme-color, 90%) cursor pointer
> .name *
margin 0 pointer-events none
font-size 0.9em
color darken($theme-color, 30%)
> i &:hover
margin-right 4px background lighten($theme-color, 90%)
margin-left 2px
text-align left
script. &:active
@mixin \api background lighten($theme-color, 85%)
@mixin \dialog
@folder = @opts.folder &[data-is-contextmenu-showing='true']
@browser = @parent &[data-draghover='true']
&:after
content ""
pointer-events none
position absolute
top -4px
right -4px
bottom -4px
left -4px
border 2px dashed rgba($theme-color, 0.3)
border-radius 4px
@title = @folder.name &[data-draghover='true']
@hover = false background lighten($theme-color, 90%)
@draghover = false
@is-contextmenu-showing = false
@onclick = ~> > .name
@browser.move @folder margin 0
font-size 0.9em
color darken($theme-color, 30%)
@onmouseover = ~> > i
@hover = true margin-right 4px
margin-left 2px
text-align left
@onmouseout = ~> </style>
<script>
@mixin \api
@mixin \dialog
@folder = @opts.folder
@browser = @parent
@title = @folder.name
@hover = false @hover = false
@draghover = false
@is-contextmenu-showing = false
@ondragover = (e) ~> @onclick = ~>
e.prevent-default! @browser.move @folder
e.stop-propagation!
# 自分自身がドラッグされていない場合 @onmouseover = ~>
if !@is-dragging @hover = true
# ドラッグされてきたものがファイルだったら
if e.data-transfer.effect-allowed == \all @onmouseout = ~>
e.data-transfer.drop-effect = \copy @hover = false
@ondragover = (e) ~>
e.prevent-default!
e.stop-propagation!
# 自分自身がドラッグされていない場合
if !@is-dragging
# ドラッグされてきたものがファイルだったら
if e.data-transfer.effect-allowed == \all
e.data-transfer.drop-effect = \copy
else
e.data-transfer.drop-effect = \move
else else
e.data-transfer.drop-effect = \move # 自分自身にはドロップさせない
else e.data-transfer.drop-effect = \none
# 自分自身にはドロップさせない
e.data-transfer.drop-effect = \none
return false
@ondragenter = ~>
if !@is-dragging
@draghover = true
@ondragleave = ~>
@draghover = false
@ondrop = (e) ~>
e.stop-propagation!
@draghover = false
# ファイルだったら
if e.data-transfer.files.length > 0
Array.prototype.for-each.call e.data-transfer.files, (file) ~>
@browser.upload file, @folder
return false return false
# データ取得 @ondragenter = ~>
data = e.data-transfer.get-data 'text' if !@is-dragging
if !data? @draghover = true
return false
# パース @ondragleave = ~>
obj = JSON.parse data @draghover = false
# (ドライブの)ファイルだったら @ondrop = (e) ~>
if obj.type == \file e.stop-propagation!
file = obj.id @draghover = false
@browser.remove-file file
@api \drive/files/update do
file_id: file
folder_id: @folder.id
.then ~>
# something
.catch (err, text-status) ~>
console.error err
# (ドライブの)フォルダーだったら # ファイルだったら
else if obj.type == \folder if e.data-transfer.files.length > 0
folder = obj.id Array.prototype.for-each.call e.data-transfer.files, (file) ~>
# 移動先が自分自身ならreject @browser.upload file, @folder
if folder == @folder.id
return false return false
@browser.remove-folder folder
@api \drive/folders/update do
folder_id: folder
parent_id: @folder.id
.then ~>
# something
.catch (err) ~>
if err == 'detected-circular-definition'
@dialog do
'<i class="fa fa-exclamation-triangle"></i>操作を完了できません'
'移動先のフォルダーは、移動するフォルダーのサブフォルダーです。'
[
text: \OK
]
return false # データ取得
data = e.data-transfer.get-data 'text'
if !data?
return false
@ondragstart = (e) ~> # パース
e.data-transfer.effect-allowed = \move obj = JSON.parse data
e.data-transfer.set-data 'text' JSON.stringify do
type: \folder
id: @folder.id
@is-dragging = true
# 親ブラウザに対して、ドラッグが開始されたフラグを立てる # (ドライブの)ファイルだったら
# (=あなたの子供が、ドラッグを開始しましたよ) if obj.type == \file
@browser.is-drag-source = true file = obj.id
@browser.remove-file file
@api \drive/files/update do
file_id: file
folder_id: @folder.id
.then ~>
# something
.catch (err, text-status) ~>
console.error err
@ondragend = (e) ~> # (ドライブの)フォルダーだったら
@is-dragging = false else if obj.type == \folder
@browser.is-drag-source = false folder = obj.id
# 移動先が自分自身ならreject
if folder == @folder.id
return false
@browser.remove-folder folder
@api \drive/folders/update do
folder_id: folder
parent_id: @folder.id
.then ~>
# something
.catch (err) ~>
if err == 'detected-circular-definition'
@dialog do
'<i class="fa fa-exclamation-triangle"></i>操作を完了できません'
'移動先のフォルダーは、移動するフォルダーのサブフォルダーです。'
[
text: \OK
]
@oncontextmenu = (e) ~> return false
e.prevent-default!
e.stop-immediate-propagation!
@is-contextmenu-showing = true @ondragstart = (e) ~>
@update! e.data-transfer.effect-allowed = \move
ctx = document.body.append-child document.create-element \mk-drive-browser-folder-contextmenu e.data-transfer.set-data 'text' JSON.stringify do
ctx = riot.mount ctx, do type: \folder
browser: @browser id: @folder.id
folder: @folder @is-dragging = true
ctx = ctx.0
ctx.open do # 親ブラウザに対して、ドラッグが開始されたフラグを立てる
x: e.page-x - window.page-x-offset # (=あなたの子供が、ドラッグを開始しましたよ)
y: e.page-y - window.page-y-offset @browser.is-drag-source = true
ctx.on \closed ~>
@is-contextmenu-showing = false @ondragend = (e) ~>
@is-dragging = false
@browser.is-drag-source = false
@oncontextmenu = (e) ~>
e.prevent-default!
e.stop-immediate-propagation!
@is-contextmenu-showing = true
@update! @update!
ctx = document.body.append-child document.create-element \mk-drive-browser-folder-contextmenu
ctx = riot.mount ctx, do
browser: @browser
folder: @folder
ctx = ctx.0
ctx.open do
x: e.page-x - window.page-x-offset
y: e.page-y - window.page-y-offset
ctx.on \closed ~>
@is-contextmenu-showing = false
@update!
return false return false
</script>
</mk-drive-browser-folder>

View file

@ -1,96 +1,97 @@
mk-drive-browser-nav-folder(data-draghover={ draghover }, onclick={ onclick }, ondragover={ ondragover }, ondragenter={ ondragenter }, ondragleave={ ondragleave }, ondrop={ ondrop }) <mk-drive-browser-nav-folder data-draghover="{ draghover }" onclick="{ onclick }" ondragover="{ ondragover }" ondragenter="{ ondragenter }" ondragleave="{ ondragleave }" ondrop="{ ondrop }"><i class="fa fa-cloud" if="{ folder == null }"></i><span>{ folder == null ? 'ドライブ' : folder.name }</span>
i.fa.fa-cloud(if={ folder == null }) <style type="stylus">
span { folder == null ? 'ドライブ' : folder.name } :scope
&[data-draghover]
background #eee
style. </style>
&[data-draghover] <script>
background #eee @mixin \api
script. # Riotのバグでnullを渡しても""になる
@mixin \api # https://github.com/riot/riot/issues/2080
#@folder = @opts.folder
@folder = if @opts.folder? and @opts.folder != '' then @opts.folder else null
@browser = @parent
# Riotのバグでnullを渡しても""になる
# https://github.com/riot/riot/issues/2080
#@folder = @opts.folder
@folder = if @opts.folder? and @opts.folder != '' then @opts.folder else null
@browser = @parent
@hover = false
@onclick = ~>
@browser.move @folder
@onmouseover = ~>
@hover = true
@onmouseout = ~>
@hover = false @hover = false
@ondragover = (e) ~> @onclick = ~>
e.prevent-default! @browser.move @folder
e.stop-propagation!
# このフォルダがルートかつカレントディレクトリならドロップ禁止 @onmouseover = ~>
if @folder == null and @browser.folder == null @hover = true
e.data-transfer.drop-effect = \none
# ドラッグされてきたものがファイルだったら
else if e.data-transfer.effect-allowed == \all
e.data-transfer.drop-effect = \copy
else
e.data-transfer.drop-effect = \move
return false
@ondragenter = ~> @onmouseout = ~>
if @folder != null or @browser.folder != null @hover = false
@draghover = true
@ondragleave = ~> @ondragover = (e) ~>
if @folder != null or @browser.folder != null e.prevent-default!
e.stop-propagation!
# このフォルダがルートかつカレントディレクトリならドロップ禁止
if @folder == null and @browser.folder == null
e.data-transfer.drop-effect = \none
# ドラッグされてきたものがファイルだったら
else if e.data-transfer.effect-allowed == \all
e.data-transfer.drop-effect = \copy
else
e.data-transfer.drop-effect = \move
return false
@ondragenter = ~>
if @folder != null or @browser.folder != null
@draghover = true
@ondragleave = ~>
if @folder != null or @browser.folder != null
@draghover = false
@ondrop = (e) ~>
e.stop-propagation!
@draghover = false @draghover = false
@ondrop = (e) ~> # ファイルだったら
e.stop-propagation! if e.data-transfer.files.length > 0
@draghover = false Array.prototype.for-each.call e.data-transfer.files, (file) ~>
@browser.upload file, @folder
# ファイルだったら
if e.data-transfer.files.length > 0
Array.prototype.for-each.call e.data-transfer.files, (file) ~>
@browser.upload file, @folder
return false
# データ取得
data = e.data-transfer.get-data 'text'
if !data?
return false
# パース
obj = JSON.parse data
# (ドライブの)ファイルだったら
if obj.type == \file
file = obj.id
@browser.remove-file file
@api \drive/files/update do
file_id: file
folder_id: if @folder? then @folder.id else null
.then ~>
# something
.catch (err, text-status) ~>
console.error err
# (ドライブの)フォルダーだったら
else if obj.type == \folder
folder = obj.id
# 移動先が自分自身ならreject
if @folder? and folder == @folder.id
return false return false
@browser.remove-folder folder
@api \drive/folders/update do
folder_id: folder
parent_id: if @folder? then @folder.id else null
.then ~>
# something
.catch (err, text-status) ~>
console.error err
return false # データ取得
data = e.data-transfer.get-data 'text'
if !data?
return false
# パース
obj = JSON.parse data
# (ドライブの)ファイルだったら
if obj.type == \file
file = obj.id
@browser.remove-file file
@api \drive/files/update do
file_id: file
folder_id: if @folder? then @folder.id else null
.then ~>
# something
.catch (err, text-status) ~>
console.error err
# (ドライブの)フォルダーだったら
else if obj.type == \folder
folder = obj.id
# 移動先が自分自身ならreject
if @folder? and folder == @folder.id
return false
@browser.remove-folder folder
@api \drive/folders/update do
folder_id: folder
parent_id: if @folder? then @folder.id else null
.then ~>
# something
.catch (err, text-status) ~>
console.error err
return false
</script>
</mk-drive-browser-nav-folder>

View file

@ -1,34 +1,41 @@
mk-ellipsis-icon <mk-ellipsis-icon>
div <div></div>
div <div></div>
div <div></div>
<style type="stylus">
:scope
display block
width 70px
margin 0 auto
text-align center
style. > div
display block display inline-block
width 70px width 18px
margin 0 auto height 18px
text-align center background-color rgba(0, 0, 0, 0.3)
border-radius 100%
animation bounce 1.4s infinite ease-in-out both
> div &:nth-child(1)
display inline-block animation-delay 0s
width 18px
height 18px
background-color rgba(0, 0, 0, 0.3)
border-radius 100%
animation bounce 1.4s infinite ease-in-out both
&:nth-child(1) &:nth-child(2)
animation-delay 0s margin 0 6px
animation-delay 0.16s
&:nth-child(2) &:nth-child(3)
margin 0 6px animation-delay 0.32s
animation-delay 0.16s
&:nth-child(3) @keyframes bounce
animation-delay 0.32s 0%, 80%, 100%
transform scale(0)
40%
transform scale(1)
@keyframes bounce
0%, 80%, 100%
transform scale(0)
40%
transform scale(1) </style>
</mk-ellipsis-icon>

View file

@ -1,127 +1,124 @@
mk-follow-button <mk-follow-button>
button(if={ !init }, class={ wait: wait, follow: !user.is_following, unfollow: user.is_following }, <button class="{ wait: wait, follow: !user.is_following, unfollow: user.is_following }" if="{ !init }" onclick="{ onclick }" disabled="{ wait }" title="{ user.is_following ? 'フォロー解除' : 'フォローする' }"><i class="fa fa-minus" if="{ !wait &amp;&amp; user.is_following }"></i><i class="fa fa-plus" if="{ !wait &amp;&amp; !user.is_following }"></i><i class="fa fa-spinner fa-pulse fa-fw" if="{ wait }"></i></button>
onclick={ onclick }, <div class="init" if="{ init }"><i class="fa fa-spinner fa-pulse fa-fw"></i></div>
disabled={ wait }, <style type="stylus">
title={ user.is_following ? 'フォロー解除' : 'フォローする' }) :scope
i.fa.fa-minus(if={ !wait && user.is_following }) display block
i.fa.fa-plus(if={ !wait && !user.is_following })
i.fa.fa-spinner.fa-pulse.fa-fw(if={ wait })
div.init(if={ init }): i.fa.fa-spinner.fa-pulse.fa-fw
style. > button
display block > .init
display block
cursor pointer
padding 0
margin 0
width 32px
height 32px
font-size 1em
outline none
border-radius 4px
> button *
> .init pointer-events none
display block
cursor pointer
padding 0
margin 0
width 32px
height 32px
font-size 1em
outline none
border-radius 4px
* &:focus
pointer-events none &:after
content ""
pointer-events none
position absolute
top -5px
right -5px
bottom -5px
left -5px
border 2px solid rgba($theme-color, 0.3)
border-radius 8px
&:focus &.follow
&:after color #888
content "" background linear-gradient(to bottom, #ffffff 0%, #f5f5f5 100%)
pointer-events none border solid 1px #e2e2e2
position absolute
top -5px
right -5px
bottom -5px
left -5px
border 2px solid rgba($theme-color, 0.3)
border-radius 8px
&.follow &:hover
color #888 background linear-gradient(to bottom, #f9f9f9 0%, #ececec 100%)
background linear-gradient(to bottom, #ffffff 0%, #f5f5f5 100%) border-color #dcdcdc
border solid 1px #e2e2e2
&:hover &:active
background linear-gradient(to bottom, #f9f9f9 0%, #ececec 100%) background #ececec
border-color #dcdcdc border-color #dcdcdc
&:active &.unfollow
background #ececec color $theme-color-foreground
border-color #dcdcdc background linear-gradient(to bottom, lighten($theme-color, 25%) 0%, lighten($theme-color, 10%) 100%)
border solid 1px lighten($theme-color, 15%)
&.unfollow &:not(:disabled)
color $theme-color-foreground font-weight bold
background linear-gradient(to bottom, lighten($theme-color, 25%) 0%, lighten($theme-color, 10%) 100%)
border solid 1px lighten($theme-color, 15%)
&:not(:disabled) &:hover:not(:disabled)
font-weight bold background linear-gradient(to bottom, lighten($theme-color, 8%) 0%, darken($theme-color, 8%) 100%)
border-color $theme-color
&:hover:not(:disabled) &:active:not(:disabled)
background linear-gradient(to bottom, lighten($theme-color, 8%) 0%, darken($theme-color, 8%) 100%) background $theme-color
border-color $theme-color border-color $theme-color
&:active:not(:disabled) &.wait
background $theme-color cursor wait !important
border-color $theme-color opacity 0.7
&.wait </style>
cursor wait !important <script>
opacity 0.7 @mixin \api
@mixin \is-promise
@mixin \stream
script. @user = null
@mixin \api @user-promise = if @is-promise @opts.user then @opts.user else Promise.resolve @opts.user
@mixin \is-promise @init = true
@mixin \stream @wait = false
@user = null @on \mount ~>
@user-promise = if @is-promise @opts.user then @opts.user else Promise.resolve @opts.user @user-promise.then (user) ~>
@init = true @user = user
@wait = false @init = false
@on \mount ~>
@user-promise.then (user) ~>
@user = user
@init = false
@update!
@stream.on \follow @on-stream-follow
@stream.on \unfollow @on-stream-unfollow
@on \unmount ~>
@stream.off \follow @on-stream-follow
@stream.off \unfollow @on-stream-unfollow
@on-stream-follow = (user) ~>
if user.id == @user.id
@user = user
@update!
@on-stream-unfollow = (user) ~>
if user.id == @user.id
@user = user
@update!
@onclick = ~>
@wait = true
if @user.is_following
@api \following/delete do
user_id: @user.id
.then ~>
@user.is_following = false
.catch (err) ->
console.error err
.then ~>
@wait = false
@update! @update!
else @stream.on \follow @on-stream-follow
@api \following/create do @stream.on \unfollow @on-stream-unfollow
user_id: @user.id
.then ~> @on \unmount ~>
@user.is_following = true @stream.off \follow @on-stream-follow
.catch (err) -> @stream.off \unfollow @on-stream-unfollow
console.error err
.then ~> @on-stream-follow = (user) ~>
@wait = false if user.id == @user.id
@user = user
@update! @update!
@on-stream-unfollow = (user) ~>
if user.id == @user.id
@user = user
@update!
@onclick = ~>
@wait = true
if @user.is_following
@api \following/delete do
user_id: @user.id
.then ~>
@user.is_following = false
.catch (err) ->
console.error err
.then ~>
@wait = false
@update!
else
@api \following/create do
user_id: @user.id
.then ~>
@user.is_following = true
.catch (err) ->
console.error err
.then ~>
@wait = false
@update!
</script>
</mk-follow-button>

View file

@ -1,163 +1,163 @@
mk-following-setuper <mk-following-setuper>
p.title 気になるユーザーをフォロー: <p class="title">気になるユーザーをフォロー:</p>
div.users(if={ !loading && users.length > 0 }) <div class="users" if="{ !loading &amp;&amp; users.length &gt; 0 }">
div.user(each={ users }) <div class="user" each="{ users }"><a class="avatar-anchor" href="{ CONFIG.url + '/' + username }"><img class="avatar" src="{ avatar_url + '?thumbnail&amp;size=42' }" alt="" data-user-preview="{ id }"/></a>
a.avatar-anchor(href={ CONFIG.url + '/' + username }) <div class="body"><a class="name" href="{ CONFIG.url + '/' + username }" target="_blank" data-user-preview="{ id }">{ name }</a>
img.avatar(src={ avatar_url + '?thumbnail&size=42' }, alt='', data-user-preview={ id }) <p class="username">@{ username }</p>
div.body </div>
a.name(href={ CONFIG.url + '/' + username }, target='_blank', data-user-preview={ id }) { name } <mk-follow-button user="{ this }"></mk-follow-button>
p.username @{ username } </div>
mk-follow-button(user={ this }) </div>
p.empty(if={ !loading && users.length == 0 }) <p class="empty" if="{ !loading &amp;&amp; users.length == 0 }">おすすめのユーザーは見つかりませんでした。</p>
| おすすめのユーザーは見つかりませんでした。 <p class="loading" if="{ loading }"><i class="fa fa-spinner fa-pulse fa-fw"></i>読み込んでいます
p.loading(if={ loading }) <mk-ellipsis></mk-ellipsis>
i.fa.fa-spinner.fa-pulse.fa-fw </p><a class="refresh" onclick="{ refresh }">もっと見る</a>
| 読み込んでいます <button class="close" onclick="{ close }" title="閉じる"><i class="fa fa-times"></i></button>
mk-ellipsis <style type="stylus">
a.refresh(onclick={ refresh }) もっと見る :scope
button.close(onclick={ close }, title='閉じる'): i.fa.fa-times
style.
display block
padding 24px
background #fff
> .title
margin 0 0 12px 0
font-size 1em
font-weight bold
color #888
> .users
&:after
content ""
display block display block
clear both padding 24px
background #fff
> .user > .title
padding 16px margin 0 0 12px 0
width 238px font-size 1em
float left font-weight bold
color #888
&:after > .users
content "" &:after
display block content ""
clear both
> .avatar-anchor
display block
float left
margin 0 12px 0 0
> .avatar
display block display block
width 42px clear both
height 42px
margin 0
border-radius 8px
vertical-align bottom
> .body > .user
float left padding 16px
width calc(100% - 54px) width 238px
float left
> .name &:after
margin 0 content ""
font-size 16px display block
line-height 24px clear both
> .avatar-anchor
display block
float left
margin 0 12px 0 0
> .avatar
display block
width 42px
height 42px
margin 0
border-radius 8px
vertical-align bottom
> .body
float left
width calc(100% - 54px)
> .name
margin 0
font-size 16px
line-height 24px
color #555
> .username
margin 0
font-size 15px
line-height 16px
color #ccc
> mk-follow-button
position absolute
top 16px
right 16px
> .empty
margin 0
padding 16px
text-align center
color #aaa
> .loading
margin 0
padding 16px
text-align center
color #aaa
> i
margin-right 4px
> .refresh
display block
margin 0 8px 0 0
text-align right
font-size 0.9em
color #999
> .close
cursor pointer
display block
position absolute
top 6px
right 6px
z-index 1
margin 0
padding 0
font-size 1.2em
color #999
border none
outline none
background transparent
&:hover
color #555 color #555
> .username &:active
margin 0 color #222
font-size 15px
line-height 16px
color #ccc
> mk-follow-button > i
position absolute padding 14px
top 16px
right 16px
> .empty </style>
margin 0 <script>
padding 16px @mixin \api
text-align center @mixin \user-preview
color #aaa
> .loading
margin 0
padding 16px
text-align center
color #aaa
> i
margin-right 4px
> .refresh
display block
margin 0 8px 0 0
text-align right
font-size 0.9em
color #999
> .close
cursor pointer
display block
position absolute
top 6px
right 6px
z-index 1
margin 0
padding 0
font-size 1.2em
color #999
border none
outline none
background transparent
&:hover
color #555
&:active
color #222
> i
padding 14px
script.
@mixin \api
@mixin \user-preview
@users = null
@loading = true
@limit = 6users
@page = 0
@on \mount ~>
@load!
@load = ~>
@loading = true
@users = null @users = null
@update! @loading = true
@api \users/recommendation do @limit = 6users
limit: @limit @page = 0
offset: @limit * @page
.then (users) ~> @on \mount ~>
@loading = false @load!
@users = users
@load = ~>
@loading = true
@users = null
@update! @update!
.catch (err, text-status) ->
console.error err
@refresh = ~> @api \users/recommendation do
if @users.length < @limit limit: @limit
@page = 0 offset: @limit * @page
else .then (users) ~>
@page++ @loading = false
@load! @users = users
@update!
.catch (err, text-status) ->
console.error err
@close = ~> @refresh = ~>
@unmount! if @users.length < @limit
@page = 0
else
@page++
@load!
@close = ~>
@unmount!
</script>
</mk-following-setuper>

View file

@ -1,15 +1,14 @@
mk-go-top <mk-go-top>
button.hidden(title='一番上へ') <button class="hidden" title="一番上へ"><i class="fa fa-angle-up"></i></button>
i.fa.fa-angle-up <script>
window.add-event-listener \load @on-scroll
window.add-event-listener \scroll @on-scroll
window.add-event-listener \resize @on-scroll
script. @on-scroll = ~>
if $ window .scroll-top! > 500px
window.add-event-listener \load @on-scroll @remove-class \hidden
window.add-event-listener \scroll @on-scroll else
window.add-event-listener \resize @on-scroll @add-class \hidden
</script>
@on-scroll = ~> </mk-go-top>
if $ window .scroll-top! > 500px
@remove-class \hidden
else
@add-class \hidden

View file

@ -1,75 +1,83 @@
mk-broadcast-home-widget <mk-broadcast-home-widget>
div.icon <div class="icon">
svg(height='32', version='1.1', viewBox='0 0 32 32', width='32') <svg height="32" version="1.1" viewBox="0 0 32 32" width="32">
path.tower(d='M16.04,11.24c1.79,0,3.239-1.45,3.239-3.24S17.83,4.76,16.04,4.76c-1.79,0-3.24,1.45-3.24,3.24 C12.78,9.78,14.24,11.24,16.04,11.24z M16.04,13.84c-0.82,0-1.66-0.2-2.4-0.6L7.34,29.98h2.98l1.72-2h8l1.681,2H24.7L18.42,13.24 C17.66,13.64,16.859,13.84,16.04,13.84z M16.02,14.8l2.02,7.2h-4L16.02,14.8z M12.04,25.98l2-2h4l2,2H12.04z') <path class="tower" d="M16.04,11.24c1.79,0,3.239-1.45,3.239-3.24S17.83,4.76,16.04,4.76c-1.79,0-3.24,1.45-3.24,3.24 C12.78,9.78,14.24,11.24,16.04,11.24z M16.04,13.84c-0.82,0-1.66-0.2-2.4-0.6L7.34,29.98h2.98l1.72-2h8l1.681,2H24.7L18.42,13.24 C17.66,13.64,16.859,13.84,16.04,13.84z M16.02,14.8l2.02,7.2h-4L16.02,14.8z M12.04,25.98l2-2h4l2,2H12.04z"></path>
path.wave.a(d='M4.66,1.04c-0.508-0.508-1.332-0.508-1.84,0c-1.86,1.92-2.8,4.44-2.8,6.94c0,2.52,0.94,5.04,2.8,6.96 c0.5,0.52,1.32,0.52,1.82,0s0.5-1.36,0-1.88C3.28,11.66,2.6,9.82,2.6,7.98S3.28,4.3,4.64,2.9C5.157,2.391,5.166,1.56,4.66,1.04z') <path class="wave a" d="M4.66,1.04c-0.508-0.508-1.332-0.508-1.84,0c-1.86,1.92-2.8,4.44-2.8,6.94c0,2.52,0.94,5.04,2.8,6.96 c0.5,0.52,1.32,0.52,1.82,0s0.5-1.36,0-1.88C3.28,11.66,2.6,9.82,2.6,7.98S3.28,4.3,4.64,2.9C5.157,2.391,5.166,1.56,4.66,1.04z"></path>
path.wave.b(d='M9.58,12.22c0.5-0.5,0.5-1.34,0-1.84C8.94,9.72,8.62,8.86,8.62,8s0.32-1.72,0.96-2.38c0.5-0.52,0.5-1.34,0-1.84 C9.346,3.534,9.02,3.396,8.68,3.4c-0.32,0-0.66,0.12-0.9,0.38C6.64,4.94,6.08,6.48,6.08,8s0.58,3.06,1.7,4.22 C8.28,12.72,9.1,12.72,9.58,12.22z') <path class="wave b" d="M9.58,12.22c0.5-0.5,0.5-1.34,0-1.84C8.94,9.72,8.62,8.86,8.62,8s0.32-1.72,0.96-2.38c0.5-0.52,0.5-1.34,0-1.84 C9.346,3.534,9.02,3.396,8.68,3.4c-0.32,0-0.66,0.12-0.9,0.38C6.64,4.94,6.08,6.48,6.08,8s0.58,3.06,1.7,4.22 C8.28,12.72,9.1,12.72,9.58,12.22z"></path>
path.wave.c(d='M22.42,3.78c-0.5,0.5-0.5,1.34,0,1.84c0.641,0.66,0.96,1.52,0.96,2.38s-0.319,1.72-0.96,2.38c-0.5,0.52-0.5,1.34,0,1.84 c0.487,0.497,1.285,0.505,1.781,0.018c0.007-0.006,0.013-0.012,0.02-0.018c1.139-1.16,1.699-2.7,1.699-4.22s-0.561-3.06-1.699-4.22 c-0.494-0.497-1.297-0.5-1.794-0.007C22.424,3.775,22.422,3.778,22.42,3.78z') <path class="wave c" d="M22.42,3.78c-0.5,0.5-0.5,1.34,0,1.84c0.641,0.66,0.96,1.52,0.96,2.38s-0.319,1.72-0.96,2.38c-0.5,0.52-0.5,1.34,0,1.84 c0.487,0.497,1.285,0.505,1.781,0.018c0.007-0.006,0.013-0.012,0.02-0.018c1.139-1.16,1.699-2.7,1.699-4.22s-0.561-3.06-1.699-4.22 c-0.494-0.497-1.297-0.5-1.794-0.007C22.424,3.775,22.422,3.778,22.42,3.78z"></path>
path.wave.d(d='M29.18,1.06c-0.479-0.502-1.273-0.522-1.775-0.044c-0.016,0.015-0.029,0.029-0.045,0.044c-0.5,0.52-0.5,1.36,0,1.88 c1.361,1.4,2.041,3.24,2.041,5.08s-0.68,3.66-2.041,5.08c-0.5,0.52-0.5,1.36,0,1.88c0.509,0.508,1.332,0.508,1.841,0 c1.86-1.92,2.8-4.44,2.8-6.96C31.99,5.424,30.98,2.931,29.18,1.06z') <path class="wave d" d="M29.18,1.06c-0.479-0.502-1.273-0.522-1.775-0.044c-0.016,0.015-0.029,0.029-0.045,0.044c-0.5,0.52-0.5,1.36,0,1.88 c1.361,1.4,2.041,3.24,2.041,5.08s-0.68,3.66-2.041,5.08c-0.5,0.52-0.5,1.36,0,1.88c0.509,0.508,1.332,0.508,1.841,0 c1.86-1.92,2.8-4.44,2.8-6.96C31.99,5.424,30.98,2.931,29.18,1.06z"></path>
</svg>
</div>
<h1>開発者募集中!</h1>
<p><a href="https://github.com/syuilo/misskey" target="_blank">Misskeyはオープンソースで開発されています。リポジトリはこちら。</a></p>
<style type="stylus">
:scope
display block
padding 10px 10px 10px 50px
background transparent
border-color #4078c0 !important
h1 開発者募集中! &:after
p: a(href='https://github.com/syuilo/misskey', target='_blank') Misskeyはオープンソースで開発されています。リポジトリはこちら。 content ""
display block
clear both
style. > .icon
display block display block
padding 10px 10px 10px 50px float left
background transparent margin-left -40px
border-color #4078c0 !important
&:after > svg
content "" fill currentColor
display block color #4078c0
clear both
> .icon > .wave
display block
float left
margin-left -40px
> svg
fill currentColor
color #4078c0
> .wave
opacity 1
&.a
animation wave 20s ease-in-out 2.1s infinite
&.b
animation wave 20s ease-in-out 2s infinite
&.c
animation wave 20s ease-in-out 2s infinite
&.d
animation wave 20s ease-in-out 2.1s infinite
@keyframes wave
0%
opacity 1
1.5%
opacity 0
3.5%
opacity 0
5%
opacity 1
6.5%
opacity 0
8.5%
opacity 0
10%
opacity 1 opacity 1
> h1 &.a
margin 0 animation wave 20s ease-in-out 2.1s infinite
font-size 0.95em &.b
font-weight normal animation wave 20s ease-in-out 2s infinite
color #4078c0 &.c
animation wave 20s ease-in-out 2s infinite
&.d
animation wave 20s ease-in-out 2.1s infinite
> p @keyframes wave
display block 0%
z-index 1 opacity 1
margin 0 1.5%
font-size 0.7em opacity 0
color #555 3.5%
opacity 0
5%
opacity 1
6.5%
opacity 0
8.5%
opacity 0
10%
opacity 1
a > h1
color #555 margin 0
font-size 0.95em
font-weight normal
color #4078c0
> p
display block
z-index 1
margin 0
font-size 0.7em
color #555
a
color #555
</style>
</mk-broadcast-home-widget>

View file

@ -1,147 +1,148 @@
mk-calendar-home-widget(data-special={ special }) <mk-calendar-home-widget data-special="{ special }">
div.calendar(data-is-holiday={ is-holiday }) <div class="calendar" data-is-holiday="{ isHoliday }">
p.month-and-year <p class="month-and-year"><span class="year">{ year }年</span><span class="month">{ month }月</span></p>
span.year { year }年 <p class="day">{ day }日</p>
span.month { month }月 <p class="week-day">{ weekDay }曜日</p>
p.day { day }日 </div>
p.week-day { week-day }曜日 <div class="info">
div.info <div>
div <p>今日:<b>{ dayP.toFixed(1) }%</b></p>
p <div class="meter">
| 今日: <div class="val" style="{ 'width:' + dayP + '%' }"></div>
b { day-p.to-fixed(1) }% </div>
div.meter </div>
div.val(style={ 'width:' + day-p + '%' }) <div>
<p>今月:<b>{ monthP.toFixed(1) }%</b></p>
<div class="meter">
<div class="val" style="{ 'width:' + monthP + '%' }"></div>
</div>
</div>
<div>
<p>今年:<b>{ yearP.toFixed(1) }%</b></p>
<div class="meter">
<div class="val" style="{ 'width:' + yearP + '%' }"></div>
</div>
</div>
</div>
<style type="stylus">
:scope
display block
padding 16px 0
color #777
background #fff
div &[data-special='on-new-years-day']
p border-color #ef95a0 !important
| 今月:
b { month-p.to-fixed(1) }%
div.meter
div.val(style={ 'width:' + month-p + '%' })
div &:after
p content ""
| 今年: display block
b { year-p.to-fixed(1) }% clear both
div.meter
div.val(style={ 'width:' + year-p + '%' })
style. > .calendar
display block float left
padding 16px 0 width 60%
color #777 text-align center
background #fff
&[data-special='on-new-years-day'] &[data-is-holiday]
border-color #ef95a0 !important > .day
color #ef95a0
&:after > p
content "" margin 0
display block line-height 18px
clear both font-size 14px
> .calendar > span
float left margin 0 4px
width 60%
text-align center
&[data-is-holiday] > .day
> .day margin 10px 0
color #ef95a0 line-height 32px
font-size 28px
> p > .info
margin 0 display block
line-height 18px float left
font-size 14px width 40%
padding 0 16px 0 0
> span > div
margin 0 4px margin-bottom 8px
> .day &:last-child
margin 10px 0 margin-bottom 4px
line-height 32px
font-size 28px
> .info > p
display block margin 0 0 2px 0
float left font-size 12px
width 40% line-height 18px
padding 0 16px 0 0 color #888
> div > b
margin-bottom 8px margin-left 2px
&:last-child > .meter
margin-bottom 4px width 100%
overflow hidden
background #eee
border-radius 8px
> p > .val
margin 0 0 2px 0 height 4px
font-size 12px background $theme-color
line-height 18px
color #888
> b &:nth-child(1)
margin-left 2px > .meter > .val
background #f7796c
> .meter &:nth-child(2)
width 100% > .meter > .val
overflow hidden background #a1de41
background #eee
border-radius 8px
> .val &:nth-child(3)
height 4px > .meter > .val
background $theme-color background #41ddde
&:nth-child(1) </style>
> .meter > .val <script>
background #f7796c @draw = ~>
now = new Date!
nd = now.get-date!
nm = now.get-month!
ny = now.get-full-year!
&:nth-child(2) @year = ny
> .meter > .val @month = nm + 1
background #a1de41 @day = nd
@week-day = [\日 \月 \火 \水 \木 \金 \土][now.get-day!]
&:nth-child(3) @day-numer = (now - (new Date ny, nm, nd))
> .meter > .val @day-denom = 1000ms * 60s * 60m * 24h
background #41ddde @month-numer = (now - (new Date ny, nm, 1))
@month-denom = (new Date ny, nm + 1, 1) - (new Date ny, nm, 1)
@year-numer = (now - (new Date ny, 0, 1))
@year-denom = (new Date ny + 1, 0, 1) - (new Date ny, 0, 1)
script. @day-p = @day-numer / @day-denom * 100
@draw = ~> @month-p = @month-numer / @month-denom * 100
now = new Date! @year-p = @year-numer / @year-denom * 100
nd = now.get-date!
nm = now.get-month!
ny = now.get-full-year!
@year = ny @is-holiday =
@month = nm + 1 (now.get-day! == 0 or now.get-day! == 6)
@day = nd
@week-day = [\日 \月 \火 \水 \木 \金 \土][now.get-day!]
@day-numer = (now - (new Date ny, nm, nd)) @special =
@day-denom = 1000ms * 60s * 60m * 24h | nm == 0 and nd == 1 => \on-new-years-day
@month-numer = (now - (new Date ny, nm, 1)) | _ => false
@month-denom = (new Date ny, nm + 1, 1) - (new Date ny, nm, 1)
@year-numer = (now - (new Date ny, 0, 1))
@year-denom = (new Date ny + 1, 0, 1) - (new Date ny, 0, 1)
@day-p = @day-numer / @day-denom * 100 @update!
@month-p = @month-numer / @month-denom * 100
@year-p = @year-numer / @year-denom * 100
@is-holiday = @draw!
(now.get-day! == 0 or now.get-day! == 6)
@special = @on \mount ~>
| nm == 0 and nd == 1 => \on-new-years-day @clock = set-interval @draw, 1000ms
| _ => false
@update! @on \unmount ~>
clear-interval @clock
@draw! </script>
</mk-calendar-home-widget>
@on \mount ~>
@clock = set-interval @draw, 1000ms
@on \unmount ~>
clear-interval @clock

View file

@ -1,37 +1,36 @@
mk-donation-home-widget <mk-donation-home-widget>
article <article>
h1 <h1><i class="fa fa-heart"></i>寄付のお願い</h1>
i.fa.fa-heart <p>
| 寄付のお願い Misskeyの運営にはドメイン、サーバー等のコストが掛かります。
p Misskeyは広告を掲載したりしないため、 収入を皆様からの寄付に頼っています。
| Misskeyの運営にはドメイン、サーバー等のコストが掛かります。 もしご興味があれば、<a href="/syuilo" data-user-preview="@syuilo">@syuilo</a>までご連絡ください。ご協力ありがとうございます。
| Misskeyは広告を掲載したりしないため、 収入を皆様からの寄付に頼っています。 </p>
| もしご興味があれば、 </article>
a(href='/syuilo', data-user-preview='@syuilo') @syuilo <style type="stylus">
| までご連絡ください。ご協力ありがとうございます。 :scope
style.
display block
background #fff
border-color #ead8bb !important
> article
padding 20px
> h1
margin 0 0 5px 0
font-size 1em
color #888
> i
margin-right 0.25em
> p
display block display block
z-index 1 background #fff
margin 0 border-color #ead8bb !important
font-size 0.8em
color #999
script. > article
@mixin \user-preview padding 20px
> h1
margin 0 0 5px 0
font-size 1em
color #888
> i
margin-right 0.25em
> p
display block
z-index 1
margin 0
font-size 0.8em
color #999
</style>
<script>@mixin \user-preview</script>
</mk-donation-home-widget>

View file

@ -1,117 +1,112 @@
mk-mentions-home-widget <mk-mentions-home-widget>
header <header><span data-is-active="{ mode == 'all' }" onclick="{ setMode.bind(this, 'all') }">すべて</span><span data-is-active="{ mode == 'following' }" onclick="{ setMode.bind(this, 'following') }">フォロー中</span></header>
span(data-is-active={ mode == 'all' }, onclick={ set-mode.bind(this, 'all') }) すべて <div class="loading" if="{ isLoading }">
span(data-is-active={ mode == 'following' }, onclick={ set-mode.bind(this, 'following') }) フォロー中 <mk-ellipsis-icon></mk-ellipsis-icon>
div.loading(if={ is-loading }) </div>
mk-ellipsis-icon <p class="empty" if="{ isEmpty }"><i class="fa fa-comments-o"></i><span if="{ mode == 'all' }">あなた宛ての投稿はありません。</span><span if="{ mode == 'following' }">あなたがフォローしているユーザーからの言及はありません。</span></p>
p.empty(if={ is-empty }) <mk-timeline ref="timeline"><yield to="footer"><i class="fa fa-moon-o" if="{ !parent.moreLoading }"></i><i class="fa fa-spinner fa-pulse fa-fw" if="{ parent.moreLoading }"></i></yield></mk-timeline>
i.fa.fa-comments-o <style type="stylus">
span(if={ mode == 'all' }) あなた宛ての投稿はありません。 :scope
span(if={ mode == 'following' }) あなたがフォローしているユーザーからの言及はありません。
mk-timeline@timeline
<yield to="footer">
i.fa.fa-moon-o(if={ !parent.more-loading })
i.fa.fa-spinner.fa-pulse.fa-fw(if={ parent.more-loading })
</yield>
style.
display block
background #fff
> header
padding 8px 16px
border-bottom solid 1px #eee
> span
margin-right 16px
line-height 27px
font-size 18px
color #555
&:not([data-is-active])
color $theme-color
cursor pointer
&:hover
text-decoration underline
> .loading
padding 64px 0
> .empty
display block
margin 0 auto
padding 32px
max-width 400px
text-align center
color #999
> i
display block display block
margin-bottom 16px background #fff
font-size 3em
color #ccc
script. > header
@mixin \i padding 8px 16px
@mixin \api border-bottom solid 1px #eee
@is-loading = true > span
@is-empty = false margin-right 16px
@more-loading = false line-height 27px
@mode = \all font-size 18px
color #555
@on \mount ~> &:not([data-is-active])
document.add-event-listener \keydown @on-document-keydown color $theme-color
window.add-event-listener \scroll @on-scroll cursor pointer
@fetch ~> &:hover
@trigger \loaded text-decoration underline
@on \unmount ~> > .loading
document.remove-event-listener \keydown @on-document-keydown padding 64px 0
window.remove-event-listener \scroll @on-scroll
@on-document-keydown = (e) ~> > .empty
tag = e.target.tag-name.to-lower-case! display block
if tag != \input and tag != \textarea margin 0 auto
if e.which == 84 # t padding 32px
@refs.timeline.focus! max-width 400px
text-align center
color #999
@fetch = (cb) ~> > i
@api \posts/mentions do display block
following: @mode == \following margin-bottom 16px
.then (posts) ~> font-size 3em
@is-loading = false color #ccc
@is-empty = posts.length == 0
</style>
<script>
@mixin \i
@mixin \api
@is-loading = true
@is-empty = false
@more-loading = false
@mode = \all
@on \mount ~>
document.add-event-listener \keydown @on-document-keydown
window.add-event-listener \scroll @on-scroll
@fetch ~>
@trigger \loaded
@on \unmount ~>
document.remove-event-listener \keydown @on-document-keydown
window.remove-event-listener \scroll @on-scroll
@on-document-keydown = (e) ~>
tag = e.target.tag-name.to-lower-case!
if tag != \input and tag != \textarea
if e.which == 84 # t
@refs.timeline.focus!
@fetch = (cb) ~>
@api \posts/mentions do
following: @mode == \following
.then (posts) ~>
@is-loading = false
@is-empty = posts.length == 0
@update!
@refs.timeline.set-posts posts
if cb? then cb!
.catch (err) ~>
console.error err
if cb? then cb!
@more = ~>
if @more-loading or @is-loading or @refs.timeline.posts.length == 0
return
@more-loading = true
@update! @update!
@refs.timeline.set-posts posts @api \posts/mentions do
if cb? then cb! following: @mode == \following
.catch (err) ~> max_id: @refs.timeline.tail!.id
console.error err .then (posts) ~>
if cb? then cb! @more-loading = false
@update!
@refs.timeline.prepend-posts posts
.catch (err) ~>
console.error err
@more = ~> @on-scroll = ~>
if @more-loading or @is-loading or @refs.timeline.posts.length == 0 current = window.scroll-y + window.inner-height
return if current > document.body.offset-height - 8
@more-loading = true @more!
@update!
@api \posts/mentions do
following: @mode == \following
max_id: @refs.timeline.tail!.id
.then (posts) ~>
@more-loading = false
@update!
@refs.timeline.prepend-posts posts
.catch (err) ~>
console.error err
@on-scroll = ~> @set-mode = (mode) ~>
current = window.scroll-y + window.inner-height @update do
if current > document.body.offset-height - 8 mode: mode
@more! @fetch!
</script>
@set-mode = (mode) ~> </mk-mentions-home-widget>
@update do
mode: mode
@fetch!

View file

@ -1,23 +1,21 @@
mk-nav-home-widget <mk-nav-home-widget><a href="{ CONFIG.urls.about }">Misskeyについて</a><i>・</i><a href="{ CONFIG.urls.about + '/status' }">ステータス</a><i>・</i><a href="https://github.com/syuilo/misskey">リポジトリ</a><i>・</i><a href="{ CONFIG.urls.dev }">開発者</a><i>・</i><a href="https://twitter.com/misskey_xyz" target="_blank">Follow us on <i class="fa fa-twitter"></i></a>
a(href={ CONFIG.urls.about }) Misskeyについて <style type="stylus">
i ・ :scope
a(href={ CONFIG.urls.about + '/status' }) ステータス display block
i ・ padding 16px
a(href='https://github.com/syuilo/misskey') リポジトリ font-size 12px
i ・ color #aaa
a(href={ CONFIG.urls.dev }) 開発者 background #fff
i ・
a(href='https://twitter.com/misskey_xyz', target='_blank') Follow us on <i class="fa fa-twitter"></i>
style. a
display block color #999
padding 16px
font-size 12px
color #aaa
background #fff
a i
color #999 color #ccc
i
color #ccc
</style>
</mk-nav-home-widget>

View file

@ -1,49 +1,50 @@
mk-notifications-home-widget <mk-notifications-home-widget>
p.title <p class="title"><i class="fa fa-bell-o"></i>通知</p>
i.fa.fa-bell-o <button onclick="{ settings }" title="通知の設定"><i class="fa fa-cog"></i></button>
| 通知 <mk-notifications></mk-notifications>
button(onclick={ settings }, title='通知の設定'): i.fa.fa-cog <style type="stylus">
mk-notifications :scope
display block
background #fff
style. > .title
display block z-index 1
background #fff 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)
> .title > i
z-index 1 margin-right 4px
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 > button
margin-right 4px position absolute
z-index 2
top 0
right 0
padding 0
width 42px
font-size 0.9em
line-height 42px
color #ccc
> button &:hover
position absolute color #aaa
z-index 2
top 0
right 0
padding 0
width 42px
font-size 0.9em
line-height 42px
color #ccc
&:hover &:active
color #aaa color #999
&:active > mk-notifications
color #999 max-height 300px
overflow auto
> mk-notifications </style>
max-height 300px <script>
overflow auto @settings = ~>
w = riot.mount document.body.append-child document.create-element \mk-settings-window .0
script. w.switch \notification
@settings = ~> </script>
w = riot.mount document.body.append-child document.create-element \mk-settings-window .0 </mk-notifications-home-widget>
w.switch \notification

View file

@ -1,86 +1,87 @@
mk-photo-stream-home-widget <mk-photo-stream-home-widget>
p.title <p class="title"><i class="fa fa-camera"></i>フォトストリーム</p>
i.fa.fa-camera <p class="initializing" if="{ initializing }"><i class="fa fa-spinner fa-pulse fa-fw"></i>読み込んでいます
| フォトストリーム <mk-ellipsis></mk-ellipsis>
p.initializing(if={ initializing }) </p>
i.fa.fa-spinner.fa-pulse.fa-fw <div class="stream" if="{ !initializing &amp;&amp; images.length &gt; 0 }">
| 読み込んでいます <virtual each="{ image in images }">
mk-ellipsis <div class="img" style="{ 'background-image: url(' + image.url + '?thumbnail&amp;size=256)' }"></div>
div.stream(if={ !initializing && images.length > 0 }) </virtual>
virtual(each={ image in images }) </div>
div.img(style={ 'background-image: url(' + image.url + '?thumbnail&size=256)' }) <p class="empty" if="{ !initializing &amp;&amp; images.length == 0 }">写真はありません</p>
p.empty(if={ !initializing && images.length == 0 }) <style type="stylus">
| 写真はありません :scope
display block
background #fff
style. > .title
display block z-index 1
background #fff 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)
> .title > i
z-index 1 margin-right 4px
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 > .stream
margin-right 4px display -webkit-flex
display -moz-flex
display -ms-flex
display flex
justify-content center
flex-wrap wrap
padding 8px
> .stream > .img
display -webkit-flex flex 1 1 33%
display -moz-flex width 33%
display -ms-flex height 80px
display flex background-position center center
justify-content center background-size cover
flex-wrap wrap background-clip content-box
padding 8px border solid 2px transparent
> .img > .initializing
flex 1 1 33% > .empty
width 33% margin 0
height 80px padding 16px
background-position center center text-align center
background-size cover color #aaa
background-clip content-box
border solid 2px transparent
> .initializing > i
> .empty margin-right 4px
margin 0
padding 16px
text-align center
color #aaa
> i </style>
margin-right 4px <script>
@mixin \api
@mixin \stream
script. @images = []
@mixin \api @initializing = true
@mixin \stream
@images = [] @on \mount ~>
@initializing = true @stream.on \drive_file_created @on-stream-drive-file-created
@on \mount ~> @api \drive/stream do
@stream.on \drive_file_created @on-stream-drive-file-created type: 'image/*'
limit: 9images
.then (images) ~>
@initializing = false
@images = images
@update!
@api \drive/stream do @on \unmount ~>
type: 'image/*' @stream.off \drive_file_created @on-stream-drive-file-created
limit: 9images
.then (images) ~>
@initializing = false
@images = images
@update!
@on \unmount ~> @on-stream-drive-file-created = (file) ~>
@stream.off \drive_file_created @on-stream-drive-file-created if /^image\/.+$/.test file.type
@images.unshift file
@on-stream-drive-file-created = (file) ~> if @images.length > 9
if /^image\/.+$/.test file.type @images.pop!
@images.unshift file @update!
if @images.length > 9 </script>
@images.pop! </mk-photo-stream-home-widget>
@update!

View file

@ -1,55 +1,56 @@
mk-profile-home-widget <mk-profile-home-widget>
div.banner(style={ I.banner_url ? 'background-image: url(' + I.banner_url + '?thumbnail&size=256)' : '' }, onclick={ set-banner }) <div class="banner" style="{ I.banner_url ? 'background-image: url(' + I.banner_url + '?thumbnail&amp;size=256)' : '' }" onclick="{ setBanner }"></div><img class="avatar" src="{ I.avatar_url + '?thumbnail&amp;size=64' }" onclick="{ setAvatar }" alt="avatar" data-user-preview="{ I.id }"/><a class="name" href="{ CONFIG.url + '/' + I.username }">{ I.name }</a>
img.avatar(src={ I.avatar_url + '?thumbnail&size=64' }, onclick={ set-avatar }, alt='avatar', data-user-preview={ I.id }) <p class="username">@{ I.username }</p>
a.name(href={ CONFIG.url + '/' + I.username }) { I.name } <style type="stylus">
p.username @{ I.username } :scope
display block
background #fff
style. > .banner
display block height 100px
background #fff background-color #f5f5f5
background-size cover
background-position center
> .banner > .avatar
height 100px display block
background-color #f5f5f5 position absolute
background-size cover top 76px
background-position center left 16px
width 58px
height 58px
margin 0
border solid 3px #fff
border-radius 8px
vertical-align bottom
> .avatar > .name
display block display block
position absolute margin 10px 0 0 92px
top 76px line-height 16px
left 16px font-weight bold
width 58px color #555
height 58px
margin 0
border solid 3px #fff
border-radius 8px
vertical-align bottom
> .name > .username
display block display block
margin 10px 0 0 92px margin 4px 0 8px 92px
line-height 16px line-height 16px
font-weight bold font-size 0.9em
color #555 color #999
> .username </style>
display block <script>
margin 4px 0 8px 92px @mixin \i
line-height 16px @mixin \user-preview
font-size 0.9em @mixin \update-avatar
color #999 @mixin \update-banner
script. @set-avatar = ~>
@mixin \i @update-avatar @I, (i) ~>
@mixin \user-preview @update-i i
@mixin \update-avatar
@mixin \update-banner
@set-avatar = ~> @set-banner = ~>
@update-avatar @I, (i) ~> @update-banner @I, (i) ~>
@update-i i @update-i i
</script>
@set-banner = ~> </mk-profile-home-widget>
@update-banner @I, (i) ~>
@update-i i

View file

@ -1,94 +1,94 @@
mk-rss-reader-home-widget <mk-rss-reader-home-widget>
p.title <p class="title"><i class="fa fa-rss-square"></i>RSS</p>
i.fa.fa-rss-square <button onclick="{ settings }" title="設定"><i class="fa fa-cog"></i></button>
| RSS <div class="feed" if="{ !initializing }">
button(onclick={ settings }, title='設定'): i.fa.fa-cog <virtual each="{ item in items }"><a href="{ item.link }" target="_blank">{ item.title }</a></virtual>
div.feed(if={ !initializing }) </div>
virtual(each={ item in items }) <p class="initializing" if="{ initializing }"><i class="fa fa-spinner fa-pulse fa-fw"></i>読み込んでいます
a(href={ item.link }, target='_blank') { item.title } <mk-ellipsis></mk-ellipsis>
p.initializing(if={ initializing }) </p>
i.fa.fa-spinner.fa-pulse.fa-fw <style type="stylus">
| 読み込んでいます :scope
mk-ellipsis
style.
display block
background #fff
> .title
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
top 0
right 0
padding 0
width 42px
font-size 0.9em
line-height 42px
color #ccc
&:hover
color #aaa
&:active
color #999
> .feed
padding 12px 16px
font-size 0.9em
> a
display block display block
padding 4px 0 background #fff
color #666
border-bottom dashed 1px #eee
&:last-child > .title
border-bottom none 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)
> .initializing > i
margin 0 margin-right 4px
padding 16px
text-align center
color #aaa
> i > button
margin-right 4px position absolute
top 0
right 0
padding 0
width 42px
font-size 0.9em
line-height 42px
color #ccc
script. &:hover
@mixin \api color #aaa
@mixin \NotImplementedException
@url = 'http://news.yahoo.co.jp/pickup/rss.xml' &:active
@items = [] color #999
@initializing = true
@on \mount ~> > .feed
@fetch! padding 12px 16px
@clock = set-interval @fetch, 60000ms font-size 0.9em
@on \unmount ~> > a
clear-interval @clock display block
padding 4px 0
color #666
border-bottom dashed 1px #eee
@fetch = ~> &:last-child
@api CONFIG.url + '/api:rss' do border-bottom none
url: @url
.then (feed) ~>
@items = feed.rss.channel.item
@initializing = false
@update!
.catch (err) ->
console.error err
@settings = ~> > .initializing
@NotImplementedException! margin 0
padding 16px
text-align center
color #aaa
> i
margin-right 4px
</style>
<script>
@mixin \api
@mixin \NotImplementedException
@url = 'http://news.yahoo.co.jp/pickup/rss.xml'
@items = []
@initializing = true
@on \mount ~>
@fetch!
@clock = set-interval @fetch, 60000ms
@on \unmount ~>
clear-interval @clock
@fetch = ~>
@api CONFIG.url + '/api:rss' do
url: @url
.then (feed) ~>
@items = feed.rss.channel.item
@initializing = false
@update!
.catch (err) ->
console.error err
@settings = ~>
@NotImplementedException!
</script>
</mk-rss-reader-home-widget>

View file

@ -1,113 +1,111 @@
mk-timeline-home-widget <mk-timeline-home-widget>
mk-following-setuper(if={ no-following }) <mk-following-setuper if="{ noFollowing }"></mk-following-setuper>
div.loading(if={ is-loading }) <div class="loading" if="{ isLoading }">
mk-ellipsis-icon <mk-ellipsis-icon></mk-ellipsis-icon>
p.empty(if={ is-empty }) </div>
i.fa.fa-comments-o <p class="empty" if="{ isEmpty }"><i class="fa fa-comments-o"></i>自分の投稿や、自分がフォローしているユーザーの投稿が表示されます。</p>
| 自分の投稿や、自分がフォローしているユーザーの投稿が表示されます。 <mk-timeline ref="timeline"><yield to="footer"><i class="fa fa-moon-o" if="{ !parent.moreLoading }"></i><i class="fa fa-spinner fa-pulse fa-fw" if="{ parent.moreLoading }"></i></yield></mk-timeline>
mk-timeline@timeline <style type="stylus">
<yield to="footer"> :scope
i.fa.fa-moon-o(if={ !parent.more-loading })
i.fa.fa-spinner.fa-pulse.fa-fw(if={ parent.more-loading })
</yield>
style.
display block
background #fff
> mk-following-setuper
border-bottom solid 1px #eee
> .loading
padding 64px 0
> .empty
display block
margin 0 auto
padding 32px
max-width 400px
text-align center
color #999
> i
display block display block
margin-bottom 16px background #fff
font-size 3em
color #ccc
script. > mk-following-setuper
@mixin \i border-bottom solid 1px #eee
@mixin \api
@mixin \stream
@is-loading = true > .loading
@is-empty = false padding 64px 0
@more-loading = false
@no-following = @I.following_count == 0
@on \mount ~> > .empty
@stream.on \post @on-stream-post display block
@stream.on \follow @on-stream-follow margin 0 auto
@stream.on \unfollow @on-stream-unfollow padding 32px
max-width 400px
text-align center
color #999
document.add-event-listener \keydown @on-document-keydown > i
window.add-event-listener \scroll @on-scroll display block
margin-bottom 16px
font-size 3em
color #ccc
@load ~> </style>
@trigger \loaded <script>
@mixin \i
@mixin \api
@mixin \stream
@on \unmount ~> @is-loading = true
@stream.off \post @on-stream-post
@stream.off \follow @on-stream-follow
@stream.off \unfollow @on-stream-unfollow
document.remove-event-listener \keydown @on-document-keydown
window.remove-event-listener \scroll @on-scroll
@on-document-keydown = (e) ~>
tag = e.target.tag-name.to-lower-case!
if tag != \input and tag != \textarea
if e.which == 84 # t
@refs.timeline.focus!
@load = (cb) ~>
@api \posts/timeline
.then (posts) ~>
@is-loading = false
@is-empty = posts.length == 0
@update!
@refs.timeline.set-posts posts
if cb? then cb!
.catch (err) ~>
console.error err
if cb? then cb!
@more = ~>
if @more-loading or @is-loading or @refs.timeline.posts.length == 0
return
@more-loading = true
@update!
@api \posts/timeline do
max_id: @refs.timeline.tail!.id
.then (posts) ~>
@more-loading = false
@update!
@refs.timeline.prepend-posts posts
.catch (err) ~>
console.error err
@on-stream-post = (post) ~>
@is-empty = false @is-empty = false
@update! @more-loading = false
@refs.timeline.add-post post @no-following = @I.following_count == 0
@on-stream-follow = ~> @on \mount ~>
@load! @stream.on \post @on-stream-post
@stream.on \follow @on-stream-follow
@stream.on \unfollow @on-stream-unfollow
@on-stream-unfollow = ~> document.add-event-listener \keydown @on-document-keydown
@load! window.add-event-listener \scroll @on-scroll
@on-scroll = ~> @load ~>
current = window.scroll-y + window.inner-height @trigger \loaded
if current > document.body.offset-height - 8
@more! @on \unmount ~>
@stream.off \post @on-stream-post
@stream.off \follow @on-stream-follow
@stream.off \unfollow @on-stream-unfollow
document.remove-event-listener \keydown @on-document-keydown
window.remove-event-listener \scroll @on-scroll
@on-document-keydown = (e) ~>
tag = e.target.tag-name.to-lower-case!
if tag != \input and tag != \textarea
if e.which == 84 # t
@refs.timeline.focus!
@load = (cb) ~>
@api \posts/timeline
.then (posts) ~>
@is-loading = false
@is-empty = posts.length == 0
@update!
@refs.timeline.set-posts posts
if cb? then cb!
.catch (err) ~>
console.error err
if cb? then cb!
@more = ~>
if @more-loading or @is-loading or @refs.timeline.posts.length == 0
return
@more-loading = true
@update!
@api \posts/timeline do
max_id: @refs.timeline.tail!.id
.then (posts) ~>
@more-loading = false
@update!
@refs.timeline.prepend-posts posts
.catch (err) ~>
console.error err
@on-stream-post = (post) ~>
@is-empty = false
@update!
@refs.timeline.add-post post
@on-stream-follow = ~>
@load!
@on-stream-unfollow = ~>
@load!
@on-scroll = ~>
current = window.scroll-y + window.inner-height
if current > document.body.offset-height - 8
@more!
</script>
</mk-timeline-home-widget>

View file

@ -1,70 +1,71 @@
mk-tips-home-widget <mk-tips-home-widget>
p@tip <p ref="tip"><i class="fa fa-lightbulb-o"></i><span ref="text"></span></p>
i.fa.fa-lightbulb-o <style type="stylus">
span@text :scope
display block
background transparent !important
border none !important
overflow visible !important
style. > p
display block display block
background transparent !important margin 0
border none !important padding 0 12px
overflow visible !important text-align center
font-size 0.7em
color #999
> p > i
display block margin-right 4px
margin 0
padding 0 12px
text-align center
font-size 0.7em
color #999
> i kbd
margin-right 4px display inline
padding 0 6px
margin 0 2px
font-size 1em
font-family inherit
border solid 1px #999
border-radius 2px
kbd </style>
display inline <script>
padding 0 6px @tips = [
margin 0 2px '<kbd>t</kbd>でタイムラインにフォーカスできます'
font-size 1em '<kbd>p</kbd>または<kbd>n</kbd>で投稿フォームを開きます'
font-family inherit '投稿フォームにはファイルをドラッグ&ドロップできます'
border solid 1px #999 '投稿フォームにクリップボードにある画像データをペーストできます'
border-radius 2px 'ドライブにファイルをドラッグ&ドロップしてアップロードできます'
'ドライブでファイルをドラッグしてフォルダ移動できます'
'ドライブでフォルダをドラッグしてフォルダ移動できます'
'ホームをカスタマイズできます(準備中)'
'MisskeyはMIT Licenseです'
]
script. @on \mount ~>
@tips = [ @set!
'<kbd>t</kbd>でタイムラインにフォーカスできます' @clock = set-interval @change, 20000ms
'<kbd>p</kbd>または<kbd>n</kbd>で投稿フォームを開きます'
'投稿フォームにはファイルをドラッグ&ドロップできます'
'投稿フォームにクリップボードにある画像データをペーストできます'
'ドライブにファイルをドラッグ&ドロップしてアップロードできます'
'ドライブでファイルをドラッグしてフォルダ移動できます'
'ドライブでフォルダをドラッグしてフォルダ移動できます'
'ホームをカスタマイズできます(準備中)'
'MisskeyはMIT Licenseです'
]
@on \mount ~> @on \unmount ~>
@set! clear-interval @clock
@clock = set-interval @change, 20000ms
@on \unmount ~> @set = ~>
clear-interval @clock @refs.text.innerHTML = @tips[Math.floor Math.random! * @tips.length]
@update!
@set = ~> @change = ~>
@refs.text.innerHTML = @tips[Math.floor Math.random! * @tips.length] Velocity @refs.tip, {
@update! opacity: 0
} {
duration: 500ms
easing: \linear
complete: @set
}
@change = ~> Velocity @refs.tip, {
Velocity @refs.tip, { opacity: 1
opacity: 0 } {
} { duration: 500ms
duration: 500ms easing: \linear
easing: \linear }
complete: @set </script>
} </mk-tips-home-widget>
Velocity @refs.tip, {
opacity: 1
} {
duration: 500ms
easing: \linear
}

View file

@ -1,154 +1,152 @@
mk-user-recommendation-home-widget <mk-user-recommendation-home-widget>
p.title <p class="title"><i class="fa fa-users"></i>おすすめユーザー</p>
i.fa.fa-users <button onclick="{ refresh }" title="他を見る"><i class="fa fa-refresh"></i></button>
| おすすめユーザー <div class="user" if="{ !loading &amp;&amp; users.length != 0 }" each="{ _user in users }"><a class="avatar-anchor" href="{ CONFIG.url + '/' + _user.username }"><img class="avatar" src="{ _user.avatar_url + '?thumbnail&amp;size=42' }" alt="" data-user-preview="{ _user.id }"/></a>
button(onclick={ refresh }, title='他を見る'): i.fa.fa-refresh <div class="body"><a class="name" href="{ CONFIG.url + '/' + _user.username }" data-user-preview="{ _user.id }">{ _user.name }</a>
div.user(if={ !loading && users.length != 0 }, each={ _user in users }) <p class="username">@{ _user.username }</p>
a.avatar-anchor(href={ CONFIG.url + '/' + _user.username }) </div>
img.avatar(src={ _user.avatar_url + '?thumbnail&size=42' }, alt='', data-user-preview={ _user.id }) <mk-follow-button user="{ _user }"></mk-follow-button>
div.body </div>
a.name(href={ CONFIG.url + '/' + _user.username }, data-user-preview={ _user.id }) { _user.name } <p class="empty" if="{ !loading &amp;&amp; users.length == 0 }">いません!</p>
p.username @{ _user.username } <p class="loading" if="{ loading }"><i class="fa fa-spinner fa-pulse fa-fw"></i>読み込んでいます
mk-follow-button(user={ _user }) <mk-ellipsis></mk-ellipsis>
p.empty(if={ !loading && users.length == 0 }) </p>
| いません! <style type="stylus">
p.loading(if={ loading }) :scope
i.fa.fa-spinner.fa-pulse.fa-fw
| 読み込んでいます
mk-ellipsis
style.
display block
background #fff
> .title
margin 0
padding 0 16px
line-height 42px
font-size 0.9em
font-weight bold
color #888
border-bottom solid 1px #eee
> 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
> .user
padding 16px
border-bottom solid 1px #eee
&:last-child
border-bottom none
&:after
content ""
display block display block
clear both background #fff
> .avatar-anchor > .title
display block margin 0
float left padding 0 16px
margin 0 12px 0 0 line-height 42px
font-size 0.9em
font-weight bold
color #888
border-bottom solid 1px #eee
> .avatar > i
display block margin-right 4px
> button
position absolute
z-index 2
top 0
right 0
padding 0
width 42px width 42px
height 42px font-size 0.9em
margin 0 line-height 42px
border-radius 8px
vertical-align bottom
> .body
float left
width calc(100% - 54px)
> .name
margin 0
font-size 16px
line-height 24px
color #555
> .username
display block
margin 0
font-size 15px
line-height 16px
color #ccc color #ccc
> mk-follow-button &:hover
position absolute color #aaa
top 16px
right 16px
> .empty &:active
margin 0 color #999
padding 16px
text-align center
color #aaa
> .loading > .user
margin 0 padding 16px
padding 16px border-bottom solid 1px #eee
text-align center
color #aaa
> i &:last-child
margin-right 4px border-bottom none
script. &:after
@mixin \api content ""
@mixin \user-preview display block
clear both
@users = null > .avatar-anchor
@loading = true display block
float left
margin 0 12px 0 0
@limit = 3users > .avatar
@page = 0 display block
width 42px
height 42px
margin 0
border-radius 8px
vertical-align bottom
@on \mount ~> > .body
@fetch! float left
@clock = set-interval ~> width calc(100% - 54px)
if @users.length < @limit
@fetch true
, 60000ms
@on \unmount ~> > .name
clear-interval @clock margin 0
font-size 16px
line-height 24px
color #555
> .username
display block
margin 0
font-size 15px
line-height 16px
color #ccc
> mk-follow-button
position absolute
top 16px
right 16px
> .empty
margin 0
padding 16px
text-align center
color #aaa
> .loading
margin 0
padding 16px
text-align center
color #aaa
> i
margin-right 4px
</style>
<script>
@mixin \api
@mixin \user-preview
@fetch = (quiet = false) ~>
@loading = true
@users = null @users = null
if not quiet then @update! @loading = true
@api \users/recommendation do
limit: @limit
offset: @limit * @page
.then (users) ~>
@loading = false
@users = users
@update!
.catch (err, text-status) ->
console.error err
@refresh = ~> @limit = 3users
if @users.length < @limit @page = 0
@page = 0
else @on \mount ~>
@page++ @fetch!
@fetch! @clock = set-interval ~>
if @users.length < @limit
@fetch true
, 60000ms
@on \unmount ~>
clear-interval @clock
@fetch = (quiet = false) ~>
@loading = true
@users = null
if not quiet then @update!
@api \users/recommendation do
limit: @limit
offset: @limit * @page
.then (users) ~>
@loading = false
@users = users
@update!
.catch (err, text-status) ->
console.error err
@refresh = ~>
if @users.length < @limit
@page = 0
else
@page++
@fetch!
</script>
</mk-user-recommendation-home-widget>

View file

@ -1,86 +1,91 @@
mk-home <mk-home>
div.main <div class="main">
div.left@left <div class="left" ref="left"></div>
main <main>
mk-timeline-home-widget@tl(if={ mode == 'timeline' }) <mk-timeline-home-widget ref="tl" if="{ mode == 'timeline' }"></mk-timeline-home-widget>
mk-mentions-home-widget@tl(if={ mode == 'mentions' }) <mk-mentions-home-widget ref="tl" if="{ mode == 'mentions' }"></mk-mentions-home-widget>
div.right@right </main>
mk-detect-slow-internet-connection-notice <div class="right" ref="right"></div>
</div>
style. <mk-detect-slow-internet-connection-notice></mk-detect-slow-internet-connection-notice>
display block <style type="stylus">
:scope
> .main
margin 0 auto
max-width 1200px
&:after
content ""
display block display block
clear both
> * > .main
float left
> *
display block
//border solid 1px #eaeaea
border solid 1px rgba(0, 0, 0, 0.075)
border-radius 6px
overflow hidden
&:not(:last-child)
margin-bottom 16px
> main
padding 16px
width calc(100% - 275px * 2)
> *:not(main)
width 275px
> .left
padding 16px 0 16px 16px
> .right
padding 16px 16px 16px 0
@media (max-width 1100px)
> *:not(main)
display none
> main
float none
width 100%
max-width 700px
margin 0 auto margin 0 auto
max-width 1200px
script. &:after
@mixin \i content ""
@mode = @opts.mode || \timeline display block
clear both
# https://github.com/riot/riot/issues/2080 > *
if @mode == '' then @mode = \timeline float left
@home = [] > *
display block
//border solid 1px #eaeaea
border solid 1px rgba(0, 0, 0, 0.075)
border-radius 6px
overflow hidden
@on \mount ~> &:not(:last-child)
@refs.tl.on \loaded ~> margin-bottom 16px
@trigger \loaded
@I.data.home.for-each (widget) ~> > main
try padding 16px
el = document.create-element \mk- + widget.name + \-home-widget width calc(100% - 275px * 2)
switch widget.place
| \left => @refs.left.append-child el
| \right => @refs.right.append-child el
@home.push (riot.mount el, do
id: widget.id
data: widget.data
.0)
catch e
# noop
@on \unmount ~> > *:not(main)
@home.for-each (widget) ~> width 275px
widget.unmount!
> .left
padding 16px 0 16px 16px
> .right
padding 16px 16px 16px 0
@media (max-width 1100px)
> *:not(main)
display none
> main
float none
width 100%
max-width 700px
margin 0 auto
</style>
<script>
@mixin \i
@mode = @opts.mode || \timeline
# https://github.com/riot/riot/issues/2080
if @mode == '' then @mode = \timeline
@home = []
@on \mount ~>
@refs.tl.on \loaded ~>
@trigger \loaded
@I.data.home.for-each (widget) ~>
try
el = document.create-element \mk- + widget.name + \-home-widget
switch widget.place
| \left => @refs.left.append-child el
| \right => @refs.right.append-child el
@home.push (riot.mount el, do
id: widget.id
data: widget.data
.0)
catch e
# noop
@on \unmount ~>
@home.for-each (widget) ~>
widget.unmount!
</script>
</mk-home>

View file

@ -1,73 +1,75 @@
mk-image-dialog <mk-image-dialog>
div.bg@bg(onclick={ close }) <div class="bg" ref="bg" onclick="{ close }"></div><img ref="img" src="{ image.url }" alt="{ image.name }" title="{ image.name }" onclick="{ close }"/>
img@img(src={ image.url }, alt={ image.name }, title={ image.name }, onclick={ close }) <style type="stylus">
:scope
display block
position fixed
z-index 2048
top 0
left 0
width 100%
height 100%
opacity 0
style. > .bg
display block display block
position fixed position fixed
z-index 2048 z-index 1
top 0 top 0
left 0 left 0
width 100% width 100%
height 100% height 100%
opacity 0 background rgba(0, 0, 0, 0.7)
> .bg > img
display block position fixed
position fixed z-index 2
z-index 1 top 0
top 0 right 0
left 0 bottom 0
width 100% left 0
height 100% max-width 100%
background rgba(0, 0, 0, 0.7) max-height 100%
margin auto
cursor zoom-out
> img </style>
position fixed <script>
z-index 2 @image = @opts.image
top 0
right 0
bottom 0
left 0
max-width 100%
max-height 100%
margin auto
cursor zoom-out
script. @on \mount ~>
@image = @opts.image Velocity @root, {
opacity: 1
} {
duration: 100ms
easing: \linear
}
@on \mount ~> #Velocity @img, {
Velocity @root, { # scale: 1
opacity: 1 # opacity: 1
} { #} {
duration: 100ms # duration: 200ms
easing: \linear # easing: \ease-out
} #}
#Velocity @img, { @close = ~>
# scale: 1 Velocity @root, {
# opacity: 1 opacity: 0
#} { } {
# duration: 200ms duration: 100ms
# easing: \ease-out easing: \linear
#} complete: ~> @unmount!
}
@close = ~> #Velocity @img, {
Velocity @root, { # scale: 0.9
opacity: 0 # opacity: 0
} { #} {
duration: 100ms # duration: 200ms
easing: \linear # easing: \ease-in
complete: ~> @unmount! # complete: ~>
} # @unmount!
#}
#Velocity @img, { </script>
# scale: 0.9 </mk-image-dialog>
# opacity: 0
#} {
# duration: 200ms
# easing: \ease-in
# complete: ~>
# @unmount!
#}

View file

@ -1,43 +1,45 @@
mk-images-viewer <mk-images-viewer>
div.image@view(onmousemove={ mousemove }, style={ 'background-image: url(' + image.url + '?thumbnail' }, onclick={ click }) <div class="image" ref="view" onmousemove="{ mousemove }" style="{ 'background-image: url(' + image.url + '?thumbnail' }" onclick="{ click }"><img src="{ image.url + '?thumbnail&amp;size=512' }" alt="{ image.name }" title="{ image.name }"/></div>
img(src={ image.url + '?thumbnail&size=512' }, alt={ image.name }, title={ image.name }) <style type="stylus">
:scope
style.
display block
padding 8px
overflow hidden
box-shadow 0 0 4px rgba(0, 0, 0, 0.2)
border-radius 4px
> .image
cursor zoom-in
> img
display block display block
max-height 256px padding 8px
max-width 100% overflow hidden
margin 0 auto box-shadow 0 0 4px rgba(0, 0, 0, 0.2)
border-radius 4px
&:hover > .image
> img cursor zoom-in
visibility hidden
&:not(:hover) > img
background-image none !important display block
max-height 256px
max-width 100%
margin 0 auto
script. &:hover
@images = @opts.images > img
@image = @images.0 visibility hidden
@mousemove = (e) ~> &:not(:hover)
rect = @refs.view.get-bounding-client-rect! background-image none !important
mouse-x = e.client-x - rect.left
mouse-y = e.client-y - rect.top
xp = mouse-x / @refs.view.offset-width * 100
yp = mouse-y / @refs.view.offset-height * 100
@refs.view.style.background-position = xp + '% ' + yp + '%'
@click = ~> </style>
dialog = document.body.append-child document.create-element \mk-image-dialog <script>
riot.mount dialog, do @images = @opts.images
image: @image @image = @images.0
@mousemove = (e) ~>
rect = @refs.view.get-bounding-client-rect!
mouse-x = e.client-x - rect.left
mouse-y = e.client-y - rect.top
xp = mouse-x / @refs.view.offset-width * 100
yp = mouse-y / @refs.view.offset-height * 100
@refs.view.style.background-position = xp + '% ' + yp + '%'
@click = ~>
dialog = document.body.append-child document.create-element \mk-image-dialog
riot.mount dialog, do
image: @image
</script>
</mk-images-viewer>

View file

@ -1,156 +1,157 @@
mk-input-dialog <mk-input-dialog>
mk-window@window(is-modal={ true }, width={ '500px' }) <mk-window ref="window" is-modal="{ true }" width="{ '500px' }"><yield to="header"><i class="fa fa-i-cursor"></i>{ parent.title }</yield>
<yield to="header"> <yield to="content">
i.fa.fa-i-cursor <div class="body">
| { parent.title } <input ref="text" oninput="{ parent.update }" onkeydown="{ parent.onKeydown }" placeholder="{ parent.placeholder }"/>
</yield> </div>
<yield to="content"> <div class="action">
div.body <button class="cancel" onclick="{ parent.cancel }">キャンセル</button>
input@text(oninput={ parent.update }, onkeydown={ parent.on-keydown }, placeholder={ parent.placeholder }) <button class="ok" disabled="{ !parent.allowEmpty &amp;&amp; refs.text.value.length == 0 }" onclick="{ parent.ok }">決定</button>
div.action </div></yield>
button.cancel(onclick={ parent.cancel }) キャンセル </mk-window>
button.ok(disabled={ !parent.allow-empty && refs.text.value.length == 0 }, onclick={ parent.ok }) 決定 <style type="stylus">
</yield> :scope
display block
style. > mk-window
display block [data-yield='header']
> i
margin-right 4px
> mk-window [data-yield='content']
[data-yield='header'] > .body
> i padding 16px
margin-right 4px
[data-yield='content'] > input
> .body display block
padding 16px padding 8px
margin 0
width 100%
max-width 100%
min-width 100%
font-size 1em
color #333
background #fff
outline none
border solid 1px rgba($theme-color, 0.1)
border-radius 4px
transition border-color .3s ease
> input &:hover
display block border-color rgba($theme-color, 0.2)
padding 8px transition border-color .1s ease
margin 0
width 100%
max-width 100%
min-width 100%
font-size 1em
color #333
background #fff
outline none
border solid 1px rgba($theme-color, 0.1)
border-radius 4px
transition border-color .3s ease
&:hover &:focus
border-color rgba($theme-color, 0.2) color $theme-color
transition border-color .1s ease border-color rgba($theme-color, 0.5)
transition border-color 0s ease
&:focus &::-webkit-input-placeholder
color $theme-color color rgba($theme-color, 0.3)
border-color rgba($theme-color, 0.5)
transition border-color 0s ease
&::-webkit-input-placeholder > .action
color rgba($theme-color, 0.3) height 72px
background lighten($theme-color, 95%)
> .action .ok
height 72px .cancel
background lighten($theme-color, 95%) display block
.ok
.cancel
display block
position absolute
bottom 16px
cursor pointer
padding 0
margin 0
width 120px
height 40px
font-size 1em
outline none
border-radius 4px
&:focus
&:after
content ""
pointer-events none
position absolute position absolute
top -5px bottom 16px
right -5px cursor pointer
bottom -5px padding 0
left -5px margin 0
border 2px solid rgba($theme-color, 0.3) width 120px
border-radius 8px height 40px
font-size 1em
outline none
border-radius 4px
&:disabled &:focus
opacity 0.7 &:after
cursor default content ""
pointer-events none
position absolute
top -5px
right -5px
bottom -5px
left -5px
border 2px solid rgba($theme-color, 0.3)
border-radius 8px
.ok &:disabled
right 16px opacity 0.7
color $theme-color-foreground cursor default
background linear-gradient(to bottom, lighten($theme-color, 25%) 0%, lighten($theme-color, 10%) 100%)
border solid 1px lighten($theme-color, 15%)
&:not(:disabled) .ok
font-weight bold right 16px
color $theme-color-foreground
background linear-gradient(to bottom, lighten($theme-color, 25%) 0%, lighten($theme-color, 10%) 100%)
border solid 1px lighten($theme-color, 15%)
&:hover:not(:disabled) &:not(:disabled)
background linear-gradient(to bottom, lighten($theme-color, 8%) 0%, darken($theme-color, 8%) 100%) font-weight bold
border-color $theme-color
&:active:not(:disabled) &:hover:not(:disabled)
background $theme-color background linear-gradient(to bottom, lighten($theme-color, 8%) 0%, darken($theme-color, 8%) 100%)
border-color $theme-color border-color $theme-color
.cancel &:active:not(:disabled)
right 148px background $theme-color
color #888 border-color $theme-color
background linear-gradient(to bottom, #ffffff 0%, #f5f5f5 100%)
border solid 1px #e2e2e2
&:hover .cancel
background linear-gradient(to bottom, #f9f9f9 0%, #ececec 100%) right 148px
border-color #dcdcdc color #888
background linear-gradient(to bottom, #ffffff 0%, #f5f5f5 100%)
border solid 1px #e2e2e2
&:active &:hover
background #ececec background linear-gradient(to bottom, #f9f9f9 0%, #ececec 100%)
border-color #dcdcdc border-color #dcdcdc
script. &:active
@done = false background #ececec
border-color #dcdcdc
@title = @opts.title </style>
@placeholder = @opts.placeholder <script>
@default = @opts.default
@allow-empty = if @opts.allow-empty? then @opts.allow-empty else true
@on \mount ~>
@text = @refs.window.refs.text
if @default?
@text.value = @default
@text.focus!
@refs.window.on \closing ~>
if @done
@opts.on-ok @text.value
else
if @opts.on-cancel?
@opts.on-cancel!
@refs.window.on \closed ~>
@unmount!
@cancel = ~>
@done = false @done = false
@refs.window.close!
@ok = ~> @title = @opts.title
if not @allow-empty and @text.value == '' then return @placeholder = @opts.placeholder
@done = true @default = @opts.default
@refs.window.close! @allow-empty = if @opts.allow-empty? then @opts.allow-empty else true
@on-keydown = (e) ~> @on \mount ~>
if e.which == 13 # Enter @text = @refs.window.refs.text
e.prevent-default! if @default?
e.stop-propagation! @text.value = @default
@ok! @text.focus!
@refs.window.on \closing ~>
if @done
@opts.on-ok @text.value
else
if @opts.on-cancel?
@opts.on-cancel!
@refs.window.on \closed ~>
@unmount!
@cancel = ~>
@done = false
@refs.window.close!
@ok = ~>
if not @allow-empty and @text.value == '' then return
@done = true
@refs.window.close!
@on-keydown = (e) ~>
if e.which == 13 # Enter
e.prevent-default!
e.stop-propagation!
@ok!
</script>
</mk-input-dialog>

View file

@ -1,100 +1,98 @@
mk-list-user <mk-list-user><a class="avatar-anchor" href="{ CONFIG.url + '/' + user.username }"><img class="avatar" src="{ user.avatar_url + '?thumbnail&amp;size=64' }" alt="avatar"/></a>
a.avatar-anchor(href={ CONFIG.url + '/' + user.username }) <div class="main">
img.avatar(src={ user.avatar_url + '?thumbnail&size=64' }, alt='avatar') <header>
div.main <div class="left"><a class="name" href="{ CONFIG.url + '/' + user.username }">{ user.name }</a><span class="username">@{ user.username }</span></div>
header </header>
div.left <div class="body">
a.name(href={ CONFIG.url + '/' + user.username }) <p class="followed" if="{ user.is_followed }">フォローされています</p>
| { user.name } <div class="bio">{ user.bio }</div>
span.username </div>
| @{ user.username } </div>
div.body <mk-follow-button user="{ user }"></mk-follow-button>
p.followed(if={ user.is_followed }) フォローされています <style type="stylus">
div.bio { user.bio } :scope
mk-follow-button(user={ user })
style.
display block
margin 0
padding 16px
font-size 16px
&:after
content ""
display block
clear both
> .avatar-anchor
display block
float left
margin 0 16px 0 0
> .avatar
display block display block
width 58px
height 58px
margin 0 margin 0
border-radius 8px padding 16px
vertical-align bottom font-size 16px
> .main
float left
width calc(100% - 74px)
> header
margin-bottom 2px
white-space nowrap
&:after &:after
content "" content ""
display block display block
clear both clear both
> .left > .avatar-anchor
float left
> .name
display inline
margin 0
padding 0
color #777
font-size 1em
font-weight 700
text-align left
text-decoration none
&:hover
text-decoration underline
> .username
text-align left
margin 0 0 0 8px
color #ccc
> .body
> .followed
display inline-block
margin 0 0 4px 0
padding 2px 8px
vertical-align top
font-size 10px
color #71afc7
background #eefaff
border-radius 4px
> .bio
cursor default
display block display block
margin 0 float left
padding 0 margin 0 16px 0 0
word-wrap break-word
font-size 1.1em
color #717171
> mk-follow-button > .avatar
position absolute display block
top 16px width 58px
right 16px height 58px
margin 0
border-radius 8px
vertical-align bottom
script. > .main
@user = @opts.user float left
width calc(100% - 74px)
> header
margin-bottom 2px
white-space nowrap
&:after
content ""
display block
clear both
> .left
float left
> .name
display inline
margin 0
padding 0
color #777
font-size 1em
font-weight 700
text-align left
text-decoration none
&:hover
text-decoration underline
> .username
text-align left
margin 0 0 0 8px
color #ccc
> .body
> .followed
display inline-block
margin 0 0 4px 0
padding 2px 8px
vertical-align top
font-size 10px
color #71afc7
background #eefaff
border-radius 4px
> .bio
cursor default
display block
margin 0
padding 0
word-wrap break-word
font-size 1.1em
color #717171
> mk-follow-button
position absolute
top 16px
right 16px
</style>
<script>@user = @opts.user</script>
</mk-list-user>

View file

@ -1,162 +1,161 @@
mk-messaging-form <mk-messaging-form>
textarea@text(onkeypress={ onkeypress }, onpaste={ onpaste }, placeholder='ここにメッセージを入力') <textarea ref="text" onkeypress="{ onkeypress }" onpaste="{ onpaste }" placeholder="ここにメッセージを入力"></textarea>
div.files <div class="files"></div>
mk-uploader@uploader <mk-uploader ref="uploader"></mk-uploader>
button.send(onclick={ send }, disabled={ sending }, title='メッセージを送信') <button class="send" onclick="{ send }" disabled="{ sending }" title="メッセージを送信"><i class="fa fa-paper-plane" if="{ !sending }"></i><i class="fa fa-spinner fa-spin" if="{ sending }"></i></button>
i.fa.fa-paper-plane(if={ !sending }) <button class="attach-from-local" type="button" title="PCから画像を添付する"><i class="fa fa-upload"></i></button>
i.fa.fa-spinner.fa-spin(if={ sending }) <button class="attach-from-drive" type="button" title="アルバムから画像を添付する"><i class="fa fa-folder-open"></i></button>
button.attach-from-local(type='button', title='PCから画像を添付する') <input name="file" type="file" accept="image/*"/>
i.fa.fa-upload <style type="stylus">
button.attach-from-drive(type='button', title='アルバムから画像を添付する') :scope
i.fa.fa-folder-open
input(name='file', type='file', accept='image/*')
style.
display block
> textarea
cursor auto
display block
width 100%
min-width 100%
max-width 100%
height 64px
margin 0
padding 8px
font-size 1em
color #000
outline none
border none
border-top solid 1px #eee
border-radius 0
box-shadow none
background transparent
> .send
position absolute
bottom 0
right 0
margin 0
padding 10px 14px
line-height 1em
font-size 1em
color #aaa
transition color 0.1s ease
&:hover
color $theme-color
&:active
color darken($theme-color, 10%)
transition color 0s ease
.files
display block
margin 0
padding 0 8px
list-style none
&:after
content ''
display block display block
clear both
> li > textarea
display block cursor auto
float left display block
margin 4px width 100%
padding 0 min-width 100%
width 64px max-width 100%
height 64px height 64px
background-color #eee
background-repeat no-repeat
background-position center center
background-size cover
cursor move
&:hover
> .remove
display block
> .remove
display none
position absolute
right -6px
top -6px
margin 0 margin 0
padding 0 padding 8px
background transparent font-size 1em
color #000
outline none outline none
border none border none
border-top solid 1px #eee
border-radius 0 border-radius 0
box-shadow none box-shadow none
cursor pointer background transparent
.attach-from-local > .send
.attach-from-drive position absolute
margin 0 bottom 0
padding 10px 14px right 0
line-height 1em margin 0
font-size 1em padding 10px 14px
font-weight normal line-height 1em
text-decoration none font-size 1em
color #aaa color #aaa
transition color 0.1s ease transition color 0.1s ease
&:hover &:hover
color $theme-color color $theme-color
&:active &:active
color darken($theme-color, 10%) color darken($theme-color, 10%)
transition color 0s ease transition color 0s ease
input[type=file] .files
display none display block
margin 0
padding 0 8px
list-style none
script. &:after
@mixin \api content ''
display block
clear both
@user = @opts.user > li
display block
float left
margin 4px
padding 0
width 64px
height 64px
background-color #eee
background-repeat no-repeat
background-position center center
background-size cover
cursor move
@onpaste = (e) ~> &:hover
data = e.clipboard-data > .remove
items = data.items display block
for i from 0 to items.length - 1
item = items[i]
switch (item.kind)
| \file =>
@upload item.get-as-file!
@onkeypress = (e) ~> > .remove
if (e.which == 10 || e.which == 13) && e.ctrl-key display none
@send! position absolute
right -6px
top -6px
margin 0
padding 0
background transparent
outline none
border none
border-radius 0
box-shadow none
cursor pointer
@select-file = ~> .attach-from-local
@refs.file.click! .attach-from-drive
margin 0
padding 10px 14px
line-height 1em
font-size 1em
font-weight normal
text-decoration none
color #aaa
transition color 0.1s ease
@select-file-from-drive = ~> &:hover
browser = document.body.append-child document.create-element \mk-select-file-from-drive-window color $theme-color
event = riot.observable!
riot.mount browser, do
multiple: true
event: event
event.one \selected (files) ~>
files.for-each @add-file
@send = ~> &:active
@sending = true color darken($theme-color, 10%)
@api \messaging/messages/create do transition color 0s ease
user_id: @user.id
text: @refs.text.value input[type=file]
.then (message) ~> display none
@clear!
.catch (err) ~> </style>
console.error err <script>
.then ~> @mixin \api
@sending = false
@user = @opts.user
@onpaste = (e) ~>
data = e.clipboard-data
items = data.items
for i from 0 to items.length - 1
item = items[i]
switch (item.kind)
| \file =>
@upload item.get-as-file!
@onkeypress = (e) ~>
if (e.which == 10 || e.which == 13) && e.ctrl-key
@send!
@select-file = ~>
@refs.file.click!
@select-file-from-drive = ~>
browser = document.body.append-child document.create-element \mk-select-file-from-drive-window
event = riot.observable!
riot.mount browser, do
multiple: true
event: event
event.one \selected (files) ~>
files.for-each @add-file
@send = ~>
@sending = true
@api \messaging/messages/create do
user_id: @user.id
text: @refs.text.value
.then (message) ~>
@clear!
.catch (err) ~>
console.error err
.then ~>
@sending = false
@update!
@clear = ~>
@refs.text.value = ''
@files = []
@update! @update!
</script>
@clear = ~> </mk-messaging-form>
@refs.text.value = ''
@files = []
@update!

View file

@ -1,302 +1,301 @@
mk-messaging <mk-messaging>
div.search <div class="search">
div.form <div class="form">
label(for='search-input') <label for="search-input"><i class="fa fa-search"></i></label>
i.fa.fa-search <input ref="searchInput" type="search" oninput="{ search }" placeholder="ユーザーを探す"/>
input@search-input(type='search', oninput={ search }, placeholder='ユーザーを探す') </div>
div.result <div class="result">
ol.users(if={ search-result.length > 0 }) <ol class="users" if="{ searchResult.length &gt; 0 }">
li(each={ user in search-result }) <li each="{ user in searchResult }"><a onclick="{ user._click }"><img class="avatar" src="{ user.avatar_url + '?thumbnail&amp;size=32' }" alt=""/><span class="name">{ user.name }</span><span class="username">@{ user.username }</span></a></li>
a(onclick={ user._click }) </ol>
img.avatar(src={ user.avatar_url + '?thumbnail&size=32' }, alt='') </div>
span.name { user.name } </div>
span.username @{ user.username } <div class="main">
div.main <div class="history" if="{ history.length &gt; 0 }">
div.history(if={ history.length > 0 }) <virtual each="{ history }"><a class="user" data-is-me="{ is_me }" data-is-read="{ is_read }" onclick="{ _click }">
virtual(each={ history }) <div><img class="avatar" src="{ (is_me ? recipient.avatar_url : user.avatar_url) + '?thumbnail&amp;size=64' }" alt=""/>
a.user(data-is-me={ is_me }, data-is-read={ is_read }, onclick={ _click }): div <header><span class="name">{ is_me ? recipient.name : user.name }</span><span class="username">{ '@' + (is_me ? recipient.username : user.username ) }</span>
img.avatar(src={ (is_me ? recipient.avatar_url : user.avatar_url) + '?thumbnail&size=64' }, alt='') <mk-time time="{ created_at }"></mk-time>
header </header>
span.name { is_me ? recipient.name : user.name } <div class="body">
span.username { '@' + (is_me ? recipient.username : user.username ) } <p class="text"><span class="me" if="{ is_me }">あなた:</span>{ text }</p>
mk-time(time={ created_at }) </div>
div.body </div></a></virtual>
p.text </div>
span.me(if={ is_me }) あなた: <p class="no-history" if="{ history.length == 0 }">履歴はありません。<br/>ユーザーを検索して、いつでもメッセージを送受信できます。</p>
| { text } </div>
p.no-history(if={ history.length == 0 }) <style type="stylus">
| 履歴はありません。 :scope
br display block
| ユーザーを検索して、いつでもメッセージを送受信できます。
style. > .search
display block
> .search
display block
position absolute
top 0
left 0
z-index 1
width 100%
background #fff
box-shadow 0 0px 2px rgba(0, 0, 0, 0.2)
> .form
padding 8px
background #f7f7f7
> label
display block display block
position absolute position absolute
top 0 top 0
left 8px left 0
z-index 1 z-index 1
height 100%
width 38px
pointer-events none
> i
display block
position absolute
top 0
right 0
bottom 0
left 0
width 1em
height 1em
margin auto
color #555
> input
margin 0
padding 0 12px 0 38px
width 100% width 100%
font-size 1em
line-height 38px
color #000
outline none
border solid 1px #eee
border-radius 5px
box-shadow none
transition color 0.5s ease, border 0.5s ease
&:hover
border solid 1px #ddd
transition border 0.2s ease
&:focus
color darken($theme-color, 20%)
border solid 1px $theme-color
transition color 0, border 0
> .result
display block
top 0
left 0
z-index 2
width 100%
margin 0
padding 0
background #fff
> .users
margin 0
padding 0
list-style none
> li
> a
display inline-block
z-index 1
width 100%
padding 8px 32px
vertical-align top
white-space nowrap
overflow hidden
color rgba(0, 0, 0, 0.8)
text-decoration none
transition none
&:hover
color #fff
background $theme-color
.name
color #fff
.username
color #fff
&:active
color #fff
background darken($theme-color, 10%)
.name
color #fff
.username
color #fff
.avatar
vertical-align middle
min-width 32px
min-height 32px
max-width 32px
max-height 32px
margin 0 8px 0 0
border-radius 6px
.name
margin 0 8px 0 0
/*font-weight bold*/
font-weight normal
color rgba(0, 0, 0, 0.8)
.username
font-weight normal
color rgba(0, 0, 0, 0.3)
> .main
padding-top 56px
> .history
> a
display block
padding 20px 30px
text-decoration none
background #fff background #fff
border-bottom solid 1px #eee box-shadow 0 0px 2px rgba(0, 0, 0, 0.2)
* > .form
pointer-events none padding 8px
user-select none background #f7f7f7
&:hover > label
background #fafafa display block
position absolute
top 0
left 8px
z-index 1
height 100%
width 38px
pointer-events none
> .avatar > i
filter saturate(200%) display block
&:active
background #eee
&[data-is-read]
&[data-is-me]
opacity 0.8
&:not([data-is-me]):not([data-is-read])
background-image url("/_/resources/desktop/unread.svg")
background-repeat no-repeat
background-position 0 center
&:after
content ""
display block
clear both
> div
max-width 500px
margin 0 auto
> header
margin-bottom 2px
white-space nowrap
overflow hidden
> .name
text-align left
display inline
margin 0
padding 0
font-size 1em
color rgba(0, 0, 0, 0.9)
font-weight bold
transition all 0.1s ease
> .username
text-align left
margin 0 0 0 8px
color rgba(0, 0, 0, 0.5)
> mk-time
position absolute position absolute
top 0 top 0
right 0 right 0
display inline bottom 0
color rgba(0, 0, 0, 0.5) left 0
font-size small width 1em
height 1em
margin auto
color #555
> .avatar > input
float left margin 0
width 54px padding 0 12px 0 38px
height 54px width 100%
margin 0 16px 0 0 font-size 1em
border-radius 8px line-height 38px
transition all 0.1s ease color #000
outline none
border solid 1px #eee
border-radius 5px
box-shadow none
transition color 0.5s ease, border 0.5s ease
> .body &:hover
border solid 1px #ddd
transition border 0.2s ease
> .text &:focus
color darken($theme-color, 20%)
border solid 1px $theme-color
transition color 0, border 0
> .result
display block
top 0
left 0
z-index 2
width 100%
margin 0
padding 0
background #fff
> .users
margin 0
padding 0
list-style none
> li
> a
display inline-block
z-index 1
width 100%
padding 8px 32px
vertical-align top
white-space nowrap
overflow hidden
color rgba(0, 0, 0, 0.8)
text-decoration none
transition none
&:hover
color #fff
background $theme-color
.name
color #fff
.username
color #fff
&:active
color #fff
background darken($theme-color, 10%)
.name
color #fff
.username
color #fff
.avatar
vertical-align middle
min-width 32px
min-height 32px
max-width 32px
max-height 32px
margin 0 8px 0 0
border-radius 6px
.name
margin 0 8px 0 0
/*font-weight bold*/
font-weight normal
color rgba(0, 0, 0, 0.8)
.username
font-weight normal
color rgba(0, 0, 0, 0.3)
> .main
padding-top 56px
> .history
> a
display block
padding 20px 30px
text-decoration none
background #fff
border-bottom solid 1px #eee
*
pointer-events none
user-select none
&:hover
background #fafafa
> .avatar
filter saturate(200%)
&:active
background #eee
&[data-is-read]
&[data-is-me]
opacity 0.8
&:not([data-is-me]):not([data-is-read])
background-image url("/_/resources/desktop/unread.svg")
background-repeat no-repeat
background-position 0 center
&:after
content ""
display block display block
margin 0 0 0 0 clear both
padding 0
overflow hidden
word-wrap break-word
font-size 1.1em
color rgba(0, 0, 0, 0.8)
.me > div
color rgba(0, 0, 0, 0.4) max-width 500px
margin 0 auto
> .image > header
display block margin-bottom 2px
max-width 100% white-space nowrap
max-height 512px overflow hidden
> .no-history > .name
margin 0 text-align left
padding 2em 1em display inline
text-align center margin 0
color #999 padding 0
font-weight 500 font-size 1em
color rgba(0, 0, 0, 0.9)
font-weight bold
transition all 0.1s ease
script. > .username
@mixin \i text-align left
@mixin \api margin 0 0 0 8px
color rgba(0, 0, 0, 0.5)
@search-result = [] > mk-time
position absolute
top 0
right 0
display inline
color rgba(0, 0, 0, 0.5)
font-size small
@on \mount ~> > .avatar
@api \messaging/history float left
.then (history) ~> width 54px
@is-loading = false height 54px
history.for-each (message) ~> margin 0 16px 0 0
message.is_me = message.user_id == @I.id border-radius 8px
message._click = ~> transition all 0.1s ease
if message.is_me
@trigger \navigate-user message.recipient
else
@trigger \navigate-user message.user
@history = history
@update!
.catch (err) ~>
console.error err
@search = ~> > .body
q = @refs.search-input.value
if q == '' > .text
@search-result = [] display block
else margin 0 0 0 0
@api \users/search do padding 0
query: q overflow hidden
.then (users) ~> word-wrap break-word
users.for-each (user) ~> font-size 1.1em
user._click = ~> color rgba(0, 0, 0, 0.8)
@trigger \navigate-user user
@search-result = [] .me
@search-result = users color rgba(0, 0, 0, 0.4)
> .image
display block
max-width 100%
max-height 512px
> .no-history
margin 0
padding 2em 1em
text-align center
color #999
font-weight 500
</style>
<script>
@mixin \i
@mixin \api
@search-result = []
@on \mount ~>
@api \messaging/history
.then (history) ~>
@is-loading = false
history.for-each (message) ~>
message.is_me = message.user_id == @I.id
message._click = ~>
if message.is_me
@trigger \navigate-user message.recipient
else
@trigger \navigate-user message.user
@history = history
@update! @update!
.catch (err) ~> .catch (err) ~>
console.error err console.error err
@search = ~>
q = @refs.search-input.value
if q == ''
@search-result = []
else
@api \users/search do
query: q
.then (users) ~>
users.for-each (user) ~>
user._click = ~>
@trigger \navigate-user user
@search-result = []
@search-result = users
@update!
.catch (err) ~>
console.error err
</script>
</mk-messaging>

View file

@ -1,227 +1,230 @@
mk-messaging-message(data-is-me={ message.is_me }) <mk-messaging-message data-is-me="{ message.is_me }"><a class="avatar-anchor" href="{ CONFIG.url + '/' + message.user.username }" title="{ message.user.username }" target="_blank"><img class="avatar" src="{ message.user.avatar_url + '?thumbnail&amp;size=64' }" alt=""/></a>
a.avatar-anchor(href={ CONFIG.url + '/' + message.user.username }, title={ message.user.username }, target='_blank') <div class="content-container">
img.avatar(src={ message.user.avatar_url + '?thumbnail&size=64' }, alt='') <div class="balloon">
div.content-container <p class="read" if="{ message.is_me &amp;&amp; message.is_read }">既読</p>
div.balloon <button class="delete-button" if="{ message.is_me }" title="メッセージを削除"><img src="/_/resources/desktop/messaging/delete.png" alt="Delete"/></button>
p.read(if={ message.is_me && message.is_read }) 既読 <div class="content" if="{ !message.is_deleted }">
button.delete-button(if={ message.is_me }, title='メッセージを削除') <div ref="text"></div>
img(src='/_/resources/desktop/messaging/delete.png', alt='Delete') <div class="image" if="{ message.file }"><img src="{ message.file.url }" alt="image" title="{ message.file.name }"/></div>
div.content(if={ !message.is_deleted }) </div>
div@text <div class="content" if="{ message.is_deleted }">
div.image(if={ message.file }) <p class="is-deleted">このメッセージは削除されました</p>
img(src={ message.file.url }, alt='image', title={ message.file.name }) </div>
div.content(if={ message.is_deleted }) </div>
p.is-deleted このメッセージは削除されました <footer>
footer <mk-time time="{ message.created_at }"></mk-time><i class="fa fa-pencil is-edited" if="{ message.is_edited }"></i>
mk-time(time={ message.created_at }) </footer>
i.fa.fa-pencil.is-edited(if={ message.is_edited }) </div>
<style type="stylus">
:scope
$me-balloon-color = #23A7B6
style.
$me-balloon-color = #23A7B6
display block
padding 10px 12px 10px 12px
background-color transparent
&:after
content ""
display block
clear both
> .avatar-anchor
display block
> .avatar
display block display block
min-width 54px padding 10px 12px 10px 12px
min-height 54px background-color transparent
max-width 54px
max-height 54px
margin 0
border-radius 8px
transition all 0.1s ease
> .content-container &:after
display block
margin 0 12px
padding 0
max-width calc(100% - 78px)
> .balloon
display block
float inherit
margin 0
padding 0
max-width 100%
min-height 38px
border-radius 16px
&:before
content "" content ""
pointer-events none
display block display block
position absolute clear both
top 12px
&:hover > .avatar-anchor
> .delete-button display block
> .avatar
display block display block
min-width 54px
min-height 54px
max-width 54px
max-height 54px
margin 0
border-radius 8px
transition all 0.1s ease
> .delete-button > .content-container
display none display block
position absolute margin 0 12px
z-index 1
top -4px
right -4px
margin 0
padding 0 padding 0
cursor pointer max-width calc(100% - 78px)
outline none
border none
border-radius 0
box-shadow none
background transparent
> img > .balloon
vertical-align bottom
width 16px
height 16px
cursor pointer
> .read
user-select none
display block
position absolute
z-index 1
bottom -4px
left -12px
margin 0
color rgba(0, 0, 0, 0.5)
font-size 11px
> .content
> .is-deleted
display block display block
float inherit
margin 0 margin 0
padding 0 padding 0
overflow hidden max-width 100%
word-wrap break-word min-height 38px
font-size 1em border-radius 16px
color rgba(0, 0, 0, 0.5)
> [ref='text'] &:before
display block content ""
margin 0 pointer-events none
padding 8px 16px display block
overflow hidden position absolute
word-wrap break-word top 12px
font-size 1em
color rgba(0, 0, 0, 0.8)
&, * &:hover
user-select text > .delete-button
cursor auto
& + .file
&.image
> img
border-radius 0 0 16px 16px
> .file
&.image
> img
display block display block
max-width 100%
max-height 512px
border-radius 16px
> footer > .delete-button
display block display none
clear both position absolute
margin 0 z-index 1
padding 2px top -4px
font-size 10px right -4px
color rgba(0, 0, 0, 0.4) margin 0
padding 0
cursor pointer
outline none
border none
border-radius 0
box-shadow none
background transparent
> .is-edited > img
margin-left 4px vertical-align bottom
width 16px
height 16px
cursor pointer
&:not([data-is-me='true']) > .read
> .avatar-anchor user-select none
float left display block
position absolute
z-index 1
bottom -4px
left -12px
margin 0
color rgba(0, 0, 0, 0.5)
font-size 11px
> .content-container > .content
float left
> .balloon > .is-deleted
background #eee display block
margin 0
padding 0
overflow hidden
word-wrap break-word
font-size 1em
color rgba(0, 0, 0, 0.5)
&:before > [ref='text']
left -14px display block
border-top solid 8px transparent margin 0
border-right solid 8px #eee padding 8px 16px
border-bottom solid 8px transparent overflow hidden
border-left solid 8px transparent word-wrap break-word
font-size 1em
color rgba(0, 0, 0, 0.8)
> footer &, *
text-align left user-select text
cursor auto
&[data-is-me='true'] & + .file
> .avatar-anchor &.image
float right > img
border-radius 0 0 16px 16px
> .content-container > .file
float right &.image
> img
display block
max-width 100%
max-height 512px
border-radius 16px
> .balloon > footer
background $me-balloon-color display block
clear both
margin 0
padding 2px
font-size 10px
color rgba(0, 0, 0, 0.4)
&:before > .is-edited
right -14px margin-left 4px
left auto
border-top solid 8px transparent
border-right solid 8px transparent
border-bottom solid 8px transparent
border-left solid 8px $me-balloon-color
> .content &:not([data-is-me='true'])
> .avatar-anchor
float left
> p.is-deleted > .content-container
color rgba(255, 255, 255, 0.5) float left
> [ref='text'] > .balloon
&, * background #eee
color #fff !important
> footer &:before
text-align right left -14px
border-top solid 8px transparent
border-right solid 8px #eee
border-bottom solid 8px transparent
border-left solid 8px transparent
&[data-is-deleted='true'] > footer
> .content-container text-align left
opacity 0.5
script. &[data-is-me='true']
@mixin \i > .avatar-anchor
@mixin \text float right
@message = @opts.message > .content-container
@message.is_me = @message.user.id == @I.id float right
@on \mount ~> > .balloon
if @message.text? background $me-balloon-color
tokens = @analyze @message.text
@refs.text.innerHTML = @compile tokens &:before
right -14px
left auto
border-top solid 8px transparent
border-right solid 8px transparent
border-bottom solid 8px transparent
border-left solid 8px $me-balloon-color
@refs.text.children.for-each (e) ~> > .content
if e.tag-name == \MK-URL
riot.mount e
# URLをプレビュー > p.is-deleted
tokens color rgba(255, 255, 255, 0.5)
.filter (t) -> t.type == \link
.map (t) ~> > [ref='text']
@preview = @refs.text.append-child document.create-element \mk-url-preview &, *
riot.mount @preview, do color #fff !important
url: t.content
> footer
text-align right
&[data-is-deleted='true']
> .content-container
opacity 0.5
</style>
<script>
@mixin \i
@mixin \text
@message = @opts.message
@message.is_me = @message.user.id == @I.id
@on \mount ~>
if @message.text?
tokens = @analyze @message.text
@refs.text.innerHTML = @compile tokens
@refs.text.children.for-each (e) ~>
if e.tag-name == \MK-URL
riot.mount e
# URLをプレビュー
tokens
.filter (t) -> t.type == \link
.map (t) ~>
@preview = @refs.text.append-child document.create-element \mk-url-preview
riot.mount @preview, do
url: t.content
</script>
</mk-messaging-message>

View file

@ -1,26 +1,25 @@
mk-messaging-room-window <mk-messaging-room-window>
mk-window@window(is-modal={ false }, width={ '500px' }, height={ '560px' }) <mk-window ref="window" is-modal="{ false }" width="{ '500px' }" height="{ '560px' }"><yield to="header"><i class="fa fa-comments"></i>メッセージ: { parent.user.name }</yield>
<yield to="header"> <yield to="content">
i.fa.fa-comments <mk-messaging-room user="{ parent.user }"></mk-messaging-room></yield>
| メッセージ: { parent.user.name } </mk-window>
</yield> <style type="stylus">
<yield to="content"> :scope
mk-messaging-room(user={ parent.user }) > mk-window
</yield> [data-yield='header']
> i
margin-right 4px
style. [data-yield='content']
> mk-window > mk-messaging-room
[data-yield='header'] height 100%
> i
margin-right 4px
[data-yield='content'] </style>
> mk-messaging-room <script>
height 100% @user = @opts.user
script. @on \mount ~>
@user = @opts.user @refs.window.on \closed ~>
@unmount!
@on \mount ~> </script>
@refs.window.on \closed ~> </mk-messaging-room-window>
@unmount!

View file

@ -1,227 +1,227 @@
mk-messaging-room <mk-messaging-room>
div.stream@stream <div class="stream" ref="stream">
p.initializing(if={ init }) <p class="initializing" if="{ init }"><i class="fa fa-spinner fa-spin"></i>読み込み中</p>
i.fa.fa-spinner.fa-spin <p class="empty" if="{ !init &amp;&amp; messages.length == 0 }"><i class="fa fa-info-circle"></i>このユーザーとまだ会話したことがありません</p>
| 読み込み中 <virtual each="{ message, i in messages }">
p.empty(if={ !init && messages.length == 0 }) <mk-messaging-message message="{ message }"></mk-messaging-message>
i.fa.fa-info-circle <p class="date" if="{ i != messages.length - 1 &amp;&amp; message._date != messages[i + 1]._date }"><span>{ messages[i + 1]._datetext }</span></p>
| このユーザーとまだ会話したことがありません </virtual>
virtual(each={ message, i in messages }) </div>
mk-messaging-message(message={ message }) <div class="typings"></div>
p.date(if={ i != messages.length - 1 && message._date != messages[i + 1]._date }) <footer>
span { messages[i + 1]._datetext } <div ref="notifications"></div>
<div class="grippie" title="ドラッグしてフォームの広さを調整"></div>
div.typings <mk-messaging-form user="{ user }"></mk-messaging-form>
footer </footer>
div@notifications <style type="stylus">
div.grippie(title='ドラッグしてフォームの広さを調整') :scope
mk-messaging-form(user={ user })
style.
display block
> .stream
position absolute
top 0
left 0
width 100%
height calc(100% - 100px)
overflow auto
> .empty
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
> .no-history
display block display block
margin 0
padding 16px
text-align center
font-size 0.8em
color rgba(0, 0, 0, 0.4)
i > .stream
margin-right 4px
> .message
// something
> .date
display block
margin 8px 0
text-align center
&:before
content ''
display block
position absolute position absolute
height 1px top 0
width 90%
top 16px
left 0 left 0
right 0 width 100%
height calc(100% - 100px)
overflow auto
> .empty
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
> .no-history
display block
margin 0
padding 16px
text-align center
font-size 0.8em
color rgba(0, 0, 0, 0.4)
i
margin-right 4px
> .message
// something
> .date
display block
margin 8px 0
text-align center
&:before
content ''
display block
position absolute
height 1px
width 90%
top 16px
left 0
right 0
margin 0 auto
background rgba(0, 0, 0, 0.1)
> span
display inline-block
margin 0
padding 0 16px
//font-weight bold
line-height 32px
color rgba(0, 0, 0, 0.3)
background #fff
> footer
position absolute
z-index 2
bottom 0
width 600px
max-width 100%
margin 0 auto margin 0 auto
background rgba(0, 0, 0, 0.1) padding 0
background rgba(255, 255, 255, 0.95)
background-clip content-box
> span > [ref='notifications']
display inline-block
margin 0
padding 0 16px
//font-weight bold
line-height 32px
color rgba(0, 0, 0, 0.3)
background #fff
> footer
position absolute
z-index 2
bottom 0
width 600px
max-width 100%
margin 0 auto
padding 0
background rgba(255, 255, 255, 0.95)
background-clip content-box
> [ref='notifications']
position absolute
top -48px
width 100%
padding 8px 0
text-align center
> p
display inline-block
margin 0
padding 0 12px 0 28px
cursor pointer
line-height 32px
font-size 12px
color $theme-color-foreground
background $theme-color
border-radius 16px
transition opacity 1s ease
> i
position absolute position absolute
top 0 top -48px
left 10px width 100%
line-height 32px padding 8px 0
font-size 16px text-align center
> .grippie > p
height 10px display inline-block
margin-top -10px margin 0
background transparent padding 0 12px 0 28px
cursor ns-resize cursor pointer
line-height 32px
font-size 12px
color $theme-color-foreground
background $theme-color
border-radius 16px
transition opacity 1s ease
&:hover > i
//background rgba(0, 0, 0, 0.1) position absolute
top 0
left 10px
line-height 32px
font-size 16px
&:active > .grippie
//background rgba(0, 0, 0, 0.2) height 10px
margin-top -10px
background transparent
cursor ns-resize
script. &:hover
@mixin \i //background rgba(0, 0, 0, 0.1)
@mixin \api
@mixin \messaging-stream
@user = @opts.user &:active
@init = true //background rgba(0, 0, 0, 0.2)
@sending = false
@messages = []
@connection = new @MessagingStreamConnection @I, @user.id </style>
<script>
@mixin \i
@mixin \api
@mixin \messaging-stream
@on \mount ~> @user = @opts.user
@connection.event.on \message @on-message @init = true
@connection.event.on \read @on-read @sending = false
@messages = []
document.add-event-listener \visibilitychange @on-visibilitychange @connection = new @MessagingStreamConnection @I, @user.id
@api \messaging/messages do @on \mount ~>
user_id: @user.id @connection.event.on \message @on-message
.then (messages) ~> @connection.event.on \read @on-read
@init = false
@messages = messages.reverse!
@update!
@scroll-to-bottom!
.catch (err) ~>
console.error err
@on \unmount ~> document.add-event-listener \visibilitychange @on-visibilitychange
@connection.event.off \message @on-message
@connection.event.off \read @on-read
@connection.close!
document.remove-event-listener \visibilitychange @on-visibilitychange @api \messaging/messages do
user_id: @user.id
@on \update ~> .then (messages) ~>
@messages.for-each (message) ~> @init = false
date = (new Date message.created_at).get-date! @messages = messages.reverse!
month = (new Date message.created_at).get-month! + 1
message._date = date
message._datetext = month + '月 ' + date + '日'
@on-message = (message) ~>
is-bottom = @is-bottom!
@messages.push message
if message.user_id != @I.id and not document.hidden
@connection.socket.send JSON.stringify do
type: \read
id: message.id
@update!
if is-bottom
# Scroll to bottom
@scroll-to-bottom!
else if message.user_id != @I.id
# Notify
@notify '新しいメッセージがあります'
@on-read = (ids) ~>
if not Array.isArray ids then ids = [ids]
ids.for-each (id) ~>
if (@messages.some (x) ~> x.id == id)
exist = (@messages.map (x) -> x.id).index-of id
@messages[exist].is_read = true
@update! @update!
@scroll-to-bottom!
.catch (err) ~>
console.error err
@is-bottom = ~> @on \unmount ~>
current = @refs.stream.scroll-top + @refs.stream.offset-height @connection.event.off \message @on-message
max = @refs.stream.scroll-height @connection.event.off \read @on-read
current > (max - 32) @connection.close!
@scroll-to-bottom = ~> document.remove-event-listener \visibilitychange @on-visibilitychange
@refs.stream.scroll-top = @refs.stream.scroll-height
@notify = (message) ~> @on \update ~>
n = document.create-element \p @messages.for-each (message) ~>
n.inner-HTML = '<i class="fa fa-arrow-circle-down"></i>' + message date = (new Date message.created_at).get-date!
n.onclick = ~> month = (new Date message.created_at).get-month! + 1
@scroll-to-bottom! message._date = date
n.parent-node.remove-child n message._datetext = month + '月 ' + date + '日'
@refs.notifications.append-child n
set-timeout ~> @on-message = (message) ~>
n.style.opacity = 0 is-bottom = @is-bottom!
set-timeout ~>
n.parent-node.remove-child n
, 1000ms
, 4000ms
@on-visibilitychange = ~> @messages.push message
if document.hidden then return if message.user_id != @I.id and not document.hidden
@messages.for-each (message) ~>
if message.user_id != @I.id and not message.is_read
@connection.socket.send JSON.stringify do @connection.socket.send JSON.stringify do
type: \read type: \read
id: message.id id: message.id
@update!
if is-bottom
# Scroll to bottom
@scroll-to-bottom!
else if message.user_id != @I.id
# Notify
@notify '新しいメッセージがあります'
@on-read = (ids) ~>
if not Array.isArray ids then ids = [ids]
ids.for-each (id) ~>
if (@messages.some (x) ~> x.id == id)
exist = (@messages.map (x) -> x.id).index-of id
@messages[exist].is_read = true
@update!
@is-bottom = ~>
current = @refs.stream.scroll-top + @refs.stream.offset-height
max = @refs.stream.scroll-height
current > (max - 32)
@scroll-to-bottom = ~>
@refs.stream.scroll-top = @refs.stream.scroll-height
@notify = (message) ~>
n = document.create-element \p
n.inner-HTML = '<i class="fa fa-arrow-circle-down"></i>' + message
n.onclick = ~>
@scroll-to-bottom!
n.parent-node.remove-child n
@refs.notifications.append-child n
set-timeout ~>
n.style.opacity = 0
set-timeout ~>
n.parent-node.remove-child n
, 1000ms
, 4000ms
@on-visibilitychange = ~>
if document.hidden then return
@messages.for-each (message) ~>
if message.user_id != @I.id and not message.is_read
@connection.socket.send JSON.stringify do
type: \read
id: message.id
</script>
</mk-messaging-room>

View file

@ -1,29 +1,28 @@
mk-messaging-window <mk-messaging-window>
mk-window@window(is-modal={ false }, width={ '500px' }, height={ '560px' }) <mk-window ref="window" is-modal="{ false }" width="{ '500px' }" height="{ '560px' }"><yield to="header"><i class="fa fa-comments"></i>メッセージ</yield>
<yield to="header"> <yield to="content">
i.fa.fa-comments <mk-messaging ref="index"></mk-messaging></yield>
| メッセージ </mk-window>
</yield> <style type="stylus">
<yield to="content"> :scope
mk-messaging@index > mk-window
</yield> [data-yield='header']
> i
margin-right 4px
style. [data-yield='content']
> mk-window > mk-messaging
[data-yield='header'] height 100%
> i
margin-right 4px
[data-yield='content'] </style>
> mk-messaging <script>
height 100% @on \mount ~>
@refs.window.on \closed ~>
@unmount!
script. @refs.window.refs.index.on \navigate-user (user) ~>
@on \mount ~> w = document.body.append-child document.create-element \mk-messaging-room-window
@refs.window.on \closed ~> riot.mount w, do
@unmount! user: user
</script>
@refs.window.refs.index.on \navigate-user (user) ~> </mk-messaging-window>
w = document.body.append-child document.create-element \mk-messaging-room-window
riot.mount w, do
user: user

View file

@ -1,226 +1,199 @@
mk-notifications <mk-notifications>
div.notifications(if={ notifications.length != 0 }) <div class="notifications" if="{ notifications.length != 0 }">
virtual(each={ notification, i in notifications }) <virtual each="{ notification, i in notifications }">
div.notification(class={ notification.type }) <div class="notification { notification.type }">
mk-time(time={ notification.created_at }) <mk-time time="{ notification.created_at }"></mk-time>
<div class="main" if="{ notification.type == 'like' }"><a class="avatar-anchor" href="{ CONFIG.url + '/' + notification.user.username }" data-user-preview="{ notification.user.id }"><img class="avatar" src="{ notification.user.avatar_url + '?thumbnail&amp;size=48' }" alt="avatar"/></a>
<div class="text">
<p><i class="fa fa-thumbs-o-up"></i><a href="{ CONFIG.url + '/' + notification.user.username }" data-user-preview="{ notification.user.id }">{ notification.user.name }</a></p><a class="post-ref" href="{ CONFIG.url + '/' + notification.post.user.username + '/' + notification.post.id }">{ getPostSummary(notification.post) }</a>
</div>
</div>
<div class="main" if="{ notification.type == 'repost' }"><a class="avatar-anchor" href="{ CONFIG.url + '/' + notification.post.user.username }" data-user-preview="{ notification.post.user_id }"><img class="avatar" src="{ notification.post.user.avatar_url + '?thumbnail&amp;size=48' }" alt="avatar"/></a>
<div class="text">
<p><i class="fa fa-retweet"></i><a href="{ CONFIG.url + '/' + notification.post.user.username }" data-user-preview="{ notification.post.user_id }">{ notification.post.user.name }</a></p><a class="post-ref" href="{ CONFIG.url + '/' + notification.post.user.username + '/' + notification.post.id }">{ getPostSummary(notification.post.repost) }</a>
</div>
</div>
<div class="main" if="{ notification.type == 'quote' }"><a class="avatar-anchor" href="{ CONFIG.url + '/' + notification.post.user.username }" data-user-preview="{ notification.post.user_id }"><img class="avatar" src="{ notification.post.user.avatar_url + '?thumbnail&amp;size=48' }" alt="avatar"/></a>
<div class="text">
<p><i class="fa fa-quote-left"></i><a href="{ CONFIG.url + '/' + notification.post.user.username }" data-user-preview="{ notification.post.user_id }">{ notification.post.user.name }</a></p><a class="post-preview" href="{ CONFIG.url + '/' + notification.post.user.username + '/' + notification.post.id }">{ getPostSummary(notification.post) }</a>
</div>
</div>
<div class="main" if="{ notification.type == 'follow' }"><a class="avatar-anchor" href="{ CONFIG.url + '/' + notification.user.username }" data-user-preview="{ notification.user.id }"><img class="avatar" src="{ notification.user.avatar_url + '?thumbnail&amp;size=48' }" alt="avatar"/></a>
<div class="text">
<p><i class="fa fa-user-plus"></i><a href="{ CONFIG.url + '/' + notification.user.username }" data-user-preview="{ notification.user.id }">{ notification.user.name }</a></p>
</div>
</div>
<div class="main" if="{ notification.type == 'reply' }"><a class="avatar-anchor" href="{ CONFIG.url + '/' + notification.post.user.username }" data-user-preview="{ notification.post.user_id }"><img class="avatar" src="{ notification.post.user.avatar_url + '?thumbnail&amp;size=48' }" alt="avatar"/></a>
<div class="text">
<p><i class="fa fa-reply"></i><a href="{ CONFIG.url + '/' + notification.post.user.username }" data-user-preview="{ notification.post.user_id }">{ notification.post.user.name }</a></p><a class="post-preview" href="{ CONFIG.url + '/' + notification.post.user.username + '/' + notification.post.id }">{ getPostSummary(notification.post) }</a>
</div>
</div>
<div class="main" if="{ notification.type == 'mention' }"><a class="avatar-anchor" href="{ CONFIG.url + '/' + notification.post.user.username }" data-user-preview="{ notification.post.user_id }"><img class="avatar" src="{ notification.post.user.avatar_url + '?thumbnail&amp;size=48' }" alt="avatar"/></a>
<div class="text">
<p><i class="fa fa-at"></i><a href="{ CONFIG.url + '/' + notification.post.user.username }" data-user-preview="{ notification.post.user_id }">{ notification.post.user.name }</a></p><a class="post-preview" href="{ CONFIG.url + '/' + notification.post.user.username + '/' + notification.post.id }">{ getPostSummary(notification.post) }</a>
</div>
</div>
</div>
<p class="date" if="{ i != notifications.length - 1 &amp;&amp; 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>
<p class="empty" if="{ notifications.length == 0 &amp;&amp; !loading }">ありません!</p>
<p class="loading" if="{ loading }"><i class="fa fa-spinner fa-pulse fa-fw"></i>読み込んでいます
<mk-ellipsis></mk-ellipsis>
</p>
<style type="stylus">
:scope
display block
div.main(if={ notification.type == 'like' }) > .notifications
a.avatar-anchor(href={ CONFIG.url + '/' + notification.user.username }, data-user-preview={ notification.user.id }) > .notification
img.avatar(src={ notification.user.avatar_url + '?thumbnail&size=48' }, alt='avatar')
div.text
p
i.fa.fa-thumbs-o-up
a(href={ CONFIG.url + '/' + notification.user.username }, data-user-preview={ notification.user.id }) { notification.user.name }
a.post-ref(href={ CONFIG.url + '/' + notification.post.user.username + '/' + notification.post.id }) { get-post-summary(notification.post) }
div.main(if={ notification.type == 'repost' })
a.avatar-anchor(href={ CONFIG.url + '/' + notification.post.user.username }, data-user-preview={ notification.post.user_id })
img.avatar(src={ notification.post.user.avatar_url + '?thumbnail&size=48' }, alt='avatar')
div.text
p
i.fa.fa-retweet
a(href={ CONFIG.url + '/' + notification.post.user.username }, data-user-preview={ notification.post.user_id }) { notification.post.user.name }
a.post-ref(href={ CONFIG.url + '/' + notification.post.user.username + '/' + notification.post.id }) { get-post-summary(notification.post.repost) }
div.main(if={ notification.type == 'quote' })
a.avatar-anchor(href={ CONFIG.url + '/' + notification.post.user.username }, data-user-preview={ notification.post.user_id })
img.avatar(src={ notification.post.user.avatar_url + '?thumbnail&size=48' }, alt='avatar')
div.text
p
i.fa.fa-quote-left
a(href={ CONFIG.url + '/' + notification.post.user.username }, data-user-preview={ notification.post.user_id }) { notification.post.user.name }
a.post-preview(href={ CONFIG.url + '/' + notification.post.user.username + '/' + notification.post.id }) { get-post-summary(notification.post) }
div.main(if={ notification.type == 'follow' })
a.avatar-anchor(href={ CONFIG.url + '/' + notification.user.username }, data-user-preview={ notification.user.id })
img.avatar(src={ notification.user.avatar_url + '?thumbnail&size=48' }, alt='avatar')
div.text
p
i.fa.fa-user-plus
a(href={ CONFIG.url + '/' + notification.user.username }, data-user-preview={ notification.user.id }) { notification.user.name }
div.main(if={ notification.type == 'reply' })
a.avatar-anchor(href={ CONFIG.url + '/' + notification.post.user.username }, data-user-preview={ notification.post.user_id })
img.avatar(src={ notification.post.user.avatar_url + '?thumbnail&size=48' }, alt='avatar')
div.text
p
i.fa.fa-reply
a(href={ CONFIG.url + '/' + notification.post.user.username }, data-user-preview={ notification.post.user_id }) { notification.post.user.name }
a.post-preview(href={ CONFIG.url + '/' + notification.post.user.username + '/' + notification.post.id }) { get-post-summary(notification.post) }
div.main(if={ notification.type == 'mention' })
a.avatar-anchor(href={ CONFIG.url + '/' + notification.post.user.username }, data-user-preview={ notification.post.user_id })
img.avatar(src={ notification.post.user.avatar_url + '?thumbnail&size=48' }, alt='avatar')
div.text
p
i.fa.fa-at
a(href={ CONFIG.url + '/' + notification.post.user.username }, data-user-preview={ notification.post.user_id }) { notification.post.user.name }
a.post-preview(href={ CONFIG.url + '/' + notification.post.user.username + '/' + notification.post.id }) { get-post-summary(notification.post) }
p.date(if={ i != notifications.length - 1 && notification._date != notifications[i + 1]._date })
span
i.fa.fa-angle-up
| { notification._datetext }
span
i.fa.fa-angle-down
| { notifications[i + 1]._datetext }
p.empty(if={ notifications.length == 0 && !loading })
| ありません!
p.loading(if={ loading })
i.fa.fa-spinner.fa-pulse.fa-fw
| 読み込んでいます
mk-ellipsis
style.
display block
> .notifications
> .notification
margin 0
padding 16px
font-size 0.9em
border-bottom solid 1px rgba(0, 0, 0, 0.05)
&:last-child
border-bottom none
> mk-time
display inline
position absolute
top 16px
right 12px
vertical-align top
color rgba(0, 0, 0, 0.6)
font-size small
> .main
word-wrap break-word
&:after
content ""
display block
clear both
.avatar-anchor
display block
float left
img
min-width 36px
min-height 36px
max-width 36px
max-height 36px
border-radius 6px
.text
float right
width calc(100% - 36px)
padding-left 8px
p
margin 0 margin 0
padding 16px
font-size 0.9em
border-bottom solid 1px rgba(0, 0, 0, 0.05)
&:last-child
border-bottom none
> mk-time
display inline
position absolute
top 16px
right 12px
vertical-align top
color rgba(0, 0, 0, 0.6)
font-size small
> .main
word-wrap break-word
&:after
content ""
display block
clear both
.avatar-anchor
display block
float left
img
min-width 36px
min-height 36px
max-width 36px
max-height 36px
border-radius 6px
.text
float right
width calc(100% - 36px)
padding-left 8px
p
margin 0
i
margin-right 4px
.post-preview
color rgba(0, 0, 0, 0.7)
.post-ref
color rgba(0, 0, 0, 0.7)
&:before, &:after
font-family FontAwesome
font-size 1em
font-weight normal
font-style normal
display inline-block
margin-right 3px
&:before
content "\f10d"
&:after
content "\f10e"
&.like
.text p i
color #FFAC33
&.repost, &.quote
.text p i
color #77B255
&.follow
.text p i
color #53c7ce
&.reply, &.mention
.text p i
color #555
> .date
display block
margin 0
line-height 32px
text-align center
font-size 0.8em
color #aaa
background #fdfdfd
border-bottom solid 1px rgba(0, 0, 0, 0.05)
span
margin 0 16px
i i
margin-right 4px margin-right 8px
.post-preview > .empty
color rgba(0, 0, 0, 0.7) margin 0
padding 16px
text-align center
color #aaa
.post-ref > .loading
color rgba(0, 0, 0, 0.7) margin 0
padding 16px
text-align center
color #aaa
&:before, &:after > i
font-family FontAwesome margin-right 4px
font-size 1em
font-weight normal
font-style normal
display inline-block
margin-right 3px
&:before </style>
content "\f10d" <script>
@mixin \api
@mixin \stream
@mixin \user-preview
@mixin \get-post-summary
&:after @notifications = []
content "\f10e" @loading = true
&.like @on \mount ~>
.text p i @api \i/notifications
color #FFAC33 .then (notifications) ~>
@notifications = notifications
@loading = false
@update!
.catch (err, text-status) ->
console.error err
&.repost, &.quote @stream.on \notification @on-notification
.text p i
color #77B255
&.follow @on \unmount ~>
.text p i @stream.off \notification @on-notification
color #53c7ce
&.reply, &.mention @on-notification = (notification) ~>
.text p i @notifications.unshift notification
color #555
> .date
display block
margin 0
line-height 32px
text-align center
font-size 0.8em
color #aaa
background #fdfdfd
border-bottom solid 1px rgba(0, 0, 0, 0.05)
span
margin 0 16px
i
margin-right 8px
> .empty
margin 0
padding 16px
text-align center
color #aaa
> .loading
margin 0
padding 16px
text-align center
color #aaa
> i
margin-right 4px
script.
@mixin \api
@mixin \stream
@mixin \user-preview
@mixin \get-post-summary
@notifications = []
@loading = true
@on \mount ~>
@api \i/notifications
.then (notifications) ~>
@notifications = notifications
@loading = false
@update! @update!
.catch (err, text-status) ->
console.error err
@stream.on \notification @on-notification @on \update ~>
@notifications.for-each (notification) ~>
@on \unmount ~> date = (new Date notification.created_at).get-date!
@stream.off \notification @on-notification month = (new Date notification.created_at).get-month! + 1
notification._date = date
@on-notification = (notification) ~> notification._datetext = month + '月 ' + date + '日'
@notifications.unshift notification </script>
@update! </mk-notifications>
@on \update ~>
@notifications.for-each (notification) ~>
date = (new Date notification.created_at).get-date!
month = (new Date notification.created_at).get-month! + 1
notification._date = date
notification._datetext = month + '月 ' + date + '日'

View file

@ -1,77 +1,80 @@
mk-entrance <mk-entrance>
main <main><img src="/_/resources/title.svg" alt="Misskey"/>
img(src='/_/resources/title.svg', alt='Misskey') <mk-entrance-signin if="{ mode == 'signin' }"></mk-entrance-signin>
<mk-entrance-signup if="{ mode == 'signup' }"></mk-entrance-signup>
mk-entrance-signin(if={ mode == 'signin' }) <div class="introduction" if="{ mode == 'introduction' }">
mk-entrance-signup(if={ mode == 'signup' }) <mk-introduction></mk-introduction>
div.introduction(if={ mode == 'introduction' }) <button onclick="{ signin }">わかった</button>
mk-introduction </div>
button(onclick={ signin }) わかった </main>
<mk-forkit></mk-forkit>
mk-forkit <footer>
<mk-copyright></mk-copyright>
footer </footer>
mk-copyright <!-- ↓ https://github.com/riot/riot/issues/2134 (将来的)-->
<style data-disable-scope="data-disable-scope">
// ↓ https://github.com/riot/riot/issues/2134 (将来的)
style(data-disable-scope).
#wait { #wait {
right: auto; right: auto;
left: 15px; left: 15px;
} }
style. </style>
display block <style type="stylus">
height 100% :scope
> main
display block
> img
display block display block
width 160px height 100%
height 170px
margin 0 auto
pointer-events none
user-select none
> .introduction > main
max-width 360px
margin 0 auto
color #777
> mk-introduction
padding 32px
background #fff
box-shadow 0 4px 16px rgba(0, 0, 0, 0.2)
> button
display block display block
margin 16px auto 0 auto
color #666
&:hover > img
text-decoration underline display block
width 160px
height 170px
margin 0 auto
pointer-events none
user-select none
> footer > .introduction
> mk-copyright max-width 360px
margin 0 margin 0 auto
text-align center color #777
line-height 64px
font-size 10px
color rgba(#000, 0.5)
script. > mk-introduction
@mode = \signin padding 32px
background #fff
box-shadow 0 4px 16px rgba(0, 0, 0, 0.2)
@signup = ~> > button
@mode = \signup display block
@update! margin 16px auto 0 auto
color #666
@signin = ~> &:hover
text-decoration underline
> footer
> mk-copyright
margin 0
text-align center
line-height 64px
font-size 10px
color rgba(#000, 0.5)
</style>
<script>
@mode = \signin @mode = \signin
@update!
@introduction = ~> @signup = ~>
@mode = \introduction @mode = \signup
@update! @update!
@signin = ~>
@mode = \signin
@update!
@introduction = ~>
@mode = \introduction
@update!
</script>
</mk-entrance>

View file

@ -1,128 +1,130 @@
mk-entrance-signin <mk-entrance-signin><a class="help" href="{ CONFIG.urls.about + '/help' }" title="お困りですか?"><i class="fa fa-question"></i></a>
a.help(href={ CONFIG.urls.about + '/help' }, title='お困りですか?'): i.fa.fa-question <div class="form">
div.form <h1><img if="{ user }" src="{ user.avatar_url + '?thumbnail&amp;size=32' }"/>
h1 <p>{ user ? user.name : 'アカウント' }</p>
img(if={ user }, src={ user.avatar_url + '?thumbnail&size=32' }) </h1>
p { user ? user.name : 'アカウント' } <mk-signin ref="signin"></mk-signin>
mk-signin@signin </div>
div.divider: span or <div class="divider"><span>or</span></div>
button.signup(onclick={ parent.signup }) 新規登録 <button class="signup" onclick="{ parent.signup }">新規登録</button><a class="introduction" onclick="{ introduction }">Misskeyについて</a>
a.introduction(onclick={ introduction }) Misskeyについて <style type="stylus">
:scope
style.
display block
width 290px
margin 0 auto
text-align center
&:hover
> .help
opacity 1
> .help
cursor pointer
display block
position absolute
top 0
right 0
z-index 1
margin 0
padding 0
font-size 1.2em
color #999
border none
outline none
background transparent
opacity 0
transition opacity 0.1s ease
&:hover
color #444
&:active
color #222
> i
padding 14px
> .form
padding 10px 28px 16px 28px
background #fff
box-shadow 0px 4px 16px rgba(0, 0, 0, 0.2)
> h1
display block display block
margin 0 width 290px
padding 0 margin 0 auto
height 54px
line-height 54px
text-align center text-align center
text-transform uppercase
font-size 1em
font-weight bold
color rgba(0, 0, 0, 0.5)
border-bottom solid 1px rgba(0, 0, 0, 0.1)
> p &:hover
display inline > .help
opacity 1
> .help
cursor pointer
display block
position absolute
top 0
right 0
z-index 1
margin 0 margin 0
padding 0 padding 0
font-size 1.2em
color #999
border none
outline none
background transparent
opacity 0
transition opacity 0.1s ease
> img &:hover
color #444
&:active
color #222
> i
padding 14px
> .form
padding 10px 28px 16px 28px
background #fff
box-shadow 0px 4px 16px rgba(0, 0, 0, 0.2)
> h1
display block
margin 0
padding 0
height 54px
line-height 54px
text-align center
text-transform uppercase
font-size 1em
font-weight bold
color rgba(0, 0, 0, 0.5)
border-bottom solid 1px rgba(0, 0, 0, 0.1)
> p
display inline
margin 0
padding 0
> img
display inline-block
top 10px
width 32px
height 32px
margin-right 8px
border-radius 100%
&[src='']
display none
> .divider
padding 16px 0
text-align center
&:after
content ""
display block
position absolute
top 50%
width 100%
height 1px
border-top solid 1px rgba(0, 0, 0, 0.1)
> *
z-index 1
padding 0 8px
color rgba(0, 0, 0, 0.5)
background #fdfdfd
> .signup
width 100%
line-height 56px
font-size 1em
color #fff
background $theme-color
border-radius 64px
&:hover
background lighten($theme-color, 5%)
&:active
background darken($theme-color, 5%)
> .introduction
display inline-block display inline-block
top 10px margin-top 16px
width 32px font-size 12px
height 32px color #666
margin-right 8px
border-radius 100%
&[src=''] </style>
display none <script>
@on \mount ~>
@refs.signin.on \user (user) ~>
@update do
user: user
> .divider @introduction = ~>
padding 16px 0 @parent.introduction!
text-align center </script>
</mk-entrance-signin>
&:after
content ""
display block
position absolute
top 50%
width 100%
height 1px
border-top solid 1px rgba(0, 0, 0, 0.1)
> *
z-index 1
padding 0 8px
color rgba(0, 0, 0, 0.5)
background #fdfdfd
> .signup
width 100%
line-height 56px
font-size 1em
color #fff
background $theme-color
border-radius 64px
&:hover
background lighten($theme-color, 5%)
&:active
background darken($theme-color, 5%)
> .introduction
display inline-block
margin-top 16px
font-size 12px
color #666
script.
@on \mount ~>
@refs.signin.on \user (user) ~>
@update do
user: user
@introduction = ~>
@parent.introduction!

View file

@ -1,44 +1,51 @@
mk-entrance-signup <mk-entrance-signup>
mk-signup <mk-signup></mk-signup>
button.cancel(type='button', onclick={ parent.signin }, title='キャンセル'): i.fa.fa-times <button class="cancel" type="button" onclick="{ parent.signin }" title="キャンセル"><i class="fa fa-times"></i></button>
<style type="stylus">
:scope
display block
width 368px
margin 0 auto
style. &:hover
display block > .cancel
width 368px opacity 1
margin 0 auto
&:hover > mk-signup
> .cancel padding 18px 32px 0 32px
opacity 1 background #fff
box-shadow 0px 4px 16px rgba(0, 0, 0, 0.2)
> mk-signup > .cancel
padding 18px 32px 0 32px cursor pointer
background #fff display block
box-shadow 0px 4px 16px rgba(0, 0, 0, 0.2) position absolute
top 0
right 0
z-index 1
margin 0
padding 0
font-size 1.2em
color #999
border none
outline none
box-shadow none
background transparent
opacity 0
transition opacity 0.1s ease
> .cancel &:hover
cursor pointer color #555
display block
position absolute
top 0
right 0
z-index 1
margin 0
padding 0
font-size 1.2em
color #999
border none
outline none
box-shadow none
background transparent
opacity 0
transition opacity 0.1s ease
&:hover &:active
color #555 color #222
&:active > i
color #222 padding 14px
> i
padding 14px
</style>
</mk-entrance-signup>

View file

@ -1,51 +1,56 @@
mk-home-page <mk-home-page>
mk-ui@ui(page={ page }): mk-home@home(mode={ parent.opts.mode }) <mk-ui ref="ui" page="{ page }">
<mk-home ref="home" mode="{ parent.opts.mode }"></mk-home>
</mk-ui>
<style type="stylus">
:scope
display block
style. background-position center center
display block background-attachment fixed
background-size cover
background-position center center </style>
background-attachment fixed <script>
background-size cover @mixin \i
@mixin \api
@mixin \ui-progress
@mixin \stream
@mixin \get-post-summary
script. @unread-count = 0
@mixin \i
@mixin \api
@mixin \ui-progress
@mixin \stream
@mixin \get-post-summary
@unread-count = 0 @page = switch @opts.mode
| \timelie => \home
| \mentions => \mentions
| _ => \home
@page = switch @opts.mode @on \mount ~>
| \timelie => \home @refs.ui.refs.home.on \loaded ~>
| \mentions => \mentions @Progress.done!
| _ => \home
@on \mount ~>
@refs.ui.refs.home.on \loaded ~>
@Progress.done!
document.title = 'Misskey'
if @I.data.wallpaper
@api \drive/files/show do
file_id: @I.data.wallpaper
.then (file) ~>
@root.style.background-image = 'url(' + file.url + ')'
@Progress.start!
@stream.on \post @on-stream-post
document.add-event-listener \visibilitychange @window-on-visibilitychange, false
@on \unmount ~>
@stream.off \post @on-stream-post
document.remove-event-listener \visibilitychange @window-on-visibilitychange
@on-stream-post = (post) ~>
if document.hidden and post.user_id !== @I.id
@unread-count++
document.title = '(' + @unread-count + ') ' + @get-post-summary post
@window-on-visibilitychange = ~>
if !document.hidden
@unread-count = 0
document.title = 'Misskey' document.title = 'Misskey'
if @I.data.wallpaper
@api \drive/files/show do
file_id: @I.data.wallpaper
.then (file) ~>
@root.style.background-image = 'url(' + file.url + ')'
@Progress.start!
@stream.on \post @on-stream-post
document.add-event-listener \visibilitychange @window-on-visibilitychange, false
@on \unmount ~>
@stream.off \post @on-stream-post
document.remove-event-listener \visibilitychange @window-on-visibilitychange
@on-stream-post = (post) ~>
if document.hidden and post.user_id !== @I.id
@unread-count++
document.title = '(' + @unread-count + ') ' + @get-post-summary post
@window-on-visibilitychange = ~>
if !document.hidden
@unread-count = 0
document.title = 'Misskey'
</script>
</mk-home-page>

View file

@ -1,46 +1,54 @@
mk-not-found <mk-not-found>
mk-ui <mk-ui>
main <main>
h1 Not Found <h1>Not Found</h1><img src="/_/resources/rogge.jpg" alt=""/>
img(src='/_/resources/rogge.jpg', alt='') <div class="mask"></div>
div.mask </main>
</mk-ui>
style. <style type="stylus">
display block :scope
main
display block
width 600px
margin 32px auto
> img
display block display block
width 600px
height 459px
pointer-events none
user-select none
border-radius 16px
box-shadow 0 0 16px rgba(0, 0, 0, 0.1)
> h1 main
display block display block
margin 0 width 600px
padding 0 margin 32px auto
position absolute
top 260px
left 225px
transform rotate(-12deg)
z-index 2
color #444
font-size 24px
line-height 20px
> .mask > img
position absolute display block
top 262px width 600px
left 217px height 459px
width 126px pointer-events none
height 18px user-select none
transform rotate(-12deg) border-radius 16px
background #D6D5DA box-shadow 0 0 16px rgba(0, 0, 0, 0.1)
border-radius 2px 6px 7px 6px
> h1
display block
margin 0
padding 0
position absolute
top 260px
left 225px
transform rotate(-12deg)
z-index 2
color #444
font-size 24px
line-height 20px
> .mask
position absolute
top 262px
left 217px
width 126px
height 18px
transform rotate(-12deg)
background #D6D5DA
border-radius 2px 6px 7px 6px
</style>
</mk-not-found>

View file

@ -1,25 +1,32 @@
mk-post-page <mk-post-page>
mk-ui@ui: main: mk-post-detail@detail(post={ parent.post }) <mk-ui ref="ui">
<main>
<mk-post-detail ref="detail" post="{ parent.post }"></mk-post-detail>
</main>
</mk-ui>
<style type="stylus">
:scope
display block
style. main
display block padding 16px
main > mk-post-detail
padding 16px margin 0 auto
> mk-post-detail </style>
margin 0 auto <script>
@mixin \ui-progress
script. @post = @opts.post
@mixin \ui-progress
@post = @opts.post @on \mount ~>
@Progress.start!
@on \mount ~> @refs.ui.refs.detail.on \post-fetched ~>
@Progress.start! @Progress.set 0.5
@refs.ui.refs.detail.on \post-fetched ~> @refs.ui.refs.detail.on \loaded ~>
@Progress.set 0.5 @Progress.done!
</script>
@refs.ui.refs.detail.on \loaded ~> </mk-post-page>
@Progress.done!

View file

@ -1,14 +1,19 @@
mk-search-page <mk-search-page>
mk-ui@ui: mk-search@search(query={ parent.opts.query }) <mk-ui ref="ui">
<mk-search ref="search" query="{ parent.opts.query }"></mk-search>
</mk-ui>
<style type="stylus">
:scope
display block
style. </style>
display block <script>
@mixin \ui-progress
script. @on \mount ~>
@mixin \ui-progress @Progress.start!
@on \mount ~> @refs.ui.refs.search.on \loaded ~>
@Progress.start! @Progress.done!
</script>
@refs.ui.refs.search.on \loaded ~> </mk-search-page>
@Progress.done!

View file

@ -1,20 +1,25 @@
mk-user-page <mk-user-page>
mk-ui@ui: mk-user@user(user={ parent.user }, page={ parent.opts.page }) <mk-ui ref="ui">
<mk-user ref="user" user="{ parent.user }" page="{ parent.opts.page }"></mk-user>
</mk-ui>
<style type="stylus">
:scope
display block
style. </style>
display block <script>
@mixin \ui-progress
script. @user = @opts.user
@mixin \ui-progress
@user = @opts.user @on \mount ~>
@Progress.start!
@on \mount ~> @refs.ui.refs.user.on \user-fetched (user) ~>
@Progress.start! @Progress.set 0.5
document.title = user.name + ' | Misskey'
@refs.ui.refs.user.on \user-fetched (user) ~> @refs.ui.refs.user.on \loaded ~>
@Progress.set 0.5 @Progress.done!
document.title = user.name + ' | Misskey' </script>
</mk-user-page>
@refs.ui.refs.user.on \loaded ~>
@Progress.done!

View file

@ -1,141 +1,140 @@
mk-post-detail-sub(title={ title }) <mk-post-detail-sub title="{ title }"><a class="avatar-anchor" href="{ CONFIG.url + '/' + post.user.username }"><img class="avatar" src="{ post.user.avatar_url + '?thumbnail&amp;size=64' }" alt="avatar" data-user-preview="{ post.user_id }"/></a>
a.avatar-anchor(href={ CONFIG.url + '/' + post.user.username }) <div class="main">
img.avatar(src={ post.user.avatar_url + '?thumbnail&size=64' }, alt='avatar', data-user-preview={ post.user_id }) <header>
div.main <div class="left"><a class="name" href="{ CONFIG.url + '/' + post.user.username }" data-user-preview="{ post.user_id }">{ post.user.name }</a><span class="username">@{ post.user.username }</span></div>
header <div class="right"><a class="time" href="{ url }">
div.left <mk-time time="{ post.created_at }"></mk-time></a></div>
a.name(href={ CONFIG.url + '/' + post.user.username }, data-user-preview={ post.user_id }) </header>
| { post.user.name } <div class="body">
span.username <div class="text" ref="text"></div>
| @{ post.user.username } <div class="media" if="{ post.media }">
div.right <virtual each="{ file in post.media }"><img src="{ file.url + '?thumbnail&amp;size=512' }" alt="{ file.name }" title="{ file.name }"/></virtual>
a.time(href={ url }) </div>
mk-time(time={ post.created_at }) </div>
div.body </div>
div.text@text <style type="stylus">
div.media(if={ post.media }) :scope
virtual(each={ file in post.media })
img(src={ file.url + '?thumbnail&size=512' }, alt={ file.name }, title={ file.name })
style.
display block
margin 0
padding 20px 32px
background #fdfdfd
&:after
content ""
display block
clear both
&:hover
> .main > footer > button
color #888
> .avatar-anchor
display block
float left
margin 0 16px 0 0
> .avatar
display block display block
width 44px
height 44px
margin 0 margin 0
border-radius 4px padding 20px 32px
vertical-align bottom background #fdfdfd
> .main
float left
width calc(100% - 60px)
> header
margin-bottom 4px
white-space nowrap
&:after &:after
content "" content ""
display block display block
clear both clear both
> .left &:hover
float left > .main > footer > button
color #888
> .name > .avatar-anchor
display inline
margin 0
padding 0
color #777
font-size 1em
font-weight 700
text-align left
text-decoration none
&:hover
text-decoration underline
> .username
text-align left
margin 0 0 0 8px
color #ccc
> .right
float right
> .time
font-size 0.9em
color #c0c0c0
> .body
> .text
cursor default
display block display block
margin 0 float left
padding 0 margin 0 16px 0 0
word-wrap break-word
font-size 1em
color #717171
> mk-url-preview > .avatar
margin-top 8px
> .media
> img
display block display block
max-width 100% width 44px
height 44px
margin 0
border-radius 4px
vertical-align bottom
script. > .main
@mixin \api float left
@mixin \text width calc(100% - 60px)
@mixin \date-stringify
@mixin \user-preview
@post = @opts.post > header
margin-bottom 4px
white-space nowrap
@url = CONFIG.url + '/' + @post.user.username + '/' + @post.id &:after
content ""
display block
clear both
@title = @date-stringify @post.created_at > .left
float left
@on \mount ~> > .name
if @post.text? display inline
tokens = @analyze @post.text margin 0
@refs.text.innerHTML = @compile tokens padding 0
color #777
font-size 1em
font-weight 700
text-align left
text-decoration none
@refs.text.children.for-each (e) ~> &:hover
if e.tag-name == \MK-URL text-decoration underline
riot.mount e
@like = ~> > .username
if @post.is_liked text-align left
@api \posts/likes/delete do margin 0 0 0 8px
post_id: @post.id color #ccc
.then ~>
@post.is_liked = false > .right
@update! float right
else
@api \posts/likes/create do > .time
post_id: @post.id font-size 0.9em
.then ~> color #c0c0c0
@post.is_liked = true
@update! > .body
> .text
cursor default
display block
margin 0
padding 0
word-wrap break-word
font-size 1em
color #717171
> mk-url-preview
margin-top 8px
> .media
> img
display block
max-width 100%
</style>
<script>
@mixin \api
@mixin \text
@mixin \date-stringify
@mixin \user-preview
@post = @opts.post
@url = CONFIG.url + '/' + @post.user.username + '/' + @post.id
@title = @date-stringify @post.created_at
@on \mount ~>
if @post.text?
tokens = @analyze @post.text
@refs.text.innerHTML = @compile tokens
@refs.text.children.for-each (e) ~>
if e.tag-name == \MK-URL
riot.mount e
@like = ~>
if @post.is_liked
@api \posts/likes/delete do
post_id: @post.id
.then ~>
@post.is_liked = false
@update!
else
@api \posts/likes/create do
post_id: @post.id
.then ~>
@post.is_liked = true
@update!
</script>
</mk-post-detail-sub>

View file

@ -1,415 +1,409 @@
mk-post-detail(title={ title }) <mk-post-detail title="{ title }">
<div class="fetching" if="{ fetching }">
div.fetching(if={ fetching }) <mk-ellipsis-icon></mk-ellipsis-icon>
mk-ellipsis-icon </div>
<div class="main" if="{ !fetching }">
div.main(if={ !fetching }) <button class="read-more" if="{ p.reply_to &amp;&amp; p.reply_to.reply_to_id &amp;&amp; context == null }" title="会話をもっと読み込む" onclick="{ loadContext }" disabled="{ loadingContext }"><i class="fa fa-ellipsis-v" if="{ !loadingContext }"></i><i class="fa fa-spinner fa-pulse" if="{ loadingContext }"></i></button>
<div class="context">
button.read-more(if={ p.reply_to && p.reply_to.reply_to_id && context == null }, title='会話をもっと読み込む', onclick={ load-context }, disabled={ loading-context }) <virtual each="{ post in context }">
i.fa.fa-ellipsis-v(if={ !loading-context }) <mk-post-detail-sub post="{ post }"></mk-post-detail-sub>
i.fa.fa-spinner.fa-pulse(if={ loading-context }) </virtual>
</div>
div.context <div class="reply-to" if="{ p.reply_to }">
virtual(each={ post in context }) <mk-post-detail-sub post="{ p.reply_to }"></mk-post-detail-sub>
mk-post-detail-sub(post={ post }) </div>
<div class="repost" if="{ isRepost }">
div.reply-to(if={ p.reply_to }) <p><a class="avatar-anchor" href="{ CONFIG.url + '/' + post.user.username }" data-user-preview="{ post.user_id }"><img class="avatar" src="{ post.user.avatar_url + '?thumbnail&amp;size=32' }" alt="avatar"/></a><i class="fa fa-retweet"></i><a class="name" href="{ CONFIG.url + '/' + post.user.username }">{ post.user.name }</a>がRepost</p>
mk-post-detail-sub(post={ p.reply_to }) </div>
<article><a class="avatar-anchor" href="{ CONFIG.url + '/' + p.user.username }"><img class="avatar" src="{ p.user.avatar_url + '?thumbnail&amp;size=64' }" alt="avatar" data-user-preview="{ p.user.id }"/></a>
div.repost(if={ is-repost }) <header><a class="name" href="{ CONFIG.url + '/' + p.user.username }" data-user-preview="{ p.user.id }">{ p.user.name }</a><span class="username">@{ p.user.username }</span><a class="time" href="{ url }">
p <mk-time time="{ p.created_at }"></mk-time></a></header>
a.avatar-anchor(href={ CONFIG.url + '/' + post.user.username }, data-user-preview={ post.user_id }): img.avatar(src={ post.user.avatar_url + '?thumbnail&size=32' }, alt='avatar') <div class="body">
i.fa.fa-retweet <div class="text" ref="text"></div>
a.name(href={ CONFIG.url + '/' + post.user.username }) { post.user.name } <div class="media" if="{ p.media }">
| がRepost <virtual each="{ file in p.media }"><img src="{ file.url + '?thumbnail&amp;size=512' }" alt="{ file.name }" title="{ file.name }"/></virtual>
</div>
article </div>
a.avatar-anchor(href={ CONFIG.url + '/' + p.user.username }) <footer>
img.avatar(src={ p.user.avatar_url + '?thumbnail&size=64' }, alt='avatar', data-user-preview={ p.user.id }) <button onclick="{ reply }" title="返信"><i class="fa fa-reply"></i>
header <p class="count" if="{ p.replies_count &gt; 0 }">{ p.replies_count }</p>
a.name(href={ CONFIG.url + '/' + p.user.username }, data-user-preview={ p.user.id }) </button>
| { p.user.name } <button onclick="{ repost }" title="Repost"><i class="fa fa-retweet"></i>
span.username <p class="count" if="{ p.repost_count &gt; 0 }">{ p.repost_count }</p>
| @{ p.user.username } </button>
a.time(href={ url }) <button class="{ liked: p.is_liked }" onclick="{ like }" title="善哉"><i class="fa fa-thumbs-o-up"></i>
mk-time(time={ p.created_at }) <p class="count" if="{ p.likes_count &gt; 0 }">{ p.likes_count }</p>
div.body </button>
div.text@text <button onclick="{ NotImplementedException }"><i class="fa fa-ellipsis-h"></i></button>
div.media(if={ p.media }) </footer>
virtual(each={ file in p.media }) <div class="reposts-and-likes">
img(src={ file.url + '?thumbnail&size=512' }, alt={ file.name }, title={ file.name }) <div class="reposts" if="{ reposts &amp;&amp; reposts.length &gt; 0 }">
footer <header><a>{ p.repost_count }</a>
button(onclick={ reply }, title='返信') <p>Repost</p>
i.fa.fa-reply </header>
p.count(if={ p.replies_count > 0 }) { p.replies_count } <ol class="users">
button(onclick={ repost }, title='Repost') <li class="user" each="{ reposts }"><a class="avatar-anchor" href="{ CONFIG.url + '/' + user.username }" title="{ user.name }" data-user-preview="{ user.id }"><img class="avatar" src="{ user.avatar_url + '?thumbnail&amp;size=32' }" alt=""/></a></li>
i.fa.fa-retweet </ol>
p.count(if={ p.repost_count > 0 }) { p.repost_count } </div>
button(class={ liked: p.is_liked }, onclick={ like }, title='善哉') <div class="likes" if="{ likes &amp;&amp; likes.length &gt; 0 }">
i.fa.fa-thumbs-o-up <header><a>{ p.likes_count }</a>
p.count(if={ p.likes_count > 0 }) { p.likes_count } <p>いいね</p>
button(onclick={ NotImplementedException }): i.fa.fa-ellipsis-h </header>
div.reposts-and-likes <ol class="users">
div.reposts(if={ reposts && reposts.length > 0 }) <li class="user" each="{ likes }"><a class="avatar-anchor" href="{ CONFIG.url + '/' + username }" title="{ name }" data-user-preview="{ id }"><img class="avatar" src="{ avatar_url + '?thumbnail&amp;size=32' }" alt=""/></a></li>
header </ol>
a { p.repost_count } </div>
p Repost </div>
ol.users </article>
li.user(each={ reposts }) <div class="replies">
a.avatar-anchor(href={ CONFIG.url + '/' + user.username }, title={ user.name }, data-user-preview={ user.id }) <virtual each="{ post in replies }">
img.avatar(src={ user.avatar_url + '?thumbnail&size=32' }, alt='') <mk-post-detail-sub post="{ post }"></mk-post-detail-sub>
div.likes(if={ likes && likes.length > 0 }) </virtual>
header </div>
a { p.likes_count } </div>
p いいね <style type="stylus">
ol.users :scope
li.user(each={ likes })
a.avatar-anchor(href={ CONFIG.url + '/' + username }, title={ name }, data-user-preview={ id })
img.avatar(src={ avatar_url + '?thumbnail&size=32' }, alt='')
div.replies
virtual(each={ post in replies })
mk-post-detail-sub(post={ post })
style.
display block
margin 0
padding 0
width 640px
overflow hidden
background #fff
border solid 1px rgba(0, 0, 0, 0.1)
border-radius 8px
> .fetching
padding 64px 0
> .main
> .read-more
display block display block
margin 0 margin 0
padding 10px 0 padding 0
width 100% width 640px
font-size 1em overflow hidden
text-align center background #fff
color #999 border solid 1px rgba(0, 0, 0, 0.1)
cursor pointer border-radius 8px
background #fafafa
outline none
border none
border-bottom solid 1px #eef0f2
border-radius 6px 6px 0 0
&:hover > .fetching
background #f6f6f6 padding 64px 0
&:active > .main
background #f0f0f0
&:disabled > .read-more
color #ccc
> .context
> *
border-bottom 1px solid #eef0f2
> .repost
color #9dbb00
background linear-gradient(to bottom, #edfde2 0%, #fff 100%)
> p
margin 0
padding 16px 32px
.avatar-anchor
display inline-block
.avatar
vertical-align bottom
min-width 28px
min-height 28px
max-width 28px
max-height 28px
margin 0 8px 0 0
border-radius 6px
i
margin-right 4px
.name
font-weight bold
& + article
padding-top 8px
> .reply-to
border-bottom 1px solid #eef0f2
> article
padding 28px 32px 18px 32px
&:after
content ""
display block
clear both
&:hover
> .main > footer > button
color #888
> .avatar-anchor
display block
width 60px
height 60px
> .avatar
display block display block
width 60px
height 60px
margin 0 margin 0
border-radius 8px padding 10px 0
vertical-align bottom width 100%
> header
position absolute
top 28px
left 108px
width calc(100% - 108px)
> .name
display inline-block
margin 0
line-height 24px
color #777
font-size 18px
font-weight 700
text-align left
text-decoration none
&:hover
text-decoration underline
> .username
display block
text-align left
margin 0
color #ccc
> .time
position absolute
top 0
right 32px
font-size 1em font-size 1em
color #c0c0c0 text-align center
color #999
> .body
padding 8px 0
> .text
cursor default
display block
margin 0
padding 0
word-wrap break-word
font-size 1.5em
color #717171
> mk-url-preview
margin-top 8px
> .media
> img
display block
max-width 100%
> footer
font-size 1.2em
> button
margin 0 28px 0 0
padding 8px
background transparent
border none
font-size 1em
color #ddd
cursor pointer cursor pointer
background #fafafa
outline none
border none
border-bottom solid 1px #eef0f2
border-radius 6px 6px 0 0
&:hover &:hover
color #666 background #f6f6f6
> .count &:active
display inline background #f0f0f0
margin 0 0 0 8px
color #999
&.liked &:disabled
color $theme-color color #ccc
> .reposts-and-likes > .context
display flex > *
justify-content center border-bottom 1px solid #eef0f2
padding 0
margin 16px 0
&:empty > .repost
display none color #9dbb00
background linear-gradient(to bottom, #edfde2 0%, #fff 100%)
> .reposts > p
> .likes margin 0
display flex padding 16px 32px
flex 1 1
padding 0 .avatar-anchor
border-top solid 1px #F2EFEE display inline-block
.avatar
vertical-align bottom
min-width 28px
min-height 28px
max-width 28px
max-height 28px
margin 0 8px 0 0
border-radius 6px
i
margin-right 4px
.name
font-weight bold
& + article
padding-top 8px
> .reply-to
border-bottom 1px solid #eef0f2
> article
padding 28px 32px 18px 32px
&:after
content ""
display block
clear both
&:hover
> .main > footer > button
color #888
> .avatar-anchor
display block
width 60px
height 60px
> .avatar
display block
width 60px
height 60px
margin 0
border-radius 8px
vertical-align bottom
> header > header
flex 1 1 80px position absolute
max-width 80px top 28px
padding 8px 5px 0px 10px left 108px
width calc(100% - 108px)
> a > .name
display inline-block
margin 0
line-height 24px
color #777
font-size 18px
font-weight 700
text-align left
text-decoration none
&:hover
text-decoration underline
> .username
display block display block
font-size 1.5em text-align left
line-height 1.4em margin 0
color #ccc
> p > .time
position absolute
top 0
right 32px
font-size 1em
color #c0c0c0
> .body
padding 8px 0
> .text
cursor default
display block display block
margin 0 margin 0
font-size 0.7em
line-height 1em
font-weight normal
color #a0a2a5
> .users
display block
flex 1 1
margin 0
padding 10px 10px 10px 5px
list-style none
> .user
display block
float left
margin 4px
padding 0 padding 0
word-wrap break-word
font-size 1.5em
color #717171
> .avatar-anchor > mk-url-preview
display:block margin-top 8px
> .avatar > .media
vertical-align bottom > img
width 24px display block
height 24px max-width 100%
border-radius 4px
> .reposts + .likes > footer
margin-left 16px font-size 1.2em
> .replies > button
> * margin 0 28px 0 0
border-top 1px solid #eef0f2 padding 8px
background transparent
border none
font-size 1em
color #ddd
cursor pointer
script. &:hover
@mixin \api color #666
@mixin \text
@mixin \user-preview
@mixin \date-stringify
@mixin \NotImplementedException
@fetching = true > .count
@loading-context = false display inline
@content = null margin 0 0 0 8px
@post = null color #999
@on \mount ~> &.liked
color $theme-color
@api \posts/show do > .reposts-and-likes
post_id: @opts.post display flex
.then (post) ~> justify-content center
@fetching = false padding 0
@post = post margin 16px 0
@trigger \loaded
@is-repost = @post.repost? &:empty
@p = if @is-repost then @post.repost else @post display none
@title = @date-stringify @p.created_at > .reposts
> .likes
display flex
flex 1 1
padding 0
border-top solid 1px #F2EFEE
@update! > header
flex 1 1 80px
max-width 80px
padding 8px 5px 0px 10px
if @p.text? > a
tokens = @analyze @p.text display block
@refs.text.innerHTML = @compile tokens font-size 1.5em
line-height 1.4em
@refs.text.children.for-each (e) ~> > p
if e.tag-name == \MK-URL display block
riot.mount e margin 0
font-size 0.7em
line-height 1em
font-weight normal
color #a0a2a5
# URLをプレビュー > .users
tokens display block
.filter (t) -> t.type == \link flex 1 1
.map (t) ~> margin 0
@preview = @refs.text.append-child document.create-element \mk-url-preview padding 10px 10px 10px 5px
riot.mount @preview, do list-style none
url: t.content
> .user
display block
float left
margin 4px
padding 0
> .avatar-anchor
display:block
> .avatar
vertical-align bottom
width 24px
height 24px
border-radius 4px
> .reposts + .likes
margin-left 16px
> .replies
> *
border-top 1px solid #eef0f2
</style>
<script>
@mixin \api
@mixin \text
@mixin \user-preview
@mixin \date-stringify
@mixin \NotImplementedException
@fetching = true
@loading-context = false
@content = null
@post = null
@on \mount ~>
@api \posts/show do
post_id: @opts.post
.then (post) ~>
@fetching = false
@post = post
@trigger \loaded
@is-repost = @post.repost?
@p = if @is-repost then @post.repost else @post
@title = @date-stringify @p.created_at
# Get likes
@api \posts/likes do
post_id: @p.id
limit: 8
.then (likes) ~>
@likes = likes
@update! @update!
# Get reposts if @p.text?
@api \posts/reposts do tokens = @analyze @p.text
post_id: @p.id @refs.text.innerHTML = @compile tokens
limit: 8
.then (reposts) ~> @refs.text.children.for-each (e) ~>
@reposts = reposts if e.tag-name == \MK-URL
riot.mount e
# URLをプレビュー
tokens
.filter (t) -> t.type == \link
.map (t) ~>
@preview = @refs.text.append-child document.create-element \mk-url-preview
riot.mount @preview, do
url: t.content
# Get likes
@api \posts/likes do
post_id: @p.id
limit: 8
.then (likes) ~>
@likes = likes
@update!
# Get reposts
@api \posts/reposts do
post_id: @p.id
limit: 8
.then (reposts) ~>
@reposts = reposts
@update!
# Get replies
@api \posts/replies do
post_id: @p.id
limit: 8
.then (replies) ~>
@replies = replies
@update!
@update! @update!
# Get replies @reply = ~>
@api \posts/replies do form = document.body.append-child document.create-element \mk-post-form-window
post_id: @p.id riot.mount form, do
limit: 8 reply: @p
.then (replies) ~>
@replies = replies @repost = ~>
form = document.body.append-child document.create-element \mk-repost-form-window
riot.mount form, do
post: @p
@like = ~>
if @p.is_liked
@api \posts/likes/delete do
post_id: @p.id
.then ~>
@p.is_liked = false
@update!
else
@api \posts/likes/create do
post_id: @p.id
.then ~>
@p.is_liked = true
@update!
@load-context = ~>
@loading-context = true
# Get context
@api \posts/context do
post_id: @p.reply_to_id
.then (context) ~>
@context = context.reverse!
@loading-context = false
@update! @update!
</script>
@update! </mk-post-detail>
@reply = ~>
form = document.body.append-child document.create-element \mk-post-form-window
riot.mount form, do
reply: @p
@repost = ~>
form = document.body.append-child document.create-element \mk-repost-form-window
riot.mount form, do
post: @p
@like = ~>
if @p.is_liked
@api \posts/likes/delete do
post_id: @p.id
.then ~>
@p.is_liked = false
@update!
else
@api \posts/likes/create do
post_id: @p.id
.then ~>
@p.is_liked = true
@update!
@load-context = ~>
@loading-context = true
# Get context
@api \posts/context do
post_id: @p.reply_to_id
.then (context) ~>
@context = context.reverse!
@loading-context = false
@update!

View file

@ -1,60 +1,55 @@
mk-post-form-window <mk-post-form-window>
<mk-window ref="window" is-modal="{ true }" colored="{ true }"><yield to="header"><span if="{ !parent.opts.reply }">新規投稿</span><span if="{ parent.opts.reply }">返信</span><span class="files" if="{ parent.files.length != 0 }">添付: { parent.files.length }ファイル</span><span class="uploading-files" if="{ parent.uploadingFiles.length != 0 }">{ parent.uploadingFiles.length }個のファイルをアップロード中
<mk-ellipsis></mk-ellipsis></span></yield>
<yield to="content">
<div class="ref" if="{ parent.opts.reply }">
<mk-post-preview post="{ parent.opts.reply }"></mk-post-preview>
</div>
<div class="body">
<mk-post-form ref="form" reply="{ parent.opts.reply }"></mk-post-form>
</div></yield>
</mk-window>
<style type="stylus">
:scope
> mk-window
mk-window@window(is-modal={ true }, colored={ true }) [data-yield='header']
> .files
> .uploading-files
margin-left 8px
opacity 0.8
<yield to="header"> &:before
span(if={ !parent.opts.reply }) 新規投稿 content '('
span(if={ parent.opts.reply }) 返信
span.files(if={ parent.files.length != 0 }) 添付: { parent.files.length }ファイル
span.uploading-files(if={ parent.uploading-files.length != 0 })
| { parent.uploading-files.length }個のファイルをアップロード中
mk-ellipsis
</yield>
<yield to="content"> &:after
div.ref(if={ parent.opts.reply }) content ')'
mk-post-preview(post={ parent.opts.reply })
div.body
mk-post-form@form(reply={ parent.opts.reply })
</yield>
style. [data-yield='content']
> mk-window > .ref
> mk-post-preview
margin 16px 22px
[data-yield='header'] </style>
> .files <script>
> .uploading-files @uploading-files = []
margin-left 8px @files = []
opacity 0.8
&:before @on \mount ~>
content '(' @refs.window.refs.form.focus!
&:after @refs.window.on \closed ~>
content ')' @unmount!
[data-yield='content'] @refs.window.refs.form.on \post ~>
> .ref @refs.window.close!
> mk-post-preview
margin 16px 22px
script. @refs.window.refs.form.on \change-uploading-files (files) ~>
@uploading-files = [] @uploading-files = files
@files = [] @update!
@on \mount ~> @refs.window.refs.form.on \change-files (files) ~>
@refs.window.refs.form.focus! @files = files
@update!
@refs.window.on \closed ~> </script>
@unmount! </mk-post-form-window>
@refs.window.refs.form.on \post ~>
@refs.window.close!
@refs.window.refs.form.on \change-uploading-files (files) ~>
@uploading-files = files
@update!
@refs.window.refs.form.on \change-files (files) ~>
@files = files
@update!

View file

@ -1,430 +1,434 @@
mk-post-form(ondragover={ ondragover }, ondragenter={ ondragenter }, ondragleave={ ondragleave }, ondrop={ ondrop }) <mk-post-form ondragover="{ ondragover }" ondragenter="{ ondragenter }" ondragleave="{ ondragleave }" ondrop="{ ondrop }">
textarea@text(disabled={ wait }, class={ withfiles: files.length != 0 }, oninput={ update }, onkeydown={ onkeydown }, onpaste={ onpaste }, placeholder={ opts.reply ? 'この投稿への返信...' : 'いまどうしてる?' }) <textarea class="{ withfiles: files.length != 0 }" ref="text" disabled="{ wait }" oninput="{ update }" onkeydown="{ onkeydown }" onpaste="{ onpaste }" placeholder="{ opts.reply ? 'この投稿への返信...' : 'いまどうしてる?' }"></textarea>
div.attaches(if={ files.length != 0 }) <div class="attaches" if="{ files.length != 0 }">
ul.files@attaches <ul class="files" ref="attaches">
li.file(each={ files }) <li class="file" each="{ files }">
div.img(style='background-image: url({ url + "?thumbnail&size=64" })', title={ name }) <div class="img" style="background-image: url({ url + &quot;?thumbnail&amp;size=64&quot; })" title="{ name }"></div><img class="remove" onclick="{ _remove }" src="/_/resources/desktop/remove.png" title="添付取り消し" alt=""/>
img.remove(onclick={ _remove }, src='/_/resources/desktop/remove.png', title='添付取り消し', alt='') </li>
li.add(if={ files.length < 4 }, title='PCからファイルを添付', onclick={ select-file }): i.fa.fa-plus <li class="add" if="{ files.length &lt; 4 }" title="PCからファイルを添付" onclick="{ selectFile }"><i class="fa fa-plus"></i></li>
p.remain </ul>
| 残り{ 4 - files.length } <p class="remain">残り{ 4 - files.length }</p>
mk-uploader@uploader </div>
button@upload(title='PCからファイルを添付', onclick={ select-file }): i.fa.fa-upload <mk-uploader ref="uploader"></mk-uploader>
button@drive(title='ドライブからファイルを添付', onclick={ select-file-from-drive }): i.fa.fa-cloud <button ref="upload" title="PCからファイルを添付" onclick="{ selectFile }"><i class="fa fa-upload"></i></button>
p.text-count(class={ over: refs.text.value.length > 300 }) のこり{ 300 - refs.text.value.length }文字 <button ref="drive" title="ドライブからファイルを添付" onclick="{ selectFileFromDrive }"><i class="fa fa-cloud"></i></button>
button@submit(class={ wait: wait }, disabled={ wait || (refs.text.value.length == 0 && files.length == 0) }, onclick={ post }) <p class="text-count { over: refs.text.value.length &gt; 300 }">のこり{ 300 - refs.text.value.length }文字</p>
| { wait ? '投稿中' : opts.reply ? '返信' : '投稿' } <button class="{ wait: wait }" ref="submit" disabled="{ wait || (refs.text.value.length == 0 &amp;&amp; files.length == 0) }" onclick="{ post }">{ wait ? '投稿中' : opts.reply ? '返信' : '投稿' }
mk-ellipsis(if={ wait }) <mk-ellipsis if="{ wait }"></mk-ellipsis>
input@file(type='file', accept='image/*', multiple, tabindex='-1', onchange={ change-file }) </button>
div.dropzone(if={ draghover }) <input ref="file" type="file" accept="image/*" multiple="multiple" tabindex="-1" onchange="{ changeFile }"/>
<div class="dropzone" if="{ draghover }"></div>
style. <style type="stylus">
display block :scope
padding 16px
background lighten($theme-color, 95%)
&:after
content ""
display block
clear both
> .attaches
margin 0
padding 0
background lighten($theme-color, 98%)
border solid 1px rgba($theme-color, 0.1)
border-top none
border-radius 0 0 4px 4px
transition border-color .3s ease
> .remain
display block display block
position absolute padding 16px
top 8px background lighten($theme-color, 95%)
right 8px
margin 0
padding 0
color rgba($theme-color, 0.4)
> .files
display block
margin 0
padding 4px
list-style none
&:after &:after
content "" content ""
display block display block
clear both clear both
> .file > .attaches
display block margin 0
float left
margin 4px
padding 0 padding 0
cursor move background lighten($theme-color, 98%)
border solid 1px rgba($theme-color, 0.1)
border-top none
border-radius 0 0 4px 4px
transition border-color .3s ease
&:hover > .remove > .remain
display block display block
> .img
width 64px
height 64px
background-size cover
background-position center center
> .remove
display none
position absolute position absolute
top -6px top 8px
right -6px right 8px
width 16px margin 0
height 16px padding 0
cursor pointer color rgba($theme-color, 0.4)
> .add > .files
display block
margin 0
padding 4px
list-style none
&:after
content ""
display block
clear both
> .file
display block
float left
margin 4px
padding 0
cursor move
&:hover > .remove
display block
> .img
width 64px
height 64px
background-size cover
background-position center center
> .remove
display none
position absolute
top -6px
right -6px
width 16px
height 16px
cursor pointer
> .add
display block
float left
margin 4px
padding 0
border dashed 2px rgba($theme-color, 0.2)
cursor pointer
&:hover
border-color rgba($theme-color, 0.3)
> i
color rgba($theme-color, 0.4)
> i
display block
width 60px
height 60px
line-height 60px
text-align center
font-size 1.2em
color rgba($theme-color, 0.2)
> mk-uploader
margin 8px 0 0 0
padding 8px
border solid 1px rgba($theme-color, 0.2)
border-radius 4px
[ref='file']
display none
[ref='text']
display block display block
float left padding 12px
margin 4px margin 0
padding 0 width 100%
border dashed 2px rgba($theme-color, 0.2) max-width 100%
cursor pointer min-width 100%
min-height calc(16px + 12px + 12px)
font-size 16px
color #333
background #fff
outline none
border solid 1px rgba($theme-color, 0.1)
border-radius 4px
transition border-color .3s ease
&:hover &:hover
border-color rgba($theme-color, 0.2)
transition border-color .1s ease
&:focus
color $theme-color
border-color rgba($theme-color, 0.5)
transition border-color 0s ease
&:disabled
opacity 0.5
&::-webkit-input-placeholder
color rgba($theme-color, 0.3)
&.withfiles
border-bottom solid 1px rgba($theme-color, 0.1) !important
border-radius 4px 4px 0 0
&:hover + .attaches
border-color rgba($theme-color, 0.2)
transition border-color .1s ease
&:focus + .attaches
border-color rgba($theme-color, 0.5)
transition border-color 0s ease
.text-count
pointer-events none
display block
position absolute
bottom 16px
right 138px
margin 0
line-height 40px
color rgba($theme-color, 0.5)
&.over
color #ec3828
[ref='submit']
display block
position absolute
bottom 16px
right 16px
cursor pointer
padding 0
margin 0
width 110px
height 40px
font-size 1em
color $theme-color-foreground
background linear-gradient(to bottom, lighten($theme-color, 25%) 0%, lighten($theme-color, 10%) 100%)
outline none
border solid 1px lighten($theme-color, 15%)
border-radius 4px
&:not(:disabled)
font-weight bold
&:hover:not(:disabled)
background linear-gradient(to bottom, lighten($theme-color, 8%) 0%, darken($theme-color, 8%) 100%)
border-color $theme-color
&:active:not(:disabled)
background $theme-color
border-color $theme-color
&:focus
&:after
content ""
pointer-events none
position absolute
top -5px
right -5px
bottom -5px
left -5px
border 2px solid rgba($theme-color, 0.3)
border-radius 8px
&:disabled
opacity 0.7
cursor default
&.wait
background linear-gradient(
45deg,
darken($theme-color, 10%) 25%,
$theme-color 25%,
$theme-color 50%,
darken($theme-color, 10%) 50%,
darken($theme-color, 10%) 75%,
$theme-color 75%,
$theme-color
)
background-size 32px 32px
animation stripe-bg 1.5s linear infinite
opacity 0.7
cursor wait
@keyframes stripe-bg
from {background-position: 0 0;}
to {background-position: -64px 32px;}
[ref='upload']
[ref='drive']
display inline-block
cursor pointer
padding 0
margin 8px 4px 0 0
width 40px
height 40px
font-size 1em
color rgba($theme-color, 0.5)
background transparent
outline none
border solid 1px transparent
border-radius 4px
&:hover
background transparent
border-color rgba($theme-color, 0.3) border-color rgba($theme-color, 0.3)
> i &:active
color rgba($theme-color, 0.4) color rgba($theme-color, 0.6)
background linear-gradient(to bottom, lighten($theme-color, 80%) 0%, lighten($theme-color, 90%) 100%)
border-color rgba($theme-color, 0.5)
box-shadow 0 2px 4px rgba(0, 0, 0, 0.15) inset
> i &:focus
display block &:after
width 60px content ""
height 60px pointer-events none
line-height 60px position absolute
text-align center top -5px
font-size 1.2em right -5px
color rgba($theme-color, 0.2) bottom -5px
left -5px
border 2px solid rgba($theme-color, 0.3)
border-radius 8px
> mk-uploader > .dropzone
margin 8px 0 0 0
padding 8px
border solid 1px rgba($theme-color, 0.2)
border-radius 4px
[ref='file']
display none
[ref='text']
display block
padding 12px
margin 0
width 100%
max-width 100%
min-width 100%
min-height calc(16px + 12px + 12px)
font-size 16px
color #333
background #fff
outline none
border solid 1px rgba($theme-color, 0.1)
border-radius 4px
transition border-color .3s ease
&:hover
border-color rgba($theme-color, 0.2)
transition border-color .1s ease
&:focus
color $theme-color
border-color rgba($theme-color, 0.5)
transition border-color 0s ease
&:disabled
opacity 0.5
&::-webkit-input-placeholder
color rgba($theme-color, 0.3)
&.withfiles
border-bottom solid 1px rgba($theme-color, 0.1) !important
border-radius 4px 4px 0 0
&:hover + .attaches
border-color rgba($theme-color, 0.2)
transition border-color .1s ease
&:focus + .attaches
border-color rgba($theme-color, 0.5)
transition border-color 0s ease
.text-count
pointer-events none
display block
position absolute
bottom 16px
right 138px
margin 0
line-height 40px
color rgba($theme-color, 0.5)
&.over
color #ec3828
[ref='submit']
display block
position absolute
bottom 16px
right 16px
cursor pointer
padding 0
margin 0
width 110px
height 40px
font-size 1em
color $theme-color-foreground
background linear-gradient(to bottom, lighten($theme-color, 25%) 0%, lighten($theme-color, 10%) 100%)
outline none
border solid 1px lighten($theme-color, 15%)
border-radius 4px
&:not(:disabled)
font-weight bold
&:hover:not(:disabled)
background linear-gradient(to bottom, lighten($theme-color, 8%) 0%, darken($theme-color, 8%) 100%)
border-color $theme-color
&:active:not(:disabled)
background $theme-color
border-color $theme-color
&:focus
&:after
content ""
pointer-events none
position absolute position absolute
top -5px left 0
right -5px top 0
bottom -5px width 100%
left -5px height 100%
border 2px solid rgba($theme-color, 0.3) border dashed 2px rgba($theme-color, 0.5)
border-radius 8px
&:disabled
opacity 0.7
cursor default
&.wait
background linear-gradient(
45deg,
darken($theme-color, 10%) 25%,
$theme-color 25%,
$theme-color 50%,
darken($theme-color, 10%) 50%,
darken($theme-color, 10%) 75%,
$theme-color 75%,
$theme-color
)
background-size 32px 32px
animation stripe-bg 1.5s linear infinite
opacity 0.7
cursor wait
@keyframes stripe-bg
from {background-position: 0 0;}
to {background-position: -64px 32px;}
[ref='upload']
[ref='drive']
display inline-block
cursor pointer
padding 0
margin 8px 4px 0 0
width 40px
height 40px
font-size 1em
color rgba($theme-color, 0.5)
background transparent
outline none
border solid 1px transparent
border-radius 4px
&:hover
background transparent
border-color rgba($theme-color, 0.3)
&:active
color rgba($theme-color, 0.6)
background linear-gradient(to bottom, lighten($theme-color, 80%) 0%, lighten($theme-color, 90%) 100%)
border-color rgba($theme-color, 0.5)
box-shadow 0 2px 4px rgba(0, 0, 0, 0.15) inset
&:focus
&:after
content ""
pointer-events none pointer-events none
position absolute
top -5px
right -5px
bottom -5px
left -5px
border 2px solid rgba($theme-color, 0.3)
border-radius 8px
> .dropzone </style>
position absolute <script>
left 0 @mixin \api
top 0 @mixin \notify
width 100% @mixin \autocomplete
height 100% @mixin \sortable
border dashed 2px rgba($theme-color, 0.5)
pointer-events none
script. @wait = false
@mixin \api @uploadings = []
@mixin \notify
@mixin \autocomplete
@mixin \sortable
@wait = false
@uploadings = []
@files = []
@autocomplete = null
@in-reply-to-post = @opts.reply
# https://github.com/riot/riot/issues/2080
if @in-reply-to-post == '' then @in-reply-to-post = null
@on \mount ~>
@refs.uploader.on \uploaded (file) ~>
@add-file file
@refs.uploader.on \change-uploads (uploads) ~>
@trigger \change-uploading-files uploads
@autocomplete = new @Autocomplete @refs.text
@autocomplete.attach!
@on \unmount ~>
@autocomplete.detach!
@focus = ~>
@refs.text.focus!
@clear = ~>
@refs.text.value = ''
@files = [] @files = []
@trigger \change-files @autocomplete = null
@update!
@ondragover = (e) ~> @in-reply-to-post = @opts.reply
e.stop-propagation!
@draghover = true
# ドラッグされてきたものがファイルだったら
if e.data-transfer.effect-allowed == \all
e.data-transfer.drop-effect = \copy
else
e.data-transfer.drop-effect = \move
return false
@ondragenter = (e) ~> # https://github.com/riot/riot/issues/2080
@draghover = true if @in-reply-to-post == '' then @in-reply-to-post = null
@ondragleave = (e) ~> @on \mount ~>
@draghover = false @refs.uploader.on \uploaded (file) ~>
@add-file file
@ondrop = (e) ~> @refs.uploader.on \change-uploads (uploads) ~>
e.prevent-default! @trigger \change-uploading-files uploads
e.stop-propagation!
@draghover = false
# ファイルだったら @autocomplete = new @Autocomplete @refs.text
if e.data-transfer.files.length > 0 @autocomplete.attach!
Array.prototype.for-each.call e.data-transfer.files, (file) ~>
@on \unmount ~>
@autocomplete.detach!
@focus = ~>
@refs.text.focus!
@clear = ~>
@refs.text.value = ''
@files = []
@trigger \change-files
@update!
@ondragover = (e) ~>
e.stop-propagation!
@draghover = true
# ドラッグされてきたものがファイルだったら
if e.data-transfer.effect-allowed == \all
e.data-transfer.drop-effect = \copy
else
e.data-transfer.drop-effect = \move
return false
@ondragenter = (e) ~>
@draghover = true
@ondragleave = (e) ~>
@draghover = false
@ondrop = (e) ~>
e.prevent-default!
e.stop-propagation!
@draghover = false
# ファイルだったら
if e.data-transfer.files.length > 0
Array.prototype.for-each.call e.data-transfer.files, (file) ~>
@upload file
return false
# データ取得
data = e.data-transfer.get-data 'text'
if !data?
return false
try
# パース
obj = JSON.parse data
# (ドライブの)ファイルだったら
if obj.type == \file
@add-file obj.file
catch
# ignore
return false
@onkeydown = (e) ~>
if (e.which == 10 || e.which == 13) && (e.ctrl-key || e.meta-key)
@post!
@onpaste = (e) ~>
data = e.clipboard-data
items = data.items
for i from 0 to items.length - 1
item = items[i]
switch (item.kind)
| \file =>
@upload item.get-as-file!
@select-file = ~>
@refs.file.click!
@select-file-from-drive = ~>
browser = document.body.append-child document.create-element \mk-select-file-from-drive-window
i = riot.mount browser, do
multiple: true
i[0].one \selected (files) ~>
files.for-each @add-file
@change-file = ~>
files = @refs.file.files
for i from 0 to files.length - 1
file = files.item i
@upload file @upload file
return false
# データ取得 @upload = (file) ~>
data = e.data-transfer.get-data 'text' @refs.uploader.upload file
if !data?
return false
try @add-file = (file) ~>
# パース file._remove = ~>
obj = JSON.parse data @files = @files.filter (x) -> x.id != file.id
@trigger \change-files @files
@update!
# (ドライブの)ファイルだったら @files.push file
if obj.type == \file
@add-file obj.file
catch
# ignore
return false
@onkeydown = (e) ~>
if (e.which == 10 || e.which == 13) && (e.ctrl-key || e.meta-key)
@post!
@onpaste = (e) ~>
data = e.clipboard-data
items = data.items
for i from 0 to items.length - 1
item = items[i]
switch (item.kind)
| \file =>
@upload item.get-as-file!
@select-file = ~>
@refs.file.click!
@select-file-from-drive = ~>
browser = document.body.append-child document.create-element \mk-select-file-from-drive-window
i = riot.mount browser, do
multiple: true
i[0].one \selected (files) ~>
files.for-each @add-file
@change-file = ~>
files = @refs.file.files
for i from 0 to files.length - 1
file = files.item i
@upload file
@upload = (file) ~>
@refs.uploader.upload file
@add-file = (file) ~>
file._remove = ~>
@files = @files.filter (x) -> x.id != file.id
@trigger \change-files @files @trigger \change-files @files
@update! @update!
@files.push file new @Sortable @refs.attaches, do
@trigger \change-files @files draggable: \.file
@update! animation: 150ms
new @Sortable @refs.attaches, do @post = (e) ~>
draggable: \.file @wait = true
animation: 150ms
@post = (e) ~> files = if @files? and @files.length > 0
@wait = true then @files.map (f) -> f.id
else undefined
files = if @files? and @files.length > 0 @api \posts/create do
then @files.map (f) -> f.id text: @refs.text.value
else undefined media_ids: files
reply_to_id: if @in-reply-to-post? then @in-reply-to-post.id else undefined
@api \posts/create do .then (data) ~>
text: @refs.text.value @trigger \post
media_ids: files @notify if @in-reply-to-post? then '返信しました!' else '投稿しました!'
reply_to_id: if @in-reply-to-post? then @in-reply-to-post.id else undefined .catch (err) ~>
.then (data) ~> console.error err
@trigger \post @notify '投稿できませんでした'
@notify if @in-reply-to-post? then '返信しました!' else '投稿しました!' .then ~>
.catch (err) ~> @wait = false
console.error err @update!
@notify '投稿できませんでした' </script>
.then ~> </mk-post-form>
@wait = false
@update!

View file

@ -1,94 +1,93 @@
mk-post-preview(title={ title }) <mk-post-preview title="{ title }">
article <article><a class="avatar-anchor" href="{ CONFIG.url + '/' + post.user.username }"><img class="avatar" src="{ post.user.avatar_url + '?thumbnail&amp;size=64' }" alt="avatar" data-user-preview="{ post.user_id }"/></a>
a.avatar-anchor(href={ CONFIG.url + '/' + post.user.username }) <div class="main">
img.avatar(src={ post.user.avatar_url + '?thumbnail&size=64' }, alt='avatar', data-user-preview={ post.user_id }) <header><a class="name" href="{ CONFIG.url + '/' + post.user.username }" data-user-preview="{ post.user_id }">{ post.user.name }</a><span class="username">@{ post.user.username }</span><a class="time" href="{ CONFIG.url + '/' + post.user.username + '/' + post.id }">
div.main <mk-time time="{ post.created_at }"></mk-time></a></header>
header <div class="body">
a.name(href={ CONFIG.url + '/' + post.user.username }, data-user-preview={ post.user_id }) <mk-sub-post-content class="text" post="{ post }"></mk-sub-post-content>
| { post.user.name } </div>
span.username </div>
| @{ post.user.username } </article>
a.time(href={ CONFIG.url + '/' + post.user.username + '/' + post.id }) <style type="stylus">
mk-time(time={ post.created_at }) :scope
div.body
mk-sub-post-content.text(post={ post })
style.
display block
margin 0
padding 0
font-size 0.9em
background #fff
> article
&:after
content ""
display block display block
clear both margin 0
padding 0
font-size 0.9em
background #fff
&:hover > article
> .main > footer > button
color #888
> .avatar-anchor &:after
display block content ""
float left display block
margin 0 16px 0 0 clear both
> .avatar &:hover
display block > .main > footer > button
width 52px color #888
height 52px
margin 0
border-radius 8px
vertical-align bottom
> .main > .avatar-anchor
float left display block
width calc(100% - 68px) float left
margin 0 16px 0 0
> header > .avatar
margin-bottom 4px display block
white-space nowrap width 52px
height 52px
margin 0
border-radius 8px
vertical-align bottom
> .name > .main
display inline float left
margin 0 width calc(100% - 68px)
padding 0
color #607073
font-size 1em
font-weight 700
text-align left
text-decoration none
&:hover > header
text-decoration underline margin-bottom 4px
white-space nowrap
> .username > .name
text-align left display inline
margin 0 0 0 8px margin 0
color #d1d8da padding 0
color #607073
font-size 1em
font-weight 700
text-align left
text-decoration none
> .time &:hover
position absolute text-decoration underline
top 0
right 0
color #b2b8bb
> .body > .username
text-align left
margin 0 0 0 8px
color #d1d8da
> .text > .time
cursor default position absolute
margin 0 top 0
padding 0 right 0
font-size 1.1em color #b2b8bb
color #717171
script. > .body
@mixin \date-stringify
@mixin \user-preview
@post = @opts.post > .text
cursor default
margin 0
padding 0
font-size 1.1em
color #717171
@title = @date-stringify @post.created_at </style>
<script>
@mixin \date-stringify
@mixin \user-preview
@post = @opts.post
@title = @date-stringify @post.created_at
</script>
</mk-post-preview>

View file

@ -1,72 +1,75 @@
mk-post-status-graph <mk-post-status-graph>
canvas@canv(width={ opts.width }, height={ opts.height }) <canvas ref="canv" width="{ opts.width }" height="{ opts.height }"></canvas>
<style type="stylus">
:scope
display block
style. > canvas
display block margin 0 auto
> canvas </style>
margin 0 auto <script>
@mixin \api
@mixin \is-promise
script. @post = null
@mixin \api @post-promise = if @is-promise @opts.post then @opts.post else Promise.resolve @opts.post
@mixin \is-promise
@post = null @on \mount ~>
@post-promise = if @is-promise @opts.post then @opts.post else Promise.resolve @opts.post post <~ @post-promise.then
@post = post
@update!
@on \mount ~> @api \aggregation/posts/like do
post <~ @post-promise.then
@post = post
@update!
@api \aggregation/posts/like do
post_id: @post.id
limit: 30days
.then (likes) ~>
likes = likes.reverse!
@api \aggregation/posts/repost do
post_id: @post.id post_id: @post.id
limit: 30days limit: 30days
.then (repost) ~> .then (likes) ~>
repost = repost.reverse! likes = likes.reverse!
@api \aggregation/posts/reply do @api \aggregation/posts/repost do
post_id: @post.id post_id: @post.id
limit: 30days limit: 30days
.then (replies) ~> .then (repost) ~>
replies = replies.reverse! repost = repost.reverse!
new Chart @refs.canv, do @api \aggregation/posts/reply do
type: \bar post_id: @post.id
data: limit: 30days
labels: likes.map (x, i) ~> if i % 3 == 2 then x.date.day + '日' else '' .then (replies) ~>
datasets: [ replies = replies.reverse!
{
label: \いいね new Chart @refs.canv, do
type: \line type: \bar
data: likes.map (x) ~> x.count data:
line-tension: 0 labels: likes.map (x, i) ~> if i % 3 == 2 then x.date.day + '日' else ''
border-width: 2 datasets: [
fill: true {
background-color: 'rgba(247, 121, 108, 0.2)' label: \いいね
point-background-color: \#fff type: \line
point-radius: 4 data: likes.map (x) ~> x.count
point-border-width: 2 line-tension: 0
border-color: \#F7796C border-width: 2
}, fill: true
{ background-color: 'rgba(247, 121, 108, 0.2)'
label: \返信 point-background-color: \#fff
type: \bar point-radius: 4
data: replies.map (x) ~> x.count point-border-width: 2
background-color: \#555 border-color: \#F7796C
}, },
{ {
label: \Repost label: \返信
type: \bar type: \bar
data: repost.map (x) ~> x.count data: replies.map (x) ~> x.count
background-color: \#a2d61e background-color: \#555
} },
] {
options: label: \Repost
responsive: false type: \bar
data: repost.map (x) ~> x.count
background-color: \#a2d61e
}
]
options:
responsive: false
</script>
</mk-post-status-graph>

View file

@ -1,92 +1,94 @@
mk-progress-dialog <mk-progress-dialog>
mk-window@window(is-modal={ false }, can-close={ false }, width={ '500px' }) <mk-window ref="window" is-modal="{ false }" can-close="{ false }" width="{ '500px' }">
<yield to="header"> <yield to="header">{ parent.title }
| { parent.title } <mk-ellipsis></mk-ellipsis></yield>
mk-ellipsis <yield to="content">
</yield> <div class="body">
<yield to="content"> <p class="init" if="{ isNaN(parent.value) }">待機中
div.body <mk-ellipsis></mk-ellipsis>
p.init(if={ isNaN(parent.value) }) </p>
| 待機中 <p class="percentage" if="{ !isNaN(parent.value) }">{ Math.floor((parent.value / parent.max) * 100) }</p>
mk-ellipsis <progress if="{ !isNaN(parent.value) &amp;&amp; parent.value &lt; parent.max }" value="{ isNaN(parent.value) ? 0 : parent.value }" max="{ parent.max }"></progress>
p.percentage(if={ !isNaN(parent.value) }) { Math.floor((parent.value / parent.max) * 100) } <div class="progress waiting" if="{ parent.value &gt;= parent.max }"></div>
progress(if={ !isNaN(parent.value) && parent.value < parent.max }, value={ isNaN(parent.value) ? 0 : parent.value }, max={ parent.max }) </div></yield>
div.progress.waiting(if={ parent.value >= parent.max }) </mk-window>
</yield> <style type="stylus">
:scope
display block
style. > mk-window
display block [data-yield='content']
> mk-window > .body
[data-yield='content'] padding 18px 24px 24px 24px
> .body > .init
padding 18px 24px 24px 24px display block
margin 0
text-align center
color rgba(#000, 0.7)
> .init > .percentage
display block display block
margin 0 margin 0 0 4px 0
text-align center text-align center
color rgba(#000, 0.7) line-height 16px
color rgba($theme-color, 0.7)
> .percentage &:after
display block content '%'
margin 0 0 4px 0
text-align center
line-height 16px
color rgba($theme-color, 0.7)
&:after > progress
content '%' > .progress
display block
margin 0
width 100%
height 10px
background transparent
border none
border-radius 4px
overflow hidden
> progress &::-webkit-progress-value
> .progress background $theme-color
display block
margin 0
width 100%
height 10px
background transparent
border none
border-radius 4px
overflow hidden
&::-webkit-progress-value &::-webkit-progress-bar
background $theme-color background rgba($theme-color, 0.1)
&::-webkit-progress-bar > .progress
background rgba($theme-color, 0.1) background linear-gradient(
45deg,
lighten($theme-color, 30%) 25%,
$theme-color 25%,
$theme-color 50%,
lighten($theme-color, 30%) 50%,
lighten($theme-color, 30%) 75%,
$theme-color 75%,
$theme-color
)
background-size 32px 32px
animation progress-dialog-tag-progress-waiting 1.5s linear infinite
> .progress @keyframes progress-dialog-tag-progress-waiting
background linear-gradient( from {background-position: 0 0;}
45deg, to {background-position: -64px 32px;}
lighten($theme-color, 30%) 25%,
$theme-color 25%,
$theme-color 50%,
lighten($theme-color, 30%) 50%,
lighten($theme-color, 30%) 75%,
$theme-color 75%,
$theme-color
)
background-size 32px 32px
animation progress-dialog-tag-progress-waiting 1.5s linear infinite
@keyframes progress-dialog-tag-progress-waiting </style>
from {background-position: 0 0;} <script>
to {background-position: -64px 32px;} @title = @opts.title
@value = parse-int @opts.value, 10
@max = parse-int @opts.max, 10
script. @on \mount ~>
@title = @opts.title @refs.window.on \closed ~>
@value = parse-int @opts.value, 10 @unmount!
@max = parse-int @opts.max, 10
@on \mount ~> @update-progress = (value, max) ~>
@refs.window.on \closed ~> @value = parse-int value, 10
@unmount! @max = parse-int max, 10
@update!
@update-progress = (value, max) ~> @close = ~>
@value = parse-int value, 10 @refs.window.close!
@max = parse-int max, 10 </script>
@update! </mk-progress-dialog>
@close = ~>
@refs.window.close!

View file

@ -1,38 +1,36 @@
mk-repost-form-window <mk-repost-form-window>
mk-window@window(is-modal={ true }, colored={ true }) <mk-window ref="window" is-modal="{ true }" colored="{ true }"><yield to="header"><i class="fa fa-retweet"></i>この投稿をRepostしますか</yield>
<yield to="header"> <yield to="content">
i.fa.fa-retweet <mk-repost-form ref="form" post="{ parent.opts.post }"></mk-repost-form></yield>
| この投稿をRepostしますか </mk-window>
</yield> <style type="stylus">
<yield to="content"> :scope
mk-repost-form@form(post={ parent.opts.post }) > mk-window
</yield> [data-yield='header']
> i
margin-right 4px
style. </style>
> mk-window <script>
[data-yield='header'] @on-document-keydown = (e) ~>
> i tag = e.target.tag-name.to-lower-case!
margin-right 4px if tag != \input and tag != \textarea
if e.which == 27 # Esc
@refs.window.close!
script. @on \mount ~>
@refs.window.refs.form.on \cancel ~>
@on-document-keydown = (e) ~>
tag = e.target.tag-name.to-lower-case!
if tag != \input and tag != \textarea
if e.which == 27 # Esc
@refs.window.close! @refs.window.close!
@on \mount ~> @refs.window.refs.form.on \posted ~>
@refs.window.refs.form.on \cancel ~> @refs.window.close!
@refs.window.close!
@refs.window.refs.form.on \posted ~> document.add-event-listener \keydown @on-document-keydown
@refs.window.close!
document.add-event-listener \keydown @on-document-keydown @refs.window.on \closed ~>
@unmount!
@refs.window.on \closed ~> @on \unmount ~>
@unmount! document.remove-event-listener \keydown @on-document-keydown
</script>
@on \unmount ~> </mk-repost-form-window>
document.remove-event-listener \keydown @on-document-keydown

View file

@ -1,140 +1,144 @@
mk-repost-form <mk-repost-form>
mk-post-preview(post={ opts.post }) <mk-post-preview post="{ opts.post }"></mk-post-preview>
div.form(if={ quote }) <div class="form" if="{ quote }">
textarea@text(disabled={ wait }, placeholder='この投稿を引用...') <textarea ref="text" disabled="{ wait }" placeholder="この投稿を引用..."></textarea>
footer </div>
a.quote(if={ !quote }, onclick={ onquote }) 引用する... <footer><a class="quote" if="{ !quote }" onclick="{ onquote }">引用する...</a>
button.cancel(onclick={ cancel }) キャンセル <button class="cancel" onclick="{ cancel }">キャンセル</button>
button.ok(onclick={ ok }) Repost <button class="ok" onclick="{ ok }">Repost</button>
</footer>
<style type="stylus">
:scope
style. > mk-post-preview
margin 16px 22px
> mk-post-preview > .form
margin 16px 22px [ref='text']
display block
padding 12px
margin 0
width 100%
max-width 100%
min-width 100%
min-height calc(1em + 12px + 12px)
font-size 1em
color #333
background #fff
outline none
border solid 1px rgba($theme-color, 0.1)
border-radius 4px
transition border-color .3s ease
> .form &:hover
[ref='text'] border-color rgba($theme-color, 0.2)
display block transition border-color .1s ease
padding 12px
margin 0
width 100%
max-width 100%
min-width 100%
min-height calc(1em + 12px + 12px)
font-size 1em
color #333
background #fff
outline none
border solid 1px rgba($theme-color, 0.1)
border-radius 4px
transition border-color .3s ease
&:hover &:focus
border-color rgba($theme-color, 0.2) color $theme-color
transition border-color .1s ease border-color rgba($theme-color, 0.5)
transition border-color 0s ease
&:focus &:disabled
color $theme-color opacity 0.5
border-color rgba($theme-color, 0.5)
transition border-color 0s ease
&:disabled &::-webkit-input-placeholder
opacity 0.5 color rgba($theme-color, 0.3)
&::-webkit-input-placeholder > div
color rgba($theme-color, 0.3) padding 16px
> div > footer
padding 16px height 72px
background lighten($theme-color, 95%)
> footer > .quote
height 72px
background lighten($theme-color, 95%)
> .quote
position absolute
bottom 16px
left 28px
line-height 40px
button
display block
position absolute
bottom 16px
cursor pointer
padding 0
margin 0
width 120px
height 40px
font-size 1em
outline none
border-radius 4px
&:focus
&:after
content ""
pointer-events none
position absolute position absolute
top -5px bottom 16px
right -5px left 28px
bottom -5px line-height 40px
left -5px
border 2px solid rgba($theme-color, 0.3)
border-radius 8px
> .cancel button
right 148px display block
color #888 position absolute
background linear-gradient(to bottom, #ffffff 0%, #f5f5f5 100%) bottom 16px
border solid 1px #e2e2e2 cursor pointer
padding 0
margin 0
width 120px
height 40px
font-size 1em
outline none
border-radius 4px
&:hover &:focus
background linear-gradient(to bottom, #f9f9f9 0%, #ececec 100%) &:after
border-color #dcdcdc content ""
pointer-events none
position absolute
top -5px
right -5px
bottom -5px
left -5px
border 2px solid rgba($theme-color, 0.3)
border-radius 8px
&:active > .cancel
background #ececec right 148px
border-color #dcdcdc color #888
background linear-gradient(to bottom, #ffffff 0%, #f5f5f5 100%)
border solid 1px #e2e2e2
> .ok &:hover
right 16px background linear-gradient(to bottom, #f9f9f9 0%, #ececec 100%)
font-weight bold border-color #dcdcdc
color $theme-color-foreground
background linear-gradient(to bottom, lighten($theme-color, 25%) 0%, lighten($theme-color, 10%) 100%)
border solid 1px lighten($theme-color, 15%)
&:hover &:active
background linear-gradient(to bottom, lighten($theme-color, 8%) 0%, darken($theme-color, 8%) 100%) background #ececec
border-color $theme-color border-color #dcdcdc
&:active > .ok
background $theme-color right 16px
border-color $theme-color font-weight bold
color $theme-color-foreground
background linear-gradient(to bottom, lighten($theme-color, 25%) 0%, lighten($theme-color, 10%) 100%)
border solid 1px lighten($theme-color, 15%)
script. &:hover
@mixin \api background linear-gradient(to bottom, lighten($theme-color, 8%) 0%, darken($theme-color, 8%) 100%)
@mixin \notify border-color $theme-color
@wait = false &:active
@quote = false background $theme-color
border-color $theme-color
@cancel = ~> </style>
@trigger \cancel <script>
@mixin \api
@mixin \notify
@ok = ~> @wait = false
@wait = true @quote = false
@api \posts/create do
repost_id: @opts.post.id
text: if @quote then @refs.text.value else undefined
.then (data) ~>
@trigger \posted
@notify 'Repostしました'
.catch (err) ~>
console.error err
@notify 'Repostできませんでした'
.then ~>
@wait = false
@update!
@onquote = ~> @cancel = ~>
@quote = true @trigger \cancel
@ok = ~>
@wait = true
@api \posts/create do
repost_id: @opts.post.id
text: if @quote then @refs.text.value else undefined
.then (data) ~>
@trigger \posted
@notify 'Repostしました'
.catch (err) ~>
console.error err
@notify 'Repostできませんでした'
.then ~>
@wait = false
@update!
@onquote = ~>
@quote = true
</script>
</mk-repost-form>

View file

@ -1,88 +1,86 @@
mk-search-posts <mk-search-posts>
div.loading(if={ is-loading }) <div class="loading" if="{ isLoading }">
mk-ellipsis-icon <mk-ellipsis-icon></mk-ellipsis-icon>
p.empty(if={ is-empty }) </div>
i.fa.fa-search <p class="empty" if="{ isEmpty }"><i class="fa fa-search"></i>「{ query }」に関する投稿は見つかりませんでした。</p>
| 「{ query }」に関する投稿は見つかりませんでした。 <mk-timeline ref="timeline"><yield to="footer"><i class="fa fa-moon-o" if="{ !parent.moreLoading }"></i><i class="fa fa-spinner fa-pulse fa-fw" if="{ parent.moreLoading }"></i></yield></mk-timeline>
mk-timeline@timeline <style type="stylus">
<yield to="footer"> :scope
i.fa.fa-moon-o(if={ !parent.more-loading })
i.fa.fa-spinner.fa-pulse.fa-fw(if={ parent.more-loading })
</yield>
style.
display block
background #fff
> .loading
padding 64px 0
> .empty
display block
margin 0 auto
padding 32px
max-width 400px
text-align center
color #999
> i
display block display block
margin-bottom 16px background #fff
font-size 3em
color #ccc
script. > .loading
@mixin \api padding 64px 0
@mixin \get-post-summary
@query = @opts.query > .empty
@is-loading = true display block
@is-empty = false margin 0 auto
@more-loading = false padding 32px
@page = 0 max-width 400px
text-align center
color #999
@on \mount ~> > i
document.add-event-listener \keydown @on-document-keydown display block
window.add-event-listener \scroll @on-scroll margin-bottom 16px
font-size 3em
color #ccc
@api \posts/search do </style>
query: @query <script>
.then (posts) ~> @mixin \api
@is-loading = false @mixin \get-post-summary
@is-empty = posts.length == 0
@query = @opts.query
@is-loading = true
@is-empty = false
@more-loading = false
@page = 0
@on \mount ~>
document.add-event-listener \keydown @on-document-keydown
window.add-event-listener \scroll @on-scroll
@api \posts/search do
query: @query
.then (posts) ~>
@is-loading = false
@is-empty = posts.length == 0
@update!
@refs.timeline.set-posts posts
@trigger \loaded
.catch (err) ~>
console.error err
@on \unmount ~>
document.remove-event-listener \keydown @on-document-keydown
window.remove-event-listener \scroll @on-scroll
@on-document-keydown = (e) ~>
tag = e.target.tag-name.to-lower-case!
if tag != \input and tag != \textarea
if e.which == 84 # t
@refs.timeline.focus!
@more = ~>
if @more-loading or @is-loading or @timeline.posts.length == 0
return
@more-loading = true
@update! @update!
@refs.timeline.set-posts posts @api \posts/search do
@trigger \loaded query: @query
.catch (err) ~> page: @page + 1
console.error err .then (posts) ~>
@more-loading = false
@page++
@update!
@refs.timeline.prepend-posts posts
.catch (err) ~>
console.error err
@on \unmount ~> @on-scroll = ~>
document.remove-event-listener \keydown @on-document-keydown current = window.scroll-y + window.inner-height
window.remove-event-listener \scroll @on-scroll if current > document.body.offset-height - 16 # 遊び
@more!
@on-document-keydown = (e) ~> </script>
tag = e.target.tag-name.to-lower-case! </mk-search-posts>
if tag != \input and tag != \textarea
if e.which == 84 # t
@refs.timeline.focus!
@more = ~>
if @more-loading or @is-loading or @timeline.posts.length == 0
return
@more-loading = true
@update!
@api \posts/search do
query: @query
page: @page + 1
.then (posts) ~>
@more-loading = false
@page++
@update!
@refs.timeline.prepend-posts posts
.catch (err) ~>
console.error err
@on-scroll = ~>
current = window.scroll-y + window.inner-height
if current > document.body.offset-height - 16 # 遊び
@more!

View file

@ -1,28 +1,32 @@
mk-search <mk-search>
header <header>
h1 { query } <h1>{ query }</h1>
mk-search-posts@posts(query={ query }) </header>
<mk-search-posts ref="posts" query="{ query }"></mk-search-posts>
<style type="stylus">
:scope
display block
padding-bottom 32px
style. > header
display block width 100%
padding-bottom 32px max-width 600px
margin 0 auto
color #555
> header > mk-search-posts
width 100% max-width 600px
max-width 600px margin 0 auto
margin 0 auto border solid 1px rgba(0, 0, 0, 0.075)
color #555 border-radius 6px
overflow hidden
> mk-search-posts </style>
max-width 600px <script>
margin 0 auto @query = @opts.query
border solid 1px rgba(0, 0, 0, 0.075)
border-radius 6px
overflow hidden
script. @on \mount ~>
@query = @opts.query @refs.posts.on \loaded ~>
@trigger \loaded
@on \mount ~> </script>
@refs.posts.on \loaded ~> </mk-search>
@trigger \loaded

View file

@ -1,160 +1,161 @@
mk-select-file-from-drive-window <mk-select-file-from-drive-window>
mk-window@window(is-modal={ true }, width={ '800px' }, height={ '500px' }) <mk-window ref="window" is-modal="{ true }" width="{ '800px' }" height="{ '500px' }"><yield to="header">
<yield to="header"> <mk-raw content="{ parent.title }"></mk-raw><span class="count" if="{ parent.multiple &amp;&amp; parent.file.length &gt; 0 }">({ parent.file.length }ファイル選択中)</span></yield>
mk-raw(content={ parent.title }) <yield to="content">
span.count(if={ parent.multiple && parent.file.length > 0 }) ({ parent.file.length }ファイル選択中) <mk-drive-browser ref="browser" multiple="{ parent.multiple }"></mk-drive-browser>
</yield> <div>
<yield to="content"> <button class="upload" title="PCからドライブにファイルをアップロード" onclick="{ parent.upload }"><i class="fa fa-upload"></i></button>
mk-drive-browser@browser(multiple={ parent.multiple }) <button class="cancel" onclick="{ parent.close }">キャンセル</button>
div <button class="ok" disabled="{ parent.multiple &amp;&amp; parent.file.length == 0 }" onclick="{ parent.ok }">決定</button>
button.upload(title='PCからドライブにファイルをアップロード', onclick={ parent.upload }): i.fa.fa-upload </div></yield>
button.cancel(onclick={ parent.close }) キャンセル </mk-window>
button.ok(disabled={ parent.multiple && parent.file.length == 0 }, onclick={ parent.ok }) 決定 <style type="stylus">
</yield> :scope
> mk-window
[data-yield='header']
> mk-raw
> i
margin-right 4px
style. .count
> mk-window margin-left 8px
[data-yield='header']
> mk-raw
> i
margin-right 4px
.count
margin-left 8px
opacity 0.7
[data-yield='content']
> mk-drive-browser
height calc(100% - 72px)
> div
height 72px
background lighten($theme-color, 95%)
.upload
display inline-block
position absolute
top 8px
left 16px
cursor pointer
padding 0
margin 8px 4px 0 0
width 40px
height 40px
font-size 1em
color rgba($theme-color, 0.5)
background transparent
outline none
border solid 1px transparent
border-radius 4px
&:hover
background transparent
border-color rgba($theme-color, 0.3)
&:active
color rgba($theme-color, 0.6)
background transparent
border-color rgba($theme-color, 0.5)
box-shadow 0 2px 4px rgba(darken($theme-color, 50%), 0.15) inset
&:focus
&:after
content ""
pointer-events none
position absolute
top -5px
right -5px
bottom -5px
left -5px
border 2px solid rgba($theme-color, 0.3)
border-radius 8px
.ok
.cancel
display block
position absolute
bottom 16px
cursor pointer
padding 0
margin 0
width 120px
height 40px
font-size 1em
outline none
border-radius 4px
&:focus
&:after
content ""
pointer-events none
position absolute
top -5px
right -5px
bottom -5px
left -5px
border 2px solid rgba($theme-color, 0.3)
border-radius 8px
&:disabled
opacity 0.7 opacity 0.7
cursor default
.ok [data-yield='content']
right 16px > mk-drive-browser
color $theme-color-foreground height calc(100% - 72px)
background linear-gradient(to bottom, lighten($theme-color, 25%) 0%, lighten($theme-color, 10%) 100%)
border solid 1px lighten($theme-color, 15%)
&:not(:disabled) > div
font-weight bold height 72px
background lighten($theme-color, 95%)
&:hover:not(:disabled) .upload
background linear-gradient(to bottom, lighten($theme-color, 8%) 0%, darken($theme-color, 8%) 100%) display inline-block
border-color $theme-color position absolute
top 8px
left 16px
cursor pointer
padding 0
margin 8px 4px 0 0
width 40px
height 40px
font-size 1em
color rgba($theme-color, 0.5)
background transparent
outline none
border solid 1px transparent
border-radius 4px
&:active:not(:disabled) &:hover
background $theme-color background transparent
border-color $theme-color border-color rgba($theme-color, 0.3)
.cancel &:active
right 148px color rgba($theme-color, 0.6)
color #888 background transparent
background linear-gradient(to bottom, #ffffff 0%, #f5f5f5 100%) border-color rgba($theme-color, 0.5)
border solid 1px #e2e2e2 box-shadow 0 2px 4px rgba(darken($theme-color, 50%), 0.15) inset
&:hover &:focus
background linear-gradient(to bottom, #f9f9f9 0%, #ececec 100%) &:after
border-color #dcdcdc content ""
pointer-events none
position absolute
top -5px
right -5px
bottom -5px
left -5px
border 2px solid rgba($theme-color, 0.3)
border-radius 8px
&:active .ok
background #ececec .cancel
border-color #dcdcdc display block
position absolute
bottom 16px
cursor pointer
padding 0
margin 0
width 120px
height 40px
font-size 1em
outline none
border-radius 4px
script. &:focus
@file = [] &:after
content ""
pointer-events none
position absolute
top -5px
right -5px
bottom -5px
left -5px
border 2px solid rgba($theme-color, 0.3)
border-radius 8px
@multiple = if @opts.multiple? then @opts.multiple else false &:disabled
@title = @opts.title || '<i class="fa fa-file-o"></i>ファイルを選択' opacity 0.7
cursor default
@on \mount ~> .ok
@refs.window.refs.browser.on \selected (file) ~> right 16px
@file = file color $theme-color-foreground
@ok! background linear-gradient(to bottom, lighten($theme-color, 25%) 0%, lighten($theme-color, 10%) 100%)
border solid 1px lighten($theme-color, 15%)
@refs.window.refs.browser.on \change-selection (files) ~> &:not(:disabled)
@file = files font-weight bold
@update!
@refs.window.on \closed ~> &:hover:not(:disabled)
@unmount! background linear-gradient(to bottom, lighten($theme-color, 8%) 0%, darken($theme-color, 8%) 100%)
border-color $theme-color
@close = ~> &:active:not(:disabled)
@refs.window.close! background $theme-color
border-color $theme-color
@upload = ~> .cancel
@refs.window.refs.browser.select-local-file! right 148px
color #888
background linear-gradient(to bottom, #ffffff 0%, #f5f5f5 100%)
border solid 1px #e2e2e2
@ok = ~> &:hover
@trigger \selected @file background linear-gradient(to bottom, #f9f9f9 0%, #ececec 100%)
@refs.window.close! border-color #dcdcdc
&:active
background #ececec
border-color #dcdcdc
</style>
<script>
@file = []
@multiple = if @opts.multiple? then @opts.multiple else false
@title = @opts.title || '<i class="fa fa-file-o"></i>ファイルを選択'
@on \mount ~>
@refs.window.refs.browser.on \selected (file) ~>
@file = file
@ok!
@refs.window.refs.browser.on \change-selection (files) ~>
@file = files
@update!
@refs.window.on \closed ~>
@unmount!
@close = ~>
@refs.window.close!
@upload = ~>
@refs.window.refs.browser.select-local-file!
@ok = ~>
@trigger \selected @file
@refs.window.close!
</script>
</mk-select-file-from-drive-window>

View file

@ -1,44 +1,46 @@
mk-set-avatar-suggestion(onclick={ set }) <mk-set-avatar-suggestion onclick="{ set }">
p <p><b>アバターを設定</b>してみませんか?
b アバターを設定 <button onclick="{ close }"><i class="fa fa-times"></i></button>
| してみませんか? </p>
button(onclick={ close }): i.fa.fa-times <style type="stylus">
:scope
style. display block
display block cursor pointer
cursor pointer
color #fff
background #a8cad0
&:hover
background #70abb5
> p
display block
margin 0 auto
padding 8px
max-width 1024px
> a
font-weight bold
color #fff color #fff
background #a8cad0
> button &:hover
position absolute background #70abb5
top 0
right 0
padding 8px
color #fff
script. > p
@mixin \i display block
@mixin \update-avatar margin 0 auto
padding 8px
max-width 1024px
@set = ~> > a
@update-avatar @I, (i) ~> font-weight bold
@update-i i color #fff
@close = (e) ~> > button
e.prevent-default! position absolute
e.stop-propagation! top 0
@unmount! right 0
padding 8px
color #fff
</style>
<script>
@mixin \i
@mixin \update-avatar
@set = ~>
@update-avatar @I, (i) ~>
@update-i i
@close = (e) ~>
e.prevent-default!
e.stop-propagation!
@unmount!
</script>
</mk-set-avatar-suggestion>

View file

@ -1,44 +1,46 @@
mk-set-banner-suggestion(onclick={ set }) <mk-set-banner-suggestion onclick="{ set }">
p <p><b>バナーを設定</b>してみませんか?
b バナーを設定 <button onclick="{ close }"><i class="fa fa-times"></i></button>
| してみませんか? </p>
button(onclick={ close }): i.fa.fa-times <style type="stylus">
:scope
style. display block
display block cursor pointer
cursor pointer
color #fff
background #a8cad0
&:hover
background #70abb5
> p
display block
margin 0 auto
padding 8px
max-width 1024px
> a
font-weight bold
color #fff color #fff
background #a8cad0
> button &:hover
position absolute background #70abb5
top 0
right 0
padding 8px
color #fff
script. > p
@mixin \i display block
@mixin \update-banner margin 0 auto
padding 8px
max-width 1024px
@set = ~> > a
@update-banner @I, (i) ~> font-weight bold
@update-i i color #fff
@close = (e) ~> > button
e.prevent-default! position absolute
e.stop-propagation! top 0
@unmount! right 0
padding 8px
color #fff
</style>
<script>
@mixin \i
@mixin \update-banner
@set = ~>
@update-banner @I, (i) ~>
@update-i i
@close = (e) ~>
e.prevent-default!
e.stop-propagation!
@unmount!
</script>
</mk-set-banner-suggestion>

View file

@ -1,26 +1,25 @@
mk-settings-window <mk-settings-window>
mk-window@window(is-modal={ true }, width={ '700px' }, height={ '550px' }) <mk-window ref="window" is-modal="{ true }" width="{ '700px' }" height="{ '550px' }"><yield to="header"><i class="fa fa-cog"></i>設定</yield>
<yield to="header"> <yield to="content">
i.fa.fa-cog <mk-settings></mk-settings></yield>
| 設定 </mk-window>
</yield> <style type="stylus">
<yield to="content"> :scope
mk-settings > mk-window
</yield> [data-yield='header']
> i
margin-right 4px
style. [data-yield='content']
> mk-window overflow auto
[data-yield='header']
> i
margin-right 4px
[data-yield='content'] </style>
overflow auto <script>
@on \mount ~>
@refs.window.on \closed ~>
@unmount!
script. @close = ~>
@on \mount ~> @refs.window.close!
@refs.window.on \closed ~> </script>
@unmount! </mk-settings-window>
@close = ~>
@refs.window.close!

View file

@ -1,271 +1,266 @@
mk-settings <mk-settings>
div.nav <div class="nav">
p(class={ active: page == 'account' }, onmousedown={ set-page.bind(null, 'account') }) <p class="{ active: page == 'account' }" onmousedown="{ setPage.bind(null, 'account') }"><i class="fa fa-fw fa-user"></i>アカウント</p>
i.fa.fa-fw.fa-user <p class="{ active: page == 'web' }" onmousedown="{ setPage.bind(null, 'web') }"><i class="fa fa-fw fa-desktop"></i>Web</p>
| アカウント <p class="{ active: page == 'notification' }" onmousedown="{ setPage.bind(null, 'notification') }"><i class="fa fa-fw fa-bell-o"></i>通知</p>
p(class={ active: page == 'web' }, onmousedown={ set-page.bind(null, 'web') }) <p class="{ active: page == 'drive' }" onmousedown="{ setPage.bind(null, 'drive') }"><i class="fa fa-fw fa-cloud"></i>ドライブ</p>
i.fa.fa-fw.fa-desktop <p class="{ active: page == 'apps' }" onmousedown="{ setPage.bind(null, 'apps') }"><i class="fa fa-fw fa-puzzle-piece"></i>アプリ</p>
| Web <p class="{ active: page == 'signin' }" onmousedown="{ setPage.bind(null, 'signin') }"><i class="fa fa-fw fa-sign-in"></i>ログイン履歴</p>
p(class={ active: page == 'notification' }, onmousedown={ set-page.bind(null, 'notification') }) <p class="{ active: page == 'password' }" onmousedown="{ setPage.bind(null, 'password') }"><i class="fa fa-fw fa-unlock-alt"></i>パスワード</p>
i.fa.fa-fw.fa-bell-o <p class="{ active: page == 'api' }" onmousedown="{ setPage.bind(null, 'api') }"><i class="fa fa-fw fa-key"></i>API</p>
| 通知 </div>
p(class={ active: page == 'drive' }, onmousedown={ set-page.bind(null, 'drive') }) <div class="pages">
i.fa.fa-fw.fa-cloud <section class="account" show="{ page == 'account' }">
| ドライブ <h1>アカウント</h1>
p(class={ active: page == 'apps' }, onmousedown={ set-page.bind(null, 'apps') }) <label class="avatar">
i.fa.fa-fw.fa-puzzle-piece <p>アバター</p><img class="avatar" src="{ I.avatar_url + '?thumbnail&amp;size=64' }" alt="avatar"/>
| アプリ <button class="style-normal" onclick="{ avatar }">画像を選択</button>
p(class={ active: page == 'signin' }, onmousedown={ set-page.bind(null, 'signin') }) </label>
i.fa.fa-fw.fa-sign-in <label>
| ログイン履歴 <p>名前</p>
p(class={ active: page == 'password' }, onmousedown={ set-page.bind(null, 'password') }) <input ref="accountName" type="text" value="{ I.name }"/>
i.fa.fa-fw.fa-unlock-alt </label>
| パスワード <label>
p(class={ active: page == 'api' }, onmousedown={ set-page.bind(null, 'api') }) <p>場所</p>
i.fa.fa-fw.fa-key <input ref="accountLocation" type="text" value="{ I.location }"/>
| API </label>
<label>
div.pages <p>自己紹介</p>
section.account(show={ page == 'account' }) <textarea ref="accountBio">{ I.bio }</textarea>
h1 アカウント </label>
label.avatar <label>
p アバター <p>誕生日</p>
img.avatar(src={ I.avatar_url + '?thumbnail&size=64' }, alt='avatar') <input ref="accountBirthday" type="date" value="{ I.birthday }"/>
button.style-normal(onclick={ avatar }) 画像を選択 </label>
label <button class="style-primary" onclick="{ updateAccount }">保存</button>
p 名前 </section>
input@account-name(type='text', value={ I.name }) <section class="web" show="{ page == 'web' }">
label <h1>デザイン</h1>
p 場所 <label>
input@account-location(type='text', value={ I.location }) <p>壁紙</p>
label <button class="style-normal" onclick="{ wallpaper }">画像を選択</button>
p 自己紹介 </label>
textarea@account-bio { I.bio } </section>
label <section class="web" show="{ page == 'web' }">
p 誕生日 <h1>その他</h1>
input@account-birthday(type='date', value={ I.birthday }) <label class="checkbox">
button.style-primary(onclick={ update-account }) 保存 <input type="checkbox" checked="{ I.data.cache }" onclick="{ updateCache }"/>
<p>読み込みを高速化する</p>
section.web(show={ page == 'web' }) <p>API通信時に新鮮なユーザー情報をキャッシュすることでフェッチのオーバーヘッドを無くします。(実験的)</p>
h1 デザイン </label>
label <label class="checkbox">
p 壁紙 <input type="checkbox" checked="{ I.data.debug }" onclick="{ updateDebug }"/>
button.style-normal(onclick={ wallpaper }) 画像を選択 <p>開発者モード</p>
section.web(show={ page == 'web' }) <p>デバッグ等の開発者モードを有効にします。</p>
h1 その他 </label>
label.checkbox <label class="checkbox">
input(type='checkbox', checked={ I.data.cache }, onclick={ update-cache }) <input type="checkbox" checked="{ I.data.nya }" onclick="{ updateNya }"/>
p 読み込みを高速化する <p><i>な</i>を<i>にゃ</i>に変換する</p>
p API通信時に新鮮なユーザー情報をキャッシュすることでフェッチのオーバーヘッドを無くします。(実験的) <p>攻撃的な投稿が多少和らぐ可能性があります。</p>
label.checkbox </label>
input(type='checkbox', checked={ I.data.debug }, onclick={ update-debug }) </section>
p 開発者モード <section class="signin" show="{ page == 'signin' }">
p デバッグ等の開発者モードを有効にします。 <h1>ログイン履歴</h1>
label.checkbox <mk-signin-history></mk-signin-history>
input(type='checkbox', checked={ I.data.nya }, onclick={ update-nya }) </section>
p <i>な</i>を<i>にゃ</i>に変換する <section class="api" show="{ page == 'api' }">
p 攻撃的な投稿が多少和らぐ可能性があります。 <h1>API</h1>
<p>Token:<code>{ I.token }</code></p>
section.signin(show={ page == 'signin' }) <p>APIを利用するには、上記のトークンを「i」というキーでパラメータに付加してリクエストします。</p>
h1 ログイン履歴 <p>アカウントを乗っ取られてしまう可能性があるため、このトークンは第三者に教えないでください(アプリなどにも入力しないでください)。</p>
mk-signin-history <p>万が一このトークンが漏れたりその可能性がある場合は
<button class="regenerate" onclick="{ regenerateToken }">トークンを再生成</button>できます。(副作用として、ログインしているすべてのデバイスでログアウトが発生します)
section.api(show={ page == 'api' }) </p>
h1 API </section>
p </div>
| Token: <style type="stylus">
code { I.token } :scope
p APIを利用するには、上記のトークンを「i」というキーでパラメータに付加してリクエストします。
p アカウントを乗っ取られてしまう可能性があるため、このトークンは第三者に教えないでください(アプリなどにも入力しないでください)。
p
| 万が一このトークンが漏れたりその可能性がある場合は
button.regenerate(onclick={ regenerate-token }) トークンを再生成
| できます。(副作用として、ログインしているすべてのデバイスでログアウトが発生します)
style.
display block
input:not([type])
input[type='text']
input[type='password']
input[type='email']
textarea
padding 8px
width 100%
font-size 16px
color #55595c
border solid 1px #dadada
border-radius 4px
&:hover
border-color #aeaeae
&:focus
border-color #aeaeae
> .nav
position absolute
top 0
left 0
width 200px
height 100%
padding 16px 0 0 0
border-right solid 1px #ddd
> p
display block display block
padding 10px 16px
margin 0
color #666
cursor pointer
-ms-user-select none input:not([type])
-moz-user-select none input[type='text']
-webkit-user-select none input[type='password']
user-select none input[type='email']
textarea
padding 8px
width 100%
font-size 16px
color #55595c
border solid 1px #dadada
border-radius 4px
transition margin-left 0.2s ease &:hover
border-color #aeaeae
> i &:focus
margin-right 4px border-color #aeaeae
&:hover > .nav
color #555 position absolute
top 0
&.active left 0
margin-left 8px width 200px
color $theme-color !important height 100%
padding 16px 0 0 0
> .pages border-right solid 1px #ddd
position absolute
top 0
left 200px
width calc(100% - 200px)
> section
padding 32px
// & + section
// margin-top 16px
h1
display block
margin 0
padding 0 0 8px 0
font-size 1em
color #555
border-bottom solid 1px #eee
label
display block
margin 16px 0
&:after
content ""
display block
clear both
> p > p
margin 0 0 8px 0 display block
font-weight bold padding 10px 16px
color #373a3c margin 0
color #666
cursor pointer
&.checkbox -ms-user-select none
> input -moz-user-select none
position absolute -webkit-user-select none
top 0 user-select none
left 0
&:checked + p transition margin-left 0.2s ease
color $theme-color
> p > i
width calc(100% - 32px) margin-right 4px
margin 0 0 0 32px
font-weight bold
&:last-child
font-weight normal
color #999
&.account
> .general
> .avatar
> img
display block
float left
width 64px
height 64px
border-radius 4px
> button
float left
margin-left 8px
&.api
code
padding 4px
background #eee
.regenerate
display inline
color $theme-color
&:hover &:hover
text-decoration underline color #555
script. &.active
@mixin \i margin-left 8px
@mixin \api color $theme-color !important
@mixin \dialog
@mixin \update-avatar
@mixin \update-wallpaper
@page = \account > .pages
position absolute
top 0
left 200px
width calc(100% - 200px)
@set-page = (page) ~> > section
@page = page padding 32px
@avatar = ~> // & + section
@update-avatar @I, (i) ~> // margin-top 16px
@update-i i
@wallpaper = ~> h1
@update-wallpaper @I, (i) ~> display block
@update-i i margin 0
padding 0 0 8px 0
font-size 1em
color #555
border-bottom solid 1px #eee
@update-account = ~> label
@api \i/update do display block
name: @refs.account-name.value margin 16px 0
location: @refs.account-location.value
bio: @refs.account-bio.value
birthday: @refs.account-birthday.value
.then (i) ~>
@update-i i
alert \ok
.catch (err) ~>
console.error err
@update-cache = ~> &:after
@I.data.cache = !@I.data.cache content ""
@api \i/appdata/set do display block
data: JSON.stringify do clear both
cache: @I.data.cache
.then ~>
@update-i!
@update-debug = ~> > p
@I.data.debug = !@I.data.debug margin 0 0 8px 0
@api \i/appdata/set do font-weight bold
data: JSON.stringify do color #373a3c
debug: @I.data.debug
.then ~>
@update-i!
@update-nya = ~> &.checkbox
@I.data.nya = !@I.data.nya > input
@api \i/appdata/set do position absolute
data: JSON.stringify do top 0
nya: @I.data.nya left 0
.then ~>
@update-i! &:checked + p
color $theme-color
> p
width calc(100% - 32px)
margin 0 0 0 32px
font-weight bold
&:last-child
font-weight normal
color #999
&.account
> .general
> .avatar
> img
display block
float left
width 64px
height 64px
border-radius 4px
> button
float left
margin-left 8px
&.api
code
padding 4px
background #eee
.regenerate
display inline
color $theme-color
&:hover
text-decoration underline
</style>
<script>
@mixin \i
@mixin \api
@mixin \dialog
@mixin \update-avatar
@mixin \update-wallpaper
@page = \account
@set-page = (page) ~>
@page = page
@avatar = ~>
@update-avatar @I, (i) ~>
@update-i i
@wallpaper = ~>
@update-wallpaper @I, (i) ~>
@update-i i
@update-account = ~>
@api \i/update do
name: @refs.account-name.value
location: @refs.account-location.value
bio: @refs.account-bio.value
birthday: @refs.account-birthday.value
.then (i) ~>
@update-i i
alert \ok
.catch (err) ~>
console.error err
@update-cache = ~>
@I.data.cache = !@I.data.cache
@api \i/appdata/set do
data: JSON.stringify do
cache: @I.data.cache
.then ~>
@update-i!
@update-debug = ~>
@I.data.debug = !@I.data.debug
@api \i/appdata/set do
data: JSON.stringify do
debug: @I.data.debug
.then ~>
@update-i!
@update-nya = ~>
@I.data.nya = !@I.data.nya
@api \i/appdata/set do
data: JSON.stringify do
nya: @I.data.nya
.then ~>
@update-i!
</script>
</mk-settings>

View file

@ -1,73 +1,75 @@
mk-signin-history <mk-signin-history>
div.records(if={ history.length != 0 }) <div class="records" if="{ history.length != 0 }">
div(each={ history }) <div each="{ history }">
mk-time(time={ created_at }) <mk-time time="{ created_at }"></mk-time>
header <header><i class="fa fa-check" if="{ success }"></i><i class="fa fa-times" if="{ !success }"></i><span class="ip">{ ip }</span></header>
i.fa.fa-check(if={ success }) <pre><code>{ JSON.stringify(headers, null, ' ') }</code></pre>
i.fa.fa-times(if={ !success }) </div>
span.ip { ip } </div>
pre: code { JSON.stringify(headers, null, ' ') } <style type="stylus">
:scope
display block
style. > .records
display block > div
padding 16px 0 0 0
border-bottom solid 1px #eee
> .records > header
> div
padding 16px 0 0 0
border-bottom solid 1px #eee
> header > i
margin-right 8px
> i &.fa-check
margin-right 8px color #0fda82
&.fa-check &.fa-times
color #0fda82 color #ff3100
&.fa-times > .ip
color #ff3100 display inline-block
color #444
background #f8f8f8
> .ip > mk-time
display inline-block position absolute
color #444 top 16px
background #f8f8f8 right 0
color #777
> mk-time > pre
position absolute overflow auto
top 16px max-height 100px
right 0
color #777
> pre > code
overflow auto white-space pre-wrap
max-height 100px word-break break-all
color #4a535a
> code </style>
white-space pre-wrap <script>
word-break break-all @mixin \api
color #4a535a @mixin \stream
script. @history = []
@mixin \api @fetching = true
@mixin \stream
@history = [] @on \mount ~>
@fetching = true @api \i/signin_history
.then (history) ~>
@history = history
@fetching = false
@update!
.catch (err) ~>
console.error err
@on \mount ~> @stream.on \signin @on-signin
@api \i/signin_history
.then (history) ~> @on \unmount ~>
@history = history @stream.off \signin @on-signin
@fetching = false
@on-signin = (signin) ~>
@history.unshift signin
@update! @update!
.catch (err) ~> </script>
console.error err </mk-signin-history>
@stream.on \signin @on-signin
@on \unmount ~>
@stream.off \signin @on-signin
@on-signin = (signin) ~>
@history.unshift signin
@update!

View file

@ -1,59 +1,54 @@
mk-stream-indicator <mk-stream-indicator>
p(if={ state == 'initializing' }) <p if="{ state == 'initializing' }"><i class="fa fa-spinner fa-spin"></i><span>接続中
i.fa.fa-spinner.fa-spin <mk-ellipsis></mk-ellipsis></span></p>
span <p if="{ state == 'reconnecting' }"><i class="fa fa-spinner fa-spin"></i><span>切断されました 接続中
| 接続中 <mk-ellipsis></mk-ellipsis></span></p>
mk-ellipsis <p if="{ state == 'connected' }"><i class="fa fa-check"></i><span>接続完了</span></p>
p(if={ state == 'reconnecting' }) <style type="stylus">
i.fa.fa-spinner.fa-spin :scope
span display block
| 切断されました 接続中 pointer-events none
mk-ellipsis position fixed
p(if={ state == 'connected' }) z-index 16384
i.fa.fa-check bottom 8px
span 接続完了 right 8px
margin 0
padding 6px 12px
font-size 0.9em
color #fff
background rgba(0, 0, 0, 0.8)
style. > p
display block display block
pointer-events none margin 0
position fixed
z-index 16384
bottom 8px
right 8px
margin 0
padding 6px 12px
font-size 0.9em
color #fff
background rgba(0, 0, 0, 0.8)
> p > i
display block margin-right 0.25em
margin 0
> i </style>
margin-right 0.25em <script>
@mixin \stream
script. @on \before-mount ~>
@mixin \stream @state = @get-stream-state!
@on \before-mount ~> if @state == \connected
@state = @get-stream-state! @root.style.opacity = 0
if @state == \connected @stream-state-ev.on \connected ~>
@root.style.opacity = 0 @state = @get-stream-state!
@update!
set-timeout ~>
Velocity @root, {
opacity: 0
} 200ms \linear
, 1000ms
@stream-state-ev.on \connected ~> @stream-state-ev.on \closed ~>
@state = @get-stream-state! @state = @get-stream-state!
@update! @update!
set-timeout ~>
Velocity @root, { Velocity @root, {
opacity: 0 opacity: 1
} 200ms \linear } 0ms
, 1000ms </script>
</mk-stream-indicator>
@stream-state-ev.on \closed ~>
@state = @get-stream-state!
@update!
Velocity @root, {
opacity: 1
} 0ms

View file

@ -1,37 +1,38 @@
mk-sub-post-content <mk-sub-post-content>
div.body <div class="body"><a class="reply" if="{ post.reply_to_id }"><i class="fa fa-reply"></i></a><span ref="text"></span><a class="quote" if="{ post.repost_id }" href="{ '/post:' + post.repost_id }">RP: ...</a></div>
a.reply(if={ post.reply_to_id }): i.fa.fa-reply <details if="{ post.media }">
span@text <summary>({ post.media.length }枚の画像)</summary>
a.quote(if={ post.repost_id }, href={ '/post:' + post.repost_id }) RP: ... <mk-images-viewer images="{ post.media }"></mk-images-viewer>
details(if={ post.media }) </details>
summary ({ post.media.length }枚の画像) <style type="stylus">
mk-images-viewer(images={ post.media }) :scope
display block
word-wrap break-word
style. > .body
display block > .reply
word-wrap break-word margin-right 6px
color #717171
> .body > .quote
> .reply margin-left 4px
margin-right 6px font-style oblique
color #717171 color #a0bf46
> .quote </style>
margin-left 4px <script>
font-style oblique @mixin \text
color #a0bf46 @mixin \user-preview
script. @post = @opts.post
@mixin \text
@mixin \user-preview
@post = @opts.post @on \mount ~>
if @post.text?
tokens = @analyze @post.text
@refs.text.innerHTML = @compile tokens, false
@on \mount ~> @refs.text.children.for-each (e) ~>
if @post.text? if e.tag-name == \MK-URL
tokens = @analyze @post.text riot.mount e
@refs.text.innerHTML = @compile tokens, false </script>
</mk-sub-post-content>
@refs.text.children.for-each (e) ~>
if e.tag-name == \MK-URL
riot.mount e

View file

@ -1,95 +1,99 @@
mk-timeline-post-sub(title={ title }) <mk-timeline-post-sub title="{ title }">
article <article><a class="avatar-anchor" href="{ CONFIG.url + '/' + post.user.username }"><img class="avatar" src="{ post.user.avatar_url + '?thumbnail&amp;size=64' }" alt="avatar" data-user-preview="{ post.user_id }"/></a>
a.avatar-anchor(href={ CONFIG.url + '/' + post.user.username }) <div class="main">
img.avatar(src={ post.user.avatar_url + '?thumbnail&size=64' }, alt='avatar', data-user-preview={ post.user_id }) <header><a class="name" href="{ CONFIG.url + '/' + post.user.username }" data-user-preview="{ post.user_id }">{ post.user.name }</a><span class="username">@{ post.user.username }</span><a class="created-at" href="{ CONFIG.url + '/' + post.user.username + '/' + post.id }">
div.main <mk-time time="{ post.created_at }"></mk-time></a></header>
header <div class="body">
a.name(href={ CONFIG.url + '/' + post.user.username }, data-user-preview={ post.user_id }) <mk-sub-post-content class="text" post="{ post }"></mk-sub-post-content>
| { post.user.name } </div>
span.username </div>
| @{ post.user.username } </article>
a.created-at(href={ CONFIG.url + '/' + post.user.username + '/' + post.id }) <script>
mk-time(time={ post.created_at }) @mixin \date-stringify
div.body @mixin \user-preview
mk-sub-post-content.text(post={ post })
script. @post = @opts.post
@mixin \date-stringify
@mixin \user-preview
@post = @opts.post @title = @date-stringify @post.created_at
@title = @date-stringify @post.created_at </script>
<style type="stylus">
style. :scope
display block
margin 0
padding 0
font-size 0.9em
> article
padding 16px
&:after
content ""
display block display block
clear both margin 0
padding 0
font-size 0.9em
&:hover > article
> .main > footer > button padding 16px
color #888
> .avatar-anchor &:after
display block content ""
float left display block
margin 0 14px 0 0 clear both
> .avatar &:hover
display block > .main > footer > button
width 52px color #888
height 52px
margin 0
border-radius 8px
vertical-align bottom
> .main > .avatar-anchor
float left display block
width calc(100% - 66px) float left
margin 0 14px 0 0
> header > .avatar
margin-bottom 4px display block
white-space nowrap width 52px
line-height 21px height 52px
margin 0
border-radius 8px
vertical-align bottom
> .name > .main
display inline float left
margin 0 width calc(100% - 66px)
padding 0
color #607073
font-size 1em
font-weight 700
text-align left
text-decoration none
&:hover > header
text-decoration underline margin-bottom 4px
white-space nowrap
line-height 21px
> .username > .name
text-align left display inline
margin 0 0 0 8px margin 0
color #d1d8da padding 0
color #607073
font-size 1em
font-weight 700
text-align left
text-decoration none
> .created-at &:hover
position absolute text-decoration underline
top 0
right 0
color #b2b8bb
> .body > .username
text-align left
margin 0 0 0 8px
color #d1d8da
> .text > .created-at
cursor default position absolute
margin 0 top 0
padding 0 right 0
font-size 1.1em color #b2b8bb
color #717171
> .body
> .text
cursor default
margin 0
padding 0
font-size 1.1em
color #717171
</style>
</mk-timeline-post-sub>

View file

@ -1,376 +1,332 @@
mk-timeline-post(tabindex='-1', title={ title }, onkeydown={ on-key-down }) <mk-timeline-post tabindex="-1" title="{ title }" onkeydown="{ onKeyDown }">
<div class="reply-to" if="{ p.reply_to }">
div.reply-to(if={ p.reply_to }) <mk-timeline-post-sub post="{ p.reply_to }"></mk-timeline-post-sub>
mk-timeline-post-sub(post={ p.reply_to }) </div>
<div class="repost" if="{ isRepost }">
div.repost(if={ is-repost }) <p><a class="avatar-anchor" href="{ CONFIG.url + '/' + post.user.username }" data-user-preview="{ post.user_id }"><img class="avatar" src="{ post.user.avatar_url + '?thumbnail&amp;size=32' }" alt="avatar"/></a><i class="fa fa-retweet"></i><a class="name" href="{ CONFIG.url + '/' + post.user.username }" data-user-preview="{ post.user_id }">{ post.user.name }</a>がRepost</p>
p <mk-time time="{ post.created_at }"></mk-time>
a.avatar-anchor(href={ CONFIG.url + '/' + post.user.username }, data-user-preview={ post.user_id }): img.avatar(src={ post.user.avatar_url + '?thumbnail&size=32' }, alt='avatar') </div>
i.fa.fa-retweet <article><a class="avatar-anchor" href="{ CONFIG.url + '/' + p.user.username }"><img class="avatar" src="{ p.user.avatar_url + '?thumbnail&amp;size=64' }" alt="avatar" data-user-preview="{ p.user.id }"/></a>
a.name(href={ CONFIG.url + '/' + post.user.username }, data-user-preview={ post.user_id }) { post.user.name } <div class="main">
| がRepost <header><a class="name" href="{ CONFIG.url + '/' + p.user.username }" data-user-preview="{ p.user.id }">{ p.user.name }</a><span class="username">@{ p.user.username }</span><a class="created-at" href="{ url }">
mk-time(time={ post.created_at }) <mk-time time="{ p.created_at }"></mk-time></a></header>
<div class="body">
article <div class="text"><a class="reply" if="{ p.reply_to }"><i class="fa fa-reply"></i></a><span ref="text"></span><a class="quote" if="{ p.repost != null }">RP:</a></div>
a.avatar-anchor(href={ CONFIG.url + '/' + p.user.username }) <div class="media" if="{ p.media }">
img.avatar(src={ p.user.avatar_url + '?thumbnail&size=64' }, alt='avatar', data-user-preview={ p.user.id }) <mk-images-viewer images="{ p.media }"></mk-images-viewer>
div.main </div>
header <div class="repost" if="{ p.repost }"><i class="fa fa-quote-right fa-flip-horizontal"></i>
a.name(href={ CONFIG.url + '/' + p.user.username }, data-user-preview={ p.user.id }) <mk-post-preview class="repost" post="{ p.repost }"></mk-post-preview>
| { p.user.name } </div>
span.username </div>
| @{ p.user.username } <footer>
a.created-at(href={ url }) <button onclick="{ reply }" title="返信"><i class="fa fa-reply"></i>
mk-time(time={ p.created_at }) <p class="count" if="{ p.replies_count &gt; 0 }">{ p.replies_count }</p>
div.body </button>
div.text <button onclick="{ repost }" title="Repost"><i class="fa fa-retweet"></i>
a.reply(if={ p.reply_to }): i.fa.fa-reply <p class="count" if="{ p.repost_count &gt; 0 }">{ p.repost_count }</p>
span@text </button>
a.quote(if={ p.repost != null }) RP: <button class="{ liked: p.is_liked }" onclick="{ like }" title="善哉"><i class="fa fa-thumbs-o-up"></i>
div.media(if={ p.media }) <p class="count" if="{ p.likes_count &gt; 0 }">{ p.likes_count }</p>
mk-images-viewer(images={ p.media }) </button>
div.repost(if={ p.repost }) <button onclick="{ NotImplementedException }"><i class="fa fa-ellipsis-h"></i></button>
i.fa.fa-quote-right.fa-flip-horizontal <button onclick="{ toggleDetail }" title="詳細"><i class="fa fa-caret-down" if="{ !isDetailOpened }"></i><i class="fa fa-caret-up" if="{ isDetailOpened }"></i></button>
mk-post-preview.repost(post={ p.repost }) </footer>
footer </div>
button(onclick={ reply }, title='返信') </article>
i.fa.fa-reply <div class="detail" if="{ isDetailOpened }">
p.count(if={ p.replies_count > 0 }) { p.replies_count } <mk-post-status-graph width="462" height="130" post="{ p }"></mk-post-status-graph>
button(onclick={ repost }, title='Repost') </div>
i.fa.fa-retweet <style type="stylus">
p.count(if={ p.repost_count > 0 }) { p.repost_count } :scope
button(class={ liked: p.is_liked }, onclick={ like }, title='善哉') display block
i.fa.fa-thumbs-o-up
p.count(if={ p.likes_count > 0 }) { p.likes_count }
button(onclick={ NotImplementedException }): i.fa.fa-ellipsis-h
button(onclick={ toggle-detail }, title='詳細')
i.fa.fa-caret-down(if={ !is-detail-opened })
i.fa.fa-caret-up(if={ is-detail-opened })
div.detail(if={ is-detail-opened })
mk-post-status-graph(width='462', height='130', post={ p })
style.
display block
margin 0
padding 0
background #fff
&:focus
z-index 1
&:after
content ""
pointer-events none
position absolute
top 2px
right 2px
bottom 2px
left 2px
border 2px solid rgba($theme-color, 0.3)
border-radius 4px
> .repost
color #9dbb00
background linear-gradient(to bottom, #edfde2 0%, #fff 100%)
> p
margin 0 margin 0
padding 16px 32px padding 0
line-height 28px background #fff
.avatar-anchor &:focus
display inline-block z-index 1
.avatar &:after
vertical-align bottom content ""
width 28px pointer-events none
height 28px
margin 0 8px 0 0
border-radius 6px
i
margin-right 4px
.name
font-weight bold
> mk-time
position absolute
top 16px
right 32px
font-size 0.9em
line-height 28px
& + article
padding-top 8px
> .reply-to
padding 0 16px
background rgba(0, 0, 0, 0.0125)
> mk-post-preview
background transparent
> article
padding 28px 32px 18px 32px
&:after
content ""
display block
clear both
&:hover
> .main > footer > button
color #888
> .avatar-anchor
display block
float left
margin 0 16px 0 0
> .avatar
display block
width 58px
height 58px
margin 0
border-radius 8px
vertical-align bottom
> .main
float left
width calc(100% - 74px)
> header
margin-bottom 4px
white-space nowrap
line-height 24px
> .name
display inline
margin 0
padding 0
color #777
font-size 1em
font-weight 700
text-align left
text-decoration none
&:hover
text-decoration underline
> .username
text-align left
margin 0 0 0 8px
color #ccc
> .created-at
position absolute position absolute
top 0 top 2px
right 0 right 2px
font-size 0.9em bottom 2px
color #c0c0c0 left 2px
border 2px solid rgba($theme-color, 0.3)
border-radius 4px
> .body > .repost
color #9dbb00
background linear-gradient(to bottom, #edfde2 0%, #fff 100%)
> .text > p
cursor default
display block
margin 0 margin 0
padding 0 padding 16px 32px
word-wrap break-word line-height 28px
font-size 1.1em
color #717171
mk-url-preview .avatar-anchor
margin-top 8px display inline-block
> .reply .avatar
margin-right 8px vertical-align bottom
color #717171 width 28px
height 28px
margin 0 8px 0 0
border-radius 6px
> .quote i
margin-left 4px margin-right 4px
font-style oblique
color #a0bf46
> .media .name
> img font-weight bold
display block
max-width 100%
> .repost > mk-time
margin 8px 0 position absolute
top 16px
right 32px
font-size 0.9em
line-height 28px
> i:first-child & + article
position absolute padding-top 8px
top -8px
left -8px
z-index 1
color #c0dac6
font-size 28px
background #fff
> mk-post-preview > .reply-to
padding 16px padding 0 16px
border dashed 1px #c0dac6 background rgba(0, 0, 0, 0.0125)
border-radius 8px
> footer > mk-post-preview
> button
margin 0 28px 0 0
padding 0 8px
line-height 32px
font-size 1em
color #ddd
background transparent background transparent
border none
cursor pointer
&:hover > article
color #666 padding 28px 32px 18px 32px
> .count &:after
display inline content ""
margin 0 0 0 8px display block
color #999 clear both
&.liked &:hover
color $theme-color > .main > footer > button
color #888
&:last-child > .avatar-anchor
position absolute display block
right 0 float left
margin 0 16px 0 0
> .avatar
display block
width 58px
height 58px
margin 0 margin 0
border-radius 8px
vertical-align bottom
> .detail > .main
padding-top 4px float left
background rgba(0, 0, 0, 0.0125) width calc(100% - 74px)
style(theme='dark'). > header
background #0D0D0D margin-bottom 4px
white-space nowrap
line-height 24px
> article > .name
display inline
margin 0
padding 0
color #777
font-size 1em
font-weight 700
text-align left
text-decoration none
&:hover &:hover
> .main > footer > button text-decoration underline
color #eee
> .main > .username
> header text-align left
> .left margin 0 0 0 8px
> .name color #ccc
color #9e9c98
> .username > .created-at
color #41403f position absolute
top 0
right 0
font-size 0.9em
color #c0c0c0
> .right > .body
> .time
color #4e4d4b
> .body > .text
> .text cursor default
color #9e9c98 display block
margin 0
padding 0
word-wrap break-word
font-size 1.1em
color #717171
> footer mk-url-preview
> button margin-top 8px
color #9e9c98
&:hover > .reply
color #fff margin-right 8px
color #717171
> .count > .quote
color #eee margin-left 4px
font-style oblique
color #a0bf46
script. > .media
@mixin \api > img
@mixin \text display block
@mixin \date-stringify max-width 100%
@mixin \user-preview
@mixin \NotImplementedException
@post = @opts.post > .repost
@is-repost = @post.repost? and !@post.text? margin 8px 0
@p = if @is-repost then @post.repost else @post
@title = @date-stringify @p.created_at > i:first-child
position absolute
top -8px
left -8px
z-index 1
color #c0dac6
font-size 28px
background #fff
@url = CONFIG.url + '/' + @p.user.username + '/' + @p.id > mk-post-preview
@is-detail-opened = false padding 16px
border dashed 1px #c0dac6
border-radius 8px
@on \mount ~> > footer
if @p.text? > button
tokens = if @p._highlight? margin 0 28px 0 0
then @analyze @p._highlight padding 0 8px
else @analyze @p.text line-height 32px
font-size 1em
color #ddd
background transparent
border none
cursor pointer
@refs.text.innerHTML = if @p._highlight? &:hover
then @compile tokens, true, false color #666
else @compile tokens
@refs.text.children.for-each (e) ~> > .count
if e.tag-name == \MK-URL display inline
riot.mount e margin 0 0 0 8px
color #999
# URLをプレビュー &.liked
tokens color $theme-color
.filter (t) -> t.type == \link
.map (t) ~>
@preview = @refs.text.append-child document.create-element \mk-url-preview
riot.mount @preview, do
url: t.content
@reply = ~> &:last-child
form = document.body.append-child document.create-element \mk-post-form-window position absolute
riot.mount form, do right 0
reply: @p margin 0
@repost = ~> > .detail
form = document.body.append-child document.create-element \mk-repost-form-window padding-top 4px
riot.mount form, do background rgba(0, 0, 0, 0.0125)
post: @p
@like = ~> </style>
if @p.is_liked <script>
@api \posts/likes/delete do @mixin \api
post_id: @p.id @mixin \text
.then ~> @mixin \date-stringify
@p.is_liked = false @mixin \user-preview
@update! @mixin \NotImplementedException
else
@api \posts/likes/create do
post_id: @p.id
.then ~>
@p.is_liked = true
@update!
@toggle-detail = ~> @post = @opts.post
@is-detail-opened = !@is-detail-opened @is-repost = @post.repost? and !@post.text?
@update! @p = if @is-repost then @post.repost else @post
@on-key-down = (e) ~> @title = @date-stringify @p.created_at
should-be-cancel = true
switch
| e.which == 38 or e.which == 74 or (e.which == 9 and e.shift-key) => # ↑, j or Shift+Tab
focus @root, (e) -> e.previous-element-sibling
| e.which == 40 or e.which == 75 or e.which == 9 => # ↓, k or Tab
focus @root, (e) -> e.next-element-sibling
| e.which == 81 or e.which == 69 => # q or e
@repost!
| e.which == 70 or e.which == 76 => # f or l
@like!
| e.which == 82 => # r
@reply!
| _ =>
should-be-cancel = false
if should-be-cancel @url = CONFIG.url + '/' + @p.user.username + '/' + @p.id
e.prevent-default! @is-detail-opened = false
function focus(el, fn) @on \mount ~>
target = fn el if @p.text?
if target? tokens = if @p._highlight?
if target.has-attribute \tabindex then @analyze @p._highlight
target.focus! else @analyze @p.text
@refs.text.innerHTML = if @p._highlight?
then @compile tokens, true, false
else @compile tokens
@refs.text.children.for-each (e) ~>
if e.tag-name == \MK-URL
riot.mount e
# URLをプレビュー
tokens
.filter (t) -> t.type == \link
.map (t) ~>
@preview = @refs.text.append-child document.create-element \mk-url-preview
riot.mount @preview, do
url: t.content
@reply = ~>
form = document.body.append-child document.create-element \mk-post-form-window
riot.mount form, do
reply: @p
@repost = ~>
form = document.body.append-child document.create-element \mk-repost-form-window
riot.mount form, do
post: @p
@like = ~>
if @p.is_liked
@api \posts/likes/delete do
post_id: @p.id
.then ~>
@p.is_liked = false
@update!
else else
focus target, fn @api \posts/likes/create do
post_id: @p.id
.then ~>
@p.is_liked = true
@update!
@toggle-detail = ~>
@is-detail-opened = !@is-detail-opened
@update!
@on-key-down = (e) ~>
should-be-cancel = true
switch
| e.which == 38 or e.which == 74 or (e.which == 9 and e.shift-key) => # ↑, j or Shift+Tab
focus @root, (e) -> e.previous-element-sibling
| e.which == 40 or e.which == 75 or e.which == 9 => # ↓, k or Tab
focus @root, (e) -> e.next-element-sibling
| e.which == 81 or e.which == 69 => # q or e
@repost!
| e.which == 70 or e.which == 76 => # f or l
@like!
| e.which == 82 => # r
@reply!
| _ =>
should-be-cancel = false
if should-be-cancel
e.prevent-default!
function focus(el, fn)
target = fn el
if target?
if target.has-attribute \tabindex
target.focus!
else
focus target, fn
</script>
</mk-timeline-post>

View file

@ -1,86 +1,79 @@
mk-timeline <mk-timeline>
virtual(each={ post, i in posts }) <virtual each="{ post, i in posts }">
mk-timeline-post(post={ post }) <mk-timeline-post post="{ post }"></mk-timeline-post>
p.date(if={ i != posts.length - 1 && post._date != posts[i + 1]._date }) <p class="date" if="{ i != posts.length - 1 &amp;&amp; post._date != posts[i + 1]._date }"><span><i class="fa fa-angle-up"></i>{ post._datetext }</span><span><i class="fa fa-angle-down"></i>{ posts[i + 1]._datetext }</span></p>
span </virtual>
i.fa.fa-angle-up <footer data-yield="footer"><yield from="footer"/></footer>
| { post._datetext } <style type="stylus">
span :scope
i.fa.fa-angle-down display block
| { posts[i + 1]._datetext }
footer(data-yield='footer')
| <yield from="footer"/>
style. > mk-timeline-post
display block border-bottom solid 1px #eaeaea
> mk-timeline-post &:first-child
border-bottom solid 1px #eaeaea border-top-left-radius 4px
border-top-right-radius 4px
&:first-child &:last-of-type
border-top-left-radius 4px border-bottom none
border-top-right-radius 4px
&:last-of-type > .date
border-bottom none display block
margin 0
line-height 32px
font-size 14px
text-align center
color #aaa
background #fdfdfd
border-bottom solid 1px #eaeaea
> .date span
display block margin 0 16px
margin 0
line-height 32px
font-size 14px
text-align center
color #aaa
background #fdfdfd
border-bottom solid 1px #eaeaea
span i
margin 0 16px margin-right 8px
i > footer
margin-right 8px padding 16px
text-align center
color #ccc
border-top solid 1px #eaeaea
border-bottom-left-radius 4px
border-bottom-right-radius 4px
> footer </style>
padding 16px <script>
text-align center @posts = []
color #ccc
border-top solid 1px #eaeaea
border-bottom-left-radius 4px
border-bottom-right-radius 4px
style(theme='dark'). @set-posts = (posts) ~>
> mk-timeline-post @posts = posts
border-bottom-color #222221
script.
@posts = []
@set-posts = (posts) ~>
@posts = posts
@update!
@prepend-posts = (posts) ~>
posts.for-each (post) ~>
@posts.push post
@update! @update!
@add-post = (post) ~> @prepend-posts = (posts) ~>
@posts.unshift post posts.for-each (post) ~>
@update! @posts.push post
@update!
@clear = ~> @add-post = (post) ~>
@posts = [] @posts.unshift post
@update! @update!
@focus = ~> @clear = ~>
@root.children.0.focus! @posts = []
@update!
@on \update ~> @focus = ~>
@posts.for-each (post) ~> @root.children.0.focus!
date = (new Date post.created_at).get-date!
month = (new Date post.created_at).get-month! + 1
post._date = date
post._datetext = month + '月 ' + date + '日'
@tail = ~> @on \update ~>
@posts[@posts.length - 1] @posts.for-each (post) ~>
date = (new Date post.created_at).get-date!
month = (new Date post.created_at).get-month! + 1
post._date = date
post._datetext = month + '月 ' + date + '日'
@tail = ~>
@posts[@posts.length - 1]
</script>
</mk-timeline>

View file

@ -1,219 +1,212 @@
mk-ui-header-account <mk-ui-header-account>
button.header(data-active={ is-open.toString() }, onclick={ toggle }) <button class="header" data-active="{ isOpen.toString() }" onclick="{ toggle }"><span class="username">{ I.username }<i class="fa fa-angle-down" if="{ !isOpen }"></i><i class="fa fa-angle-up" if="{ isOpen }"></i></span><img class="avatar" src="{ I.avatar_url + '?thumbnail&amp;size=64' }" alt="avatar"/></button>
span.username <div class="menu" if="{ isOpen }">
| { I.username } <ul>
i.fa.fa-angle-down(if={ !is-open }) <li><a href="{ '/' + I.username }"><i class="fa fa-user"></i>プロフィール<i class="fa fa-angle-right"></i></a></li>
i.fa.fa-angle-up(if={ is-open }) <li onclick="{ drive }">
img.avatar(src={ I.avatar_url + '?thumbnail&size=64' }, alt='avatar') <p><i class="fa fa-cloud"></i>ドライブ<i class="fa fa-angle-right"></i></p>
div.menu(if={ is-open }) </li>
ul <li><a href="/i&gt;mentions"><i class="fa fa-at"></i>あなた宛て<i class="fa fa-angle-right"></i></a></li>
li: a(href={ '/' + I.username }) </ul>
i.fa.fa-user <ul>
| プロフィール <li onclick="{ settings }">
i.fa.fa-angle-right <p><i class="fa fa-cog"></i>設定<i class="fa fa-angle-right"></i></p>
li(onclick={ drive }): p </li>
i.fa.fa-cloud </ul>
| ドライブ <ul>
i.fa.fa-angle-right <li onclick="{ signout }">
li: a(href='/i>mentions') <p><i class="fa fa-power-off"></i>サインアウト<i class="fa fa-angle-right"></i></p>
i.fa.fa-at </li>
| あなた宛て </ul>
i.fa.fa-angle-right </div>
ul <style type="stylus">
li(onclick={ settings }): p :scope
i.fa.fa-cog
| 設定
i.fa.fa-angle-right
ul
li(onclick={ signout }): p
i(class='fa fa-power-off')
| サインアウト
i.fa.fa-angle-right
style.
display block
float left
> .header
display block
margin 0
padding 0
color #9eaba8
border none
background transparent
cursor pointer
*
pointer-events none
&:hover
color darken(#9eaba8, 20%)
&:active
color darken(#9eaba8, 30%)
&[data-active='true']
color darken(#9eaba8, 20%)
> .avatar
$saturate = 150%
filter saturate($saturate)
-webkit-filter saturate($saturate)
-moz-filter saturate($saturate)
-ms-filter saturate($saturate)
> .username
display block display block
float left float left
margin 0 12px 0 16px
max-width 16em
line-height 48px
font-weight bold
font-family Meiryo, sans-serif
text-decoration none
i > .header
margin-left 8px
> .avatar
display block
float left
min-width 32px
max-width 32px
min-height 32px
max-height 32px
margin 8px 8px 8px 0
border-radius 4px
transition filter 100ms ease
> .menu
display block
position absolute
top 56px
right -2px
width 230px
font-size 0.8em
background #fff
border-radius 4px
box-shadow 0 1px 4px rgba(0, 0, 0, 0.25)
&:before
content ""
pointer-events none
display block
position absolute
top -28px
right 12px
border-top solid 14px transparent
border-right solid 14px transparent
border-bottom solid 14px rgba(0, 0, 0, 0.1)
border-left solid 14px transparent
&:after
content ""
pointer-events none
display block
position absolute
top -27px
right 12px
border-top solid 14px transparent
border-right solid 14px transparent
border-bottom solid 14px #fff
border-left solid 14px transparent
ul
display block
margin 10px 0
padding 0
list-style none
& + ul
padding-top 10px
border-top solid 1px #eee
> li
display block display block
margin 0 margin 0
padding 0 padding 0
color #9eaba8
border none
background transparent
cursor pointer
> a *
> p pointer-events none
&:hover
color darken(#9eaba8, 20%)
&:active
color darken(#9eaba8, 30%)
&[data-active='true']
color darken(#9eaba8, 20%)
> .avatar
$saturate = 150%
filter saturate($saturate)
-webkit-filter saturate($saturate)
-moz-filter saturate($saturate)
-ms-filter saturate($saturate)
> .username
display block display block
z-index 1 float left
padding 0 28px margin 0 12px 0 16px
margin 0 max-width 16em
line-height 40px line-height 48px
color #868C8C font-weight bold
cursor pointer font-family Meiryo, sans-serif
text-decoration none
* i
pointer-events none margin-left 8px
> i:first-of-type > .avatar
margin-right 6px display block
float left
min-width 32px
max-width 32px
min-height 32px
max-height 32px
margin 8px 8px 8px 0
border-radius 4px
transition filter 100ms ease
> i:last-of-type > .menu
display block
position absolute
top 56px
right -2px
width 230px
font-size 0.8em
background #fff
border-radius 4px
box-shadow 0 1px 4px rgba(0, 0, 0, 0.25)
&:before
content ""
pointer-events none
display block
position absolute
top -28px
right 12px
border-top solid 14px transparent
border-right solid 14px transparent
border-bottom solid 14px rgba(0, 0, 0, 0.1)
border-left solid 14px transparent
&:after
content ""
pointer-events none
display block
position absolute
top -27px
right 12px
border-top solid 14px transparent
border-right solid 14px transparent
border-bottom solid 14px #fff
border-left solid 14px transparent
ul
display block
margin 10px 0
padding 0
list-style none
& + ul
padding-top 10px
border-top solid 1px #eee
> li
display block display block
position absolute margin 0
top 0 padding 0
right 8px
z-index 1
padding 0 20px
font-size 1.2em
line-height 40px
&:hover, &:active > a
text-decoration none > p
background $theme-color display block
color $theme-color-foreground z-index 1
padding 0 28px
margin 0
line-height 40px
color #868C8C
cursor pointer
script. *
@mixin \i pointer-events none
@mixin \signout
@is-open = false > i:first-of-type
margin-right 6px
@on \before-unmount ~> > i:last-of-type
@close! display block
position absolute
top 0
right 8px
z-index 1
padding 0 20px
font-size 1.2em
line-height 40px
@toggle = ~> &:hover, &:active
if @is-open text-decoration none
@close! background $theme-color
else color $theme-color-foreground
@open!
@open = ~> </style>
@is-open = true <script>
@update! @mixin \i
all = document.query-selector-all 'body *' @mixin \signout
Array.prototype.for-each.call all, (el) ~>
el.add-event-listener \mousedown @mousedown
@close = ~>
@is-open = false @is-open = false
@update!
all = document.query-selector-all 'body *'
Array.prototype.for-each.call all, (el) ~>
el.remove-event-listener \mousedown @mousedown
@mousedown = (e) ~> @on \before-unmount ~>
e.prevent-default!
if (!contains @root, e.target) and (@root != e.target)
@close! @close!
return false
@drive = ~> @toggle = ~>
@close! if @is-open
riot.mount document.body.append-child document.create-element \mk-drive-browser-window @close!
else
@open!
@settings = ~> @open = ~>
@close! @is-open = true
riot.mount document.body.append-child document.create-element \mk-settings-window @update!
all = document.query-selector-all 'body *'
Array.prototype.for-each.call all, (el) ~>
el.add-event-listener \mousedown @mousedown
function contains(parent, child) @close = ~>
node = child.parent-node @is-open = false
while node? @update!
if node == parent all = document.query-selector-all 'body *'
return true Array.prototype.for-each.call all, (el) ~>
node = node.parent-node el.remove-event-listener \mousedown @mousedown
return false
@mousedown = (e) ~>
e.prevent-default!
if (!contains @root, e.target) and (@root != e.target)
@close!
return false
@drive = ~>
@close!
riot.mount document.body.append-child document.create-element \mk-drive-browser-window
@settings = ~>
@close!
riot.mount document.body.append-child document.create-element \mk-settings-window
function contains(parent, child)
node = child.parent-node
while node?
if node == parent
return true
node = node.parent-node
return false
</script>
</mk-ui-header-account>

View file

@ -1,82 +1,87 @@
mk-ui-header-clock <mk-ui-header-clock>
div.header <div class="header">
time@time <time ref="time"></time>
div.content </div>
mk-analog-clock <div class="content">
<mk-analog-clock></mk-analog-clock>
</div>
<style type="stylus">
:scope
display inline-block
overflow visible
style. > .header
display inline-block padding 0 12px
overflow visible text-align center
font-size 0.5em
> .header &, *
padding 0 12px cursor: default
text-align center
font-size 0.5em
&, * &:hover
cursor: default background #899492
&:hover & + .content
background #899492 visibility visible
& + .content > time
visibility visible color #fff !important
> time *
color #fff !important color #fff !important
* &:after
color #fff !important content ""
display block
clear both
&:after > time
content "" display table-cell
display block vertical-align middle
clear both height 48px
color #9eaba8
> time > .yyyymmdd
display table-cell opacity 0.7
vertical-align middle
height 48px
color #9eaba8
> .yyyymmdd > .content
opacity 0.7 visibility hidden
display block
position absolute
top auto
right 0
z-index 3
margin 0
padding 0
width 256px
background #899492
> .content </style>
visibility hidden <script>
display block @draw = ~>
position absolute now = new Date!
top auto
right 0
z-index 3
margin 0
padding 0
width 256px
background #899492
script. yyyy = now.get-full-year!
@draw = ~> mm = (\0 + (now.get-month! + 1)).slice -2
now = new Date! dd = (\0 + now.get-date!).slice -2
yyyymmdd = "<span class='yyyymmdd'>#yyyy/#mm/#dd</span>"
yyyy = now.get-full-year! hh = (\0 + now.get-hours!).slice -2
mm = (\0 + (now.get-month! + 1)).slice -2 mm = (\0 + now.get-minutes!).slice -2
dd = (\0 + now.get-date!).slice -2 hhmm = "<span class='hhmm'>#hh:#mm</span>"
yyyymmdd = "<span class='yyyymmdd'>#yyyy/#mm/#dd</span>"
hh = (\0 + now.get-hours!).slice -2 if now.get-seconds! % 2 == 0
mm = (\0 + now.get-minutes!).slice -2 hhmm .= replace \: '<span style=\'visibility:visible\'>:</span>'
hhmm = "<span class='hhmm'>#hh:#mm</span>" else
hhmm .= replace \: '<span style=\'visibility:hidden\'>:</span>'
if now.get-seconds! % 2 == 0 @refs.time.innerHTML = "#yyyymmdd<br>#hhmm"
hhmm .= replace \: '<span style=\'visibility:visible\'>:</span>'
else
hhmm .= replace \: '<span style=\'visibility:hidden\'>:</span>'
@refs.time.innerHTML = "#yyyymmdd<br>#hhmm" @on \mount ~>
@draw!
@clock = set-interval @draw, 1000ms
@on \mount ~> @on \unmount ~>
@draw! clear-interval @clock
@clock = set-interval @draw, 1000ms </script>
</mk-ui-header-clock>
@on \unmount ~>
clear-interval @clock

View file

@ -1,113 +1,113 @@
mk-ui-header-nav: ul(if={ SIGNIN }) <mk-ui-header-nav>
li.home(class={ active: page == 'home' }): a(href={ CONFIG.url }) <ul if="{ SIGNIN }">
i.fa.fa-home <li class="home { active: page == 'home' }"><a href="{ CONFIG.url }"><i class="fa fa-home"></i>
p ホーム <p>ホーム</p></a></li>
li.messaging: a(onclick={ messaging }) <li class="messaging"><a onclick="{ messaging }"><i class="fa fa-comments"></i>
i.fa.fa-comments <p>メッセージ</p><i class="fa fa-circle" if="{ hasUnreadMessagingMessages }"></i></a></li>
p メッセージ <li class="info"><a href="https://twitter.com/misskey_xyz" target="_blank"><i class="fa fa-info"></i>
i.fa.fa-circle(if={ has-unread-messaging-messages }) <p>お知らせ</p></a></li>
li.info: a(href='https://twitter.com/misskey_xyz', target='_blank') <li class="tv"><a href="https://misskey.tk" target="_blank"><i class="fa fa-television"></i>
i.fa.fa-info <p>MisskeyTV™</p></a></li>
p お知らせ <style type="stylus">
li.tv: a(href='https://misskey.tk', target='_blank') :scope
i.fa.fa-television
p MisskeyTV™
style.
display inline-block
margin 0
padding 0
line-height 3rem
vertical-align top
> ul
display inline-block
margin 0
padding 0
vertical-align top
line-height 3rem
list-style none
> li
display inline-block
vertical-align top
height 48px
line-height 48px
&.active
> a
border-bottom solid 3px $theme-color
> a
display inline-block display inline-block
z-index 1 margin 0
height 100% padding 0
padding 0 24px line-height 3rem
font-size 1em vertical-align top
font-variant small-caps
color #9eaba8
text-decoration none
transition none
cursor pointer
* > ul
pointer-events none display inline-block
&:hover
color darken(#9eaba8, 20%)
text-decoration none
> i:first-child
margin-right 8px
> i:last-child
margin-left 5px
vertical-align super
font-size 10px
color $theme-color
@media (max-width 1100px)
margin-left -5px
> p
display inline
margin 0 margin 0
padding 0
vertical-align top
line-height 3rem
list-style none
@media (max-width 1100px) > li
display none display inline-block
vertical-align top
height 48px
line-height 48px
@media (max-width 700px) &.active
padding 0 12px > a
border-bottom solid 3px $theme-color
script. > a
@mixin \i display inline-block
@mixin \api z-index 1
@mixin \stream height 100%
padding 0 24px
font-size 1em
font-variant small-caps
color #9eaba8
text-decoration none
transition none
cursor pointer
@page = @opts.page *
pointer-events none
@on \mount ~> &:hover
@stream.on \read_all_messaging_messages @on-read-all-messaging-messages color darken(#9eaba8, 20%)
@stream.on \unread_messaging_message @on-unread-messaging-message text-decoration none
# Fetch count of unread messaging messages > i:first-child
@api \messaging/unread margin-right 8px
.then (count) ~>
if count.count > 0 > i:last-child
margin-left 5px
vertical-align super
font-size 10px
color $theme-color
@media (max-width 1100px)
margin-left -5px
> p
display inline
margin 0
@media (max-width 1100px)
display none
@media (max-width 700px)
padding 0 12px
</style>
<script>
@mixin \i
@mixin \api
@mixin \stream
@page = @opts.page
@on \mount ~>
@stream.on \read_all_messaging_messages @on-read-all-messaging-messages
@stream.on \unread_messaging_message @on-unread-messaging-message
# Fetch count of unread messaging messages
@api \messaging/unread
.then (count) ~>
if count.count > 0
@has-unread-messaging-messages = true
@update!
@on \unmount ~>
@stream.off \read_all_messaging_messages @on-read-all-messaging-messages
@stream.off \unread_messaging_message @on-unread-messaging-message
@on-read-all-messaging-messages = ~>
@has-unread-messaging-messages = false
@update!
@on-unread-messaging-message = ~>
@has-unread-messaging-messages = true @has-unread-messaging-messages = true
@update! @update!
@on \unmount ~> @messaging = ~>
@stream.off \read_all_messaging_messages @on-read-all-messaging-messages riot.mount document.body.append-child document.create-element \mk-messaging-window
@stream.off \unread_messaging_message @on-unread-messaging-message </script>
</ul>
@on-read-all-messaging-messages = ~> </mk-ui-header-nav>
@has-unread-messaging-messages = false
@update!
@on-unread-messaging-message = ~>
@has-unread-messaging-messages = true
@update!
@messaging = ~>
riot.mount document.body.append-child document.create-element \mk-messaging-window

View file

@ -1,111 +1,114 @@
mk-ui-header-notifications <mk-ui-header-notifications>
button.header(data-active={ is-open }, onclick={ toggle }) <button class="header" data-active="{ isOpen }" onclick="{ toggle }"><i class="fa fa-bell-o"></i></button>
i.fa.fa-bell-o <div class="notifications" if="{ isOpen }">
div.notifications(if={ is-open }) <mk-notifications></mk-notifications>
mk-notifications </div>
<style type="stylus">
style. :scope
display block
float left
> .header
display block
margin 0
padding 0
width 32px
color #9eaba8
border none
background transparent
cursor pointer
*
pointer-events none
&:hover
color darken(#9eaba8, 20%)
&:active
color darken(#9eaba8, 30%)
&[data-active='true']
color darken(#9eaba8, 20%)
> i
font-size 1.2em
line-height 48px
> .notifications
display block
position absolute
top 56px
right -72px
width 300px
background #fff
border-radius 4px
box-shadow 0 1px 4px rgba(0, 0, 0, 0.25)
&:before
content ""
pointer-events none
display block display block
position absolute float left
top -28px
right 74px
border-top solid 14px transparent
border-right solid 14px transparent
border-bottom solid 14px rgba(0, 0, 0, 0.1)
border-left solid 14px transparent
&:after > .header
content "" display block
pointer-events none margin 0
display block padding 0
position absolute width 32px
top -27px color #9eaba8
right 74px border none
border-top solid 14px transparent background transparent
border-right solid 14px transparent cursor pointer
border-bottom solid 14px #fff
border-left solid 14px transparent
> mk-notifications *
max-height 350px pointer-events none
font-size 1rem
overflow auto
script. &:hover
@is-open = false color darken(#9eaba8, 20%)
@toggle = ~> &:active
if @is-open color darken(#9eaba8, 30%)
@close!
else
@open!
@open = ~> &[data-active='true']
@is-open = true color darken(#9eaba8, 20%)
@update!
all = document.query-selector-all 'body *'
Array.prototype.for-each.call all, (el) ~>
el.add-event-listener \mousedown @mousedown
@close = ~> > i
font-size 1.2em
line-height 48px
> .notifications
display block
position absolute
top 56px
right -72px
width 300px
background #fff
border-radius 4px
box-shadow 0 1px 4px rgba(0, 0, 0, 0.25)
&:before
content ""
pointer-events none
display block
position absolute
top -28px
right 74px
border-top solid 14px transparent
border-right solid 14px transparent
border-bottom solid 14px rgba(0, 0, 0, 0.1)
border-left solid 14px transparent
&:after
content ""
pointer-events none
display block
position absolute
top -27px
right 74px
border-top solid 14px transparent
border-right solid 14px transparent
border-bottom solid 14px #fff
border-left solid 14px transparent
> mk-notifications
max-height 350px
font-size 1rem
overflow auto
</style>
<script>
@is-open = false @is-open = false
@update!
all = document.query-selector-all 'body *'
Array.prototype.for-each.call all, (el) ~>
el.remove-event-listener \mousedown @mousedown
@mousedown = (e) ~> @toggle = ~>
e.prevent-default! if @is-open
if (!contains @root, e.target) and (@root != e.target) @close!
@close! else
return false @open!
function contains(parent, child) @open = ~>
node = child.parent-node @is-open = true
while node? @update!
if node == parent all = document.query-selector-all 'body *'
return true Array.prototype.for-each.call all, (el) ~>
node = node.parent-node el.add-event-listener \mousedown @mousedown
return false
@close = ~>
@is-open = false
@update!
all = document.query-selector-all 'body *'
Array.prototype.for-each.call all, (el) ~>
el.remove-event-listener \mousedown @mousedown
@mousedown = (e) ~>
e.prevent-default!
if (!contains @root, e.target) and (@root != e.target)
@close!
return false
function contains(parent, child)
node = child.parent-node
while node?
if node == parent
return true
node = node.parent-node
return false
</script>
</mk-ui-header-notifications>

View file

@ -1,39 +1,41 @@
mk-ui-header-post-button <mk-ui-header-post-button>
button(onclick={ post }, title='新規投稿') <button onclick="{ post }" title="新規投稿"><i class="fa fa-pencil-square-o"></i></button>
i.fa.fa-pencil-square-o <style type="stylus">
:scope
display inline-block
padding 8px
height 100%
vertical-align top
style. > button
display inline-block display inline-block
padding 8px margin 0
height 100% padding 0 10px
vertical-align top height 100%
font-size 1.2em
font-weight normal
text-decoration none
color $theme-color-foreground
background $theme-color !important
outline none
border none
border-radius 2px
transition background 0.1s ease
cursor pointer
> button *
display inline-block pointer-events none
margin 0
padding 0 10px
height 100%
font-size 1.2em
font-weight normal
text-decoration none
color $theme-color-foreground
background $theme-color !important
outline none
border none
border-radius 2px
transition background 0.1s ease
cursor pointer
* &:hover
pointer-events none background lighten($theme-color, 10%) !important
&:hover &:active
background lighten($theme-color, 10%) !important background darken($theme-color, 10%) !important
transition background 0s ease
&:active </style>
background darken($theme-color, 10%) !important <script>
transition background 0s ease @post = (e) ~>
@parent.parent.open-post-form!
script. </script>
@post = (e) ~> </mk-ui-header-post-button>
@parent.parent.open-post-form!

View file

@ -1,37 +1,41 @@
mk-ui-header-search <mk-ui-header-search>
form.search(onsubmit={ onsubmit }) <form class="search" onsubmit="{ onsubmit }">
input@q(type='search', placeholder!='&#xf002; 検索') <input ref="q" type="search" placeholder="&#xf002; 検索"/>
div.result <div class="result"></div>
</form>
<style type="stylus">
:scope
style. > form
display block
float left
> form > input
display block user-select text
float left cursor auto
margin 0
padding 6px 18px
width 14em
height 48px
font-size 1em
line-height calc(48px - 12px)
background transparent
outline none
//border solid 1px #ddd
border none
border-radius 0
transition color 0.5s ease, border 0.5s ease
font-family FontAwesome, sans-serif
> input &::-webkit-input-placeholder
user-select text color #9eaba8
cursor auto
margin 0
padding 6px 18px
width 14em
height 48px
font-size 1em
line-height calc(48px - 12px)
background transparent
outline none
//border solid 1px #ddd
border none
border-radius 0
transition color 0.5s ease, border 0.5s ease
font-family FontAwesome, sans-serif
&::-webkit-input-placeholder </style>
color #9eaba8 <script>
@mixin \page
script. @onsubmit = (e) ~>
@mixin \page e.prevent-default!
@page '/search:' + @refs.q.value
@onsubmit = (e) ~> </script>
e.prevent-default! </mk-ui-header-search>
@page '/search:' + @refs.q.value

Some files were not shown because too many files have changed in this diff Show more