Compare commits

..

87 Commits

Author SHA1 Message Date
f6b2f76bbf 10.39.1 2018-11-06 01:52:39 +09:00
1235bef038 [API] Fix bug 2018-11-06 01:51:42 +09:00
2e11f3a843 Clean up 2018-11-06 01:49:35 +09:00
84b7e0bb7d 🎨 2018-11-06 01:48:33 +09:00
9f5dc2c0df [WIP] Use FontAwesome Component for Vue (#3127)
* wip

* Rename

* Clean up

* Clean up

* wip

* wip

* Enable tree shaking

* ✌️

* ✌️

* wip

* wip

* Clean up
2018-11-06 01:40:11 +09:00
e640dbc501 🎨 2018-11-05 22:45:45 +09:00
85db090d9f 🎨 2018-11-05 22:42:08 +09:00
9f2d8e1d51 10.39.0 2018-11-05 21:07:24 +09:00
0c98a90b75 [Client] カスタム絵文字にホバーしたときに拡大するエフェクトを追加 2018-11-05 21:04:19 +09:00
0047920c1a Merge pull request #3117 from syuilo/twemoji
Use Twemoji
2018-11-05 20:52:55 +09:00
e4bb534f20 Better emoji regexp 2018-11-05 20:49:17 +09:00
3fc04fcdc5 Improve readdability 2018-11-05 20:49:02 +09:00
e542dcac30 Fix test 2018-11-05 20:40:39 +09:00
a0b13505a0 Insert missing spaces 2018-11-05 20:15:09 +09:00
389f9bfea2 Add test 2018-11-05 20:14:49 +09:00
630a534cee Fix test 2018-11-05 20:10:28 +09:00
5744c391e6 Revert "Fix test fails"
This reverts commit b9b05a7401.
2018-11-05 20:10:00 +09:00
b9b05a7401 Fix test fails 2018-11-05 19:50:38 +09:00
359470a263 Fix bug 2018-11-05 19:40:09 +09:00
3fe934ee62 Better alt value 2018-11-05 19:33:28 +09:00
3abe632f06 Clean up 2018-11-05 19:29:50 +09:00
65961bc15b Refactoring & 設定でTwemojiを使うかどうか切り替えられるように 2018-11-05 19:20:35 +09:00
12f932d48a Update CI configuration (#3120)
* Update config.yml

* Add `npm prune` command

refs: https://misskey.xyz/notes/5bd9b87168b2a30045edb3aa

* Ensure package-lock.json exists
2018-11-05 17:38:57 +09:00
54e9147782 Refactoring codes
refs: https://github.com/syuilo/misskey/pull/3117#pullrequestreview-171437187
2018-11-05 17:04:17 +09:00
31b7626d01 Make code better
refs: https://github.com/syuilo/misskey/pull/3117#pullrequestreview-171423739
refs: https://github.com/syuilo/misskey/pull/3117#pullrequestreview-171424596
refs: https://github.com/syuilo/misskey/pull/3117#pullrequestreview-171425303
2018-11-05 16:19:14 +09:00
200ebefe92 Add support for unicode emojis
refs: https://github.com/syuilo/misskey/pull/3117#issuecomment-435745613
2018-11-05 15:15:37 +09:00
9d29a2e85a 10.38.8 2018-11-05 13:47:57 +09:00
c62a225542 oops 2018-11-05 13:46:46 +09:00
d5d995a3e6 Refactor 2018-11-05 13:38:50 +09:00
b7f10fdc10 Fix bug
refs: https://github.com/syuilo/misskey/pull/3117#discussion_r230624389
2018-11-05 13:24:54 +09:00
cbba03b376 [Client] Fix bug 2018-11-05 13:23:30 +09:00
f84e9c7dc8 絵文字サジェストでスペースを挿入しないように 2018-11-05 12:35:50 +09:00
a22ddb1fb9 ✌️ 2018-11-05 11:58:41 +09:00
0d23ce3d45 Make /api/v1/instance and /api/v1/custom_emojis better (#3118)
* Separate commits

From commit dca110ebaa.

* Re-separate commits

From commit 9719387bee.
2018-11-05 11:57:17 +09:00
9719387bee Re-separate commits 2018-11-05 11:51:14 +09:00
dca110ebaa Separate commits
Flash Back 90's
2018-11-05 11:39:13 +09:00
136f23c7ad Merge branch 'develop' into twemoji 2018-11-05 11:21:34 +09:00
0963e6d6e1 Use Twemoji 2018-11-05 11:19:40 +09:00
712802e682 10.38.7 2018-11-05 11:11:23 +09:00
abe99c3c73 Update locales/ja-JP.yml 2018-11-05 11:10:02 +09:00
d7a3b71028 投稿の最大文字数情報を設定ファイルではなくDBに保存するように 2018-11-05 11:09:05 +09:00
10c434f24a Remove Travis
Closes #3109
2018-11-05 10:52:07 +09:00
fe46c53ea6 Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2018-11-05 10:48:51 +09:00
cdd123dfd3 [doc] specify node version 2018-11-05 10:48:40 +09:00
a1a3ee44b5 Implement /api/v1/custom_emojis (#3116) 2018-11-05 10:45:57 +09:00
4e7fbd8967 Implement /api/v1/custom_emojis 2018-11-05 10:42:46 +09:00
a86c419f95 Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2018-11-05 10:40:15 +09:00
e3ec0ad97e [Client] Improve admin panel usability 2018-11-05 10:40:01 +09:00
75791981ce Fix #3115 2018-11-05 10:34:53 +09:00
e813fe16b9 [API] Better validation of admin/emoji/add 2018-11-05 10:33:49 +09:00
42ac7b954d Improve admin panel usability 2018-11-05 10:32:45 +09:00
c1bbf5dab6 [Client] Fix error 2018-11-05 10:29:57 +09:00
e16dc2a910 Update README.md (#3112) 2018-11-05 01:57:08 +09:00
e236c05d79 10.38.6 2018-11-05 01:43:31 +09:00
454c1e3faf [API] Fix bug 2018-11-05 01:42:41 +09:00
43daf814df [Client] 絵文字登録フォームに便利情報を表示 2018-11-05 01:33:06 +09:00
c40b630530 10.38.5 2018-11-04 23:20:06 +09:00
7fc0698ecf 🎨 2018-11-04 23:15:46 +09:00
4f3c8b940e [API] Fix #3099 2018-11-04 23:13:35 +09:00
1855ab60f1 Resolve #3098 2018-11-04 23:00:43 +09:00
af4f1a7bd6 Clean up 2018-11-04 22:05:42 +09:00
8646a9c49c Add GitHub auth (#3095) 2018-11-04 22:03:55 +09:00
8d7c033cf5 Clean up 2018-11-04 21:21:34 +09:00
b8900e32de 🎨 2018-11-04 21:14:17 +09:00
d48c25d2c9 [API] Fix #3097 2018-11-04 21:11:54 +09:00
a87c5899c5 Fix typo 2018-11-04 20:08:31 +09:00
147ad69864 Revert "Add GitHub auth"
This reverts commit c146006476.
2018-11-04 19:22:04 +09:00
c146006476 Add GitHub auth 2018-11-04 19:17:30 +09:00
a0f10d7ca1 10.38.4 2018-11-04 18:38:04 +09:00
299b91edc4 [API] Improve admin/emoji/add 2018-11-04 18:37:12 +09:00
95c89ca6db RE: [Client] Fix bug 2018-11-04 18:36:19 +09:00
7fe0d71e7f [Client] Fix bug 2018-11-04 18:35:55 +09:00
fbbb506e86 🎨 2018-11-04 18:31:27 +09:00
ec80b06a45 Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2018-11-04 18:24:20 +09:00
41e1619f1f [Client] Fix bug 2018-11-04 18:24:08 +09:00
ba6a9c6a93 Merge pull request #3092 from syuilo/l10n_develop
New Crowdin translations
2018-11-04 18:22:19 +09:00
18571c52fb Fix: emoji regex (#3093) 2018-11-04 17:36:37 +09:00
5d5dfeaa83 New translations ja-JP.yml (Japanese, Kansai) 2018-11-04 17:11:19 +09:00
3669d8c0f3 New translations ja-JP.yml (Japanese, Kansai) 2018-11-04 17:01:11 +09:00
69d72819c6 10.38.3 2018-11-04 15:18:37 +09:00
54dcc10250 Fix bug for Mastodon(?) 2018-11-04 15:17:52 +09:00
1edfce8f73 [Client] スマホ/タブレットからでも管理者ページを使えるように 2018-11-04 15:16:05 +09:00
675e573a8c 🎨 2018-11-04 14:23:28 +09:00
1080fa63a9 10.38.2 2018-11-04 11:09:31 +09:00
8047086988 Good bye package-lock 2018-11-04 11:08:46 +09:00
449b9f7fa0 [Client] Improve admin panel 2018-11-04 11:08:03 +09:00
b7a15bf6ca 絵文字を作成した/更新した時にupdateAtを更新するように 2018-11-04 10:42:16 +09:00
244 changed files with 2114 additions and 19392 deletions

View File

@ -23,6 +23,10 @@ jobs:
executor: default
steps:
- checkout
- run:
name: Ensure package-lock.json
command: |
[ ! -e package-lock.json ] && echo '{}' > package-lock.json
- restore_cache:
name: Restore npm package caches
keys:
@ -35,6 +39,7 @@ jobs:
name: Install Dependencies
command: |
npm install
npm prune
- run:
name: Configure
command: |
@ -50,8 +55,8 @@ jobs:
key: npm-v1-arch-{{ arch }}-env-{{ .Environment.variableName }}-package-{{ checksum "package.json" }}-lock-{{ checksum "package-lock.json" }}-ls-{{ checksum "ls" }}
paths:
- node_modules
- store_artifacts:
path: built
# - store_artifacts:
# path: built
- persist_to_workspace:
root: .
paths:
@ -98,7 +103,6 @@ jobs:
name: Build
command: |
docker build . | tee docker.log
tail -n 1 docker.log | read __Successfully __built tag
- when:
condition: <<parameters.with_deploy>>
steps:
@ -107,6 +111,7 @@ jobs:
command: |
if [ "$DOCKERHUB_USERNAME$DOCKERHUB_PASSWORD" ]
then
tail -n 1 docker.log | read __Successfully __built tag
docker tag $tag misskey/misskey
docker login -u $DOCKERHUB_USERNAME -p $DOCKERHUB_PASSWORD
docker push misskey/misskey
@ -126,10 +131,13 @@ workflows:
without_redis: "true"
requires:
- build
- docker:
filters:
branches:
ignore: master
only: master
# - docker:
# filters:
# branches:
# ignore: master
- docker:
with_deploy: "true"
filters:

View File

@ -1,6 +1,3 @@
name: example-instance-name # Name of your instance
description: example-description # Description of your instance
maintainer:
name: example-maitainer-name # Your name
url: http://example.com/ # Your contact (http or mailto)
@ -25,7 +22,7 @@ url: https://example.tld/
# +------+ |+-------------+ +----------------+|
# +---------------------------------------+
#
# You need to setup reverse proxy. (eg. Nginx)
# You need to setup reverse proxy. (eg. nginx)
# You do not define 'https' section.
# Option 2: Standalone
@ -148,6 +145,12 @@ drive:
# consumer_key: example-twitter-consumer-key
# consumer_secret: example-twitter-consumer-secret-key
# GitHub integration
# You need to set the oauth callback url as : https://<your-misskey-instance>/api/gh/cb
#github:
# client_id: example-github-client-id
# client_secret: example-github-client-secret
# Ghost
# Ghost account is an account used for the purpose of delegating
# followers when putting users in the list.
@ -164,6 +167,3 @@ drive:
# external: true
# engine: http://vinayaka.distsn.org/cgi-bin/vinayaka-user-match-misskey-api.cgi?{{host}}+{{user}}+{{limit}}+{{offset}}
# timeout: 300000
# Max allowed note text length in charactors
maxNoteTextLength: 1000

1
.npmrc
View File

@ -1 +1,2 @@
save-exact = true
package-lock = false

View File

@ -1,41 +0,0 @@
# travis file
# https://docs.travis-ci.com/user/customizing-the-build
notifications:
email: false
branches:
except:
- l10n_master
language: node_js
node_js:
- 11.0.0
env:
- CXX=g++-4.8 NODE_ENV=production
addons:
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- g++-4.8
cache:
directories:
- node_modules
services:
- mongodb
- redis-server
before_script:
- npm install
# 設定ファイルを配置
- cp ./.ci/default.yml ./.config
- cp ./.ci/test.yml ./.config
- travis_wait npm run build

View File

@ -23,5 +23,5 @@ Please use [Crowdin](https://crowdin.com/project/misskey) for localization.
* Test codes are located in `/test`.
## Continuous integration
Misskey uses Travis for automated test.
Configuration files are located in `/.travis`.
Misskey uses CircleCI for automated test.
Configuration files are located in `/.circleci`.

View File

@ -4,7 +4,6 @@
================================================================
[![CircleCI](https://circleci.com/gh/syuilo/misskey.svg?style=svg)](https://circleci.com/gh/syuilo/misskey)
[![][travis-badge]][travis-link]
[![][dependencies-badge]][dependencies-link]
[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com)
@ -44,7 +43,7 @@ Easiest way to tell your emotions. Misskey allows you to add various type of rea
<h3 align="left">Interface</h3>
<p align="left">
No UI fits for everyone. Therefore, Misskey has a highly customizable UI for your taste. You can edit layouts of your timeline, place selectable widgets you can easily move and create your unique home as this place will be your home.
Highly customizable UI for your taste. We understand no UI fits for everyone. You can edit layouts of your timeline, place selectable widgets you can easily move and create your unique home as this place will be your home.
</p>
---
@ -124,8 +123,6 @@ Misskey is an open-source software licensed under the [GNU AGPLv3](LICENSE).
[agpl-3.0]: https://www.gnu.org/licenses/agpl-3.0.en.html
[agpl-3.0-badge]: https://img.shields.io/badge/license-AGPL--3.0-444444.svg?style=flat-square
[travis-link]: https://travis-ci.org/syuilo/misskey
[travis-badge]: http://img.shields.io/travis/syuilo/misskey/master.svg?style=flat-square
[dependencies-link]: https://david-dm.org/syuilo/misskey
[dependencies-badge]: https://img.shields.io/david/syuilo/misskey.svg?style=flat-square

View File

@ -22,7 +22,7 @@ adduser --disabled-password --disabled-login misskey
Please install and setup these softwares:
#### Dependencies :package:
* **[Node.js](https://nodejs.org/en/)**
* **[Node.js](https://nodejs.org/en/)** >= 10.0.0
* **[MongoDB](https://www.mongodb.com/)** >= 3.6
##### Optional

View File

@ -22,7 +22,7 @@ adduser --disabled-password --disabled-login misskey
これらのソフトウェアをインストール・設定してください:
#### 依存関係 :package:
* **[Node.js](https://nodejs.org/en/)**
* **[Node.js](https://nodejs.org/en/)** (10.0.0以上)
* **[MongoDB](https://www.mongodb.com/)** (3.6以上)
##### オプション

View File

@ -21,7 +21,6 @@ import * as htmlmin from 'gulp-htmlmin';
const uglifyes = require('uglify-es');
const locales = require('./locales');
import { fa } from './src/misc/fa';
const uglify = uglifyComposer(uglifyes, console);
@ -164,8 +163,7 @@ gulp.task('build:client:pug', [
gulp.src('./src/client/app/base.pug')
.pipe(pug({
locals: {
themeColor: constants.themeColor,
facss: fa.dom.css()
themeColor: constants.themeColor
}
}))
.pipe(htmlmin({

View File

@ -131,6 +131,7 @@ common:
show-full-acct: "ユーザー名のホストを省略しない"
reduce-motion: "UIの動きを減らす"
this-setting-is-this-device-only: "このデバイスのみ"
use-os-default-emojis: "OS標準の絵文字を使用"
do-not-use-in-production: 'これは開発ビルドです。本番環境で使用しないでください。'
@ -417,6 +418,7 @@ common/views/components/signin.vue:
signin: "サインイン"
or: "または"
signin-with-twitter: "Twitterでログイン"
signin-with-github: "GitHubでログイン"
login-failed: "ログインできませんでした。ユーザー名とパスワードを確認してください。"
common/views/components/signup.vue:
@ -460,6 +462,14 @@ common/views/components/twitter-setting.vue:
connect: "Twitterと接続する"
disconnect: "切断する"
common/views/components/github-setting.vue:
description: "お使いのGitHubアカウントをお使いのMisskeyアカウントに接続しておくと、プロフィールでGitHubアカウント情報が表示されるようになったり、GitHubを用いた便利なサインインを利用できるようになります。"
connected-to: "次のGitHubアカウントに接続されています"
detail: "詳細..."
reconnect: "再接続する"
connect: "GitHubと接続する"
disconnect: "切断する"
common/views/components/uploader.vue:
waiting: "待機中"
@ -599,32 +609,6 @@ desktop/views/components/calendar.vue:
next: "次の月"
go: "クリックして時間遡行"
desktop/views/components/charts.vue:
title: "チャート"
per-day: "1日ごと"
per-hour: "1時間ごと"
federation: "フェデレーション"
notes: "投稿"
users: "ユーザー"
drive: "ドライブ"
network: "ネットワーク"
charts:
federation-instances: "インスタンスの増減"
federation-instances-total: "インスタンスの積算"
notes: "投稿の増減 (統合)"
local-notes: "投稿の増減 (ローカル)"
remote-notes: "投稿の増減 (リモート)"
notes-total: "投稿の積算"
users: "ユーザーの増減"
users-total: "ユーザーの積算"
drive: "ドライブ使用量の増減"
drive-total: "ドライブ使用量の積算"
drive-files: "ドライブのファイル数の増減"
drive-files-total: "ドライブのファイル数の積算"
network-requests: "リクエスト"
network-time: "応答時間"
network-usage: "通信量"
desktop/views/components/choose-file-from-drive-window.vue:
choose-file: "ファイル選択中"
upload: "PCからドライブにファイルをアップロード"
@ -1088,10 +1072,18 @@ admin/views/dashboard.vue:
instances: "インスタンス"
this-instance: "このインスタンス"
federated: "連合"
admin/views/instance.vue:
instance: "インスタンス"
instance-name: "インスタンス名"
instance-description: "インスタンスの紹介"
banner-url: "バナー画像URL"
max-note-text-length: "投稿の最大文字数"
disable-registration: "ユーザー登録の受付を停止する"
disable-local-timeline: "ローカルタイムラインを無効にする"
invite: "招待"
banner-url: "Banner URL"
disableRegistration: "Disable new user registration"
disableLocalTimeline: "Disable the local timeline"
save: "保存"
saved: "保存しました"
admin/views/charts.vue:
title: "チャート"
@ -1142,10 +1134,16 @@ admin/views/emoji.vue:
aliases-desc: "スペースで区切って複数設定できます。"
url: "絵文字画像URL"
add: "追加"
info: "50KB以下のPNG画像をおすすめします。"
added: "絵文字を登録しました"
emojis:
title: "絵文字一覧"
update: "更新"
remove: "削除"
updated: "更新しました"
remove-emoji:
are-you-sure: "「$1」を削除しますか"
removed: "削除しました"
admin/views/announcements.vue:
announcements: "お知らせ"
@ -1154,6 +1152,10 @@ admin/views/announcements.vue:
add: "追加"
title: "タイトル"
text: "内容"
saved: "保存しました"
_remove:
are-you-sure: "「$1」を削除しますか"
removed: "削除しました"
admin/views/hashtags.vue:
hided-tags: "Hidden Tags"
@ -1173,12 +1175,6 @@ desktop/views/pages/deck/deck.user-column.vue:
pinned-notes: "ピン留めされた投稿"
push-to-a-list: "リストに追加"
desktop/views/pages/stats/stats.vue:
all-users: "全てのユーザー"
original-users: "このインスタンスのユーザー"
all-notes: "全ての投稿"
original-notes: "このインスタンスの投稿"
desktop/views/pages/welcome.vue:
about: "詳しく..."
gotit: "わかった"
@ -1560,6 +1556,10 @@ mobile/views/pages/settings.vue:
twitter-connect: "Twitterアカウントに接続する"
twitter-reconnect: "再接続する"
twitter-disconnect: "切断する"
github: "GitHub連携"
github-connect: "GitHubアカウントに接続する"
github-reconnect: "再接続する"
github-disconnect: "切断する"
update: "Misskey Update"
version: "バージョン:"
latest-version: "最新のバージョン:"

View File

@ -186,7 +186,7 @@ common:
stack-left: "左に重ねんで!"
pop-right: "右に出すで!"
dev: "アプリの作成あかんかったわ。もっぺんやってみて。"
ai-chan-kawaii: "藍ちゃかわいい"
ai-chan-kawaii: "藍ちゃめっさべっぴんさんや"
auth/views/form.vue:
share-access: "<i>{{ app.name }}</i>があんさんのアカウントにアクセスすんのを<b>許可</b>してもええか?"
permission-ask: "このアプリは次の権限を要求してんで:"
@ -744,7 +744,7 @@ desktop/views/components/settings.vue:
apps: "アプリ"
mute-and-block: "ミュート/ブロック"
blocking: "ブロック"
security: "守護神セキュリティ"
security: "セキュリティ"
signin: "こんな感じでサインインしたらしいで"
password: "パスワード"
2fa: "二段階認証"
@ -873,15 +873,15 @@ common/views/components/mute-and-block.vue:
mute-and-block: "ミュートとブロック"
mute: "ミュート"
block: "ブロック"
no-muted-users: "ミュートしているユーザーはいません"
no-blocked-users: "ブロックしているユーザーはいません"
no-muted-users: "ミュートしるユーザーはおらんで"
no-blocked-users: "ブロックしるユーザーはおらんで"
common/views/components/password-settings.vue:
reset: "パスワードを変更する"
enter-current-password: "現在のパスワードを入力してください"
enter-new-password: "新しいパスワードを入力してください"
enter-new-password-again: "もう一度新しいパスワードを入力してください"
not-match: "新しいパスワードが一致しません"
changed: "パスワードを変更しました"
reset: "パスワード変える"
enter-current-password: "のパスワードを入れてや"
enter-new-password: "こんどのパスワード入れてや"
enter-new-password-again: "もっぺん入れてや"
not-match: "パスワードがおうとらん"
changed: "パスワード変えたわ"
desktop/views/components/sub-note-content.vue:
private: "この投稿は見せられへんわ"
deleted: "この投稿なんか無くなってもうたわ"
@ -953,7 +953,7 @@ admin/views/index.vue:
emoji: "カスタム絵文字"
users: "ユーザー"
update: "更新"
announcements: "お知らせ"
announcements: "知っといてや"
hashtags: "ハッシュタグ"
back-to-misskey: "Misskeyに戻る"
admin/views/dashboard.vue:
@ -962,9 +962,9 @@ admin/views/dashboard.vue:
notes: "投稿"
drive: "ドライブ"
instances: "インスタンス"
this-instance: "のインスタンス"
this-instance: "ワイのインスタンス"
federated: "連合"
invite: "招待"
invite: "来てや"
banner-url: "Banner URL"
disableRegistration: "Disable new user registration"
disableLocalTimeline: "Disable the local timeline"
@ -980,7 +980,7 @@ admin/views/charts.vue:
charts:
federation-instances: "インスタンスの増減"
federation-instances-total: "インスタンスの積算"
notes: "投稿の増減 (統合)"
notes: "投稿の増減(統合)"
local-notes: "投稿の増減 (ローカル)"
remote-notes: "投稿の増減 (リモート)"
notes-total: "投稿の積算"
@ -1387,7 +1387,7 @@ mobile/views/pages/user.vue:
mute: "ミュート"
unmute: "ミュート解除"
block: "ブロック"
unblock: "ブロック解除"
unblock: "ブロックやめたる"
mobile/views/pages/user/home.vue:
recent-notes: "最近儲かりまっか?"
images: "画像"

17362
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +1,8 @@
{
"name": "misskey",
"author": "syuilo <i@syuilo.com>",
"version": "10.38.1",
"clientVersion": "1.0.11482",
"version": "10.39.1",
"clientVersion": "1.0.11569",
"codename": "nighthike",
"main": "./built/index.js",
"private": true,
@ -20,10 +20,11 @@
"format": "gulp format"
},
"dependencies": {
"@fortawesome/fontawesome-svg-core": "1.2.6",
"@fortawesome/free-brands-svg-icons": "5.4.1",
"@fortawesome/free-regular-svg-icons": "5.4.1",
"@fortawesome/free-solid-svg-icons": "5.4.1",
"@fortawesome/fontawesome-svg-core": "1.2.8",
"@fortawesome/free-brands-svg-icons": "5.5.0",
"@fortawesome/free-regular-svg-icons": "5.5.0",
"@fortawesome/free-solid-svg-icons": "5.5.0",
"@fortawesome/vue-fontawesome": "0.1.2",
"@koa/cors": "2.2.2",
"@prezzemolo/rap": "0.1.2",
"@prezzemolo/zip": "0.0.3",
@ -62,6 +63,7 @@
"@types/mongodb": "3.1.12",
"@types/ms": "0.7.30",
"@types/node": "10.12.2",
"@types/oauth": "0.9.1",
"@types/portscanner": "2.1.0",
"@types/pug": "2.0.4",
"@types/qrcode": "1.3.0",
@ -95,7 +97,6 @@
"chai": "4.2.0",
"chai-http": "4.2.0",
"chalk": "2.4.1",
"chart.js": "2.7.3",
"commander": "2.19.0",
"crc-32": "1.2.0",
"css-loader": "1.0.1",
@ -211,12 +212,10 @@
"uuid": "3.3.2",
"v-animate-css": "0.0.2",
"vue": "2.5.17",
"vue-chartjs": "3.4.0",
"vue-color": "2.7.0",
"vue-content-loading": "1.5.3",
"vue-cropperjs": "2.2.2",
"vue-js-modal": "1.3.26",
"vue-json-tree-view": "2.1.4",
"vue-loader": "15.4.2",
"vue-router": "3.0.1",
"vue-style-loader": "4.1.2",

View File

@ -1,7 +1,7 @@
<template>
<div>
<div class="cdeuzmsthagexbkpofbmatmugjuvogfb">
<ui-card>
<div slot="title">%fa:broadcast-tower% %i18n:@announcements%</div>
<div slot="title"><fa icon="broadcast-tower"/> %i18n:@announcements%</div>
<section v-for="(announcement, i) in announcements" class="fit-top">
<ui-input v-model="announcement.title" @change="save">
<span>%i18n:@title%</span>
@ -9,13 +9,13 @@
<ui-textarea v-model="announcement.text">
<span>%i18n:@text%</span>
</ui-textarea>
<ui-button-group>
<ui-button inline @click="save">%fa:save R% %i18n:@save%</ui-button>
<ui-button inline @click="remove(i)">%fa:trash-alt R% %i18n:@remove%</ui-button>
</ui-button-group>
<ui-horizon-group>
<ui-button @click="save()">%fa:save R% %i18n:@save%</ui-button>
<ui-button @click="remove(i)">%fa:trash-alt R% %i18n:@remove%</ui-button>
</ui-horizon-group>
</section>
<section>
<ui-button @click="add">%fa:plus% %i18n:@add%</ui-button>
<ui-button @click="add"><fa icon="plus"/> %i18n:@add%</ui-button>
</section>
</ui-card>
</div>
@ -46,19 +46,45 @@ export default Vue.extend({
},
remove(i) {
this.announcements = this.announcements.filter((_, j) => j !== i);
this.save();
this.$swal({
type: 'warning',
text: '%i18n:@_remove.are-you-sure%'.replace('$1', this.announcements.find((_, j) => j == i).title),
showCancelButton: true
}).then(res => {
if (!res.value) return;
this.announcements = this.announcements.filter((_, j) => j !== i);
this.save(true);
this.$swal({
type: 'success',
text: '%i18n:@_remove.removed%'
});
});
},
save() {
save(silent) {
(this as any).api('admin/update-meta', {
broadcasts: this.announcements
}).then(() => {
(this as any).os.apis.dialog({ text: `Saved` });
if (!silent) {
this.$swal({
type: 'success',
text: '%i18n:@saved%'
});
}
}).catch(e => {
(this as any).os.apis.dialog({ text: `Failed ${e}` });
this.$swal({
type: 'error',
text: e
});
});
}
}
});
</script>
<style lang="stylus" scoped>
.cdeuzmsthagexbkpofbmatmugjuvogfb
@media (min-width 500px)
padding 16px
</style>

View File

@ -3,10 +3,10 @@
<table>
<thead>
<tr>
<th>%fa:exchange-alt% In/Out</th>
<th>%fa:server% Host</th>
<th>%fa:bolt% Activity</th>
<th>%fa:user% Actor</th>
<th><fa icon="exchange-alt"/> In/Out</th>
<th><fa icon="server"/> Host</th>
<th><fa icon="bolt"/> Activity</th>
<th><fa icon="user"/> Actor</th>
</tr>
</thead>
<tbody>
@ -63,11 +63,11 @@ export default Vue.extend({
<style lang="stylus" scoped>
.hyhctythnmwihguaaapnbrbszsjqxpio
display block
padding 16px
padding 12px 16px 16px 16px
height 250px
overflow auto
overflow hidden
box-shadow 0 2px 4px rgba(0, 0, 0, 0.1)
background var(--face)
background var(--adminDashboardCardBg)
border-radius 8px
> table
@ -76,10 +76,11 @@ export default Vue.extend({
overflow auto
border-spacing 0
border-collapse collapse
color #555
color var(--adminDashboardCardFg)
font-size 14px
thead
border-bottom solid 2px #eee
border-bottom solid 1px var(--adminDashboardCardDivider)
tr
th
@ -89,7 +90,7 @@ export default Vue.extend({
tbody
tr
&:nth-child(odd)
background #fbfbfb
background rgba(0, 0, 0, 0.025)
th, td
padding 8px 16px

View File

@ -1,7 +1,7 @@
<template>
<div class="qvgidhudpqhjttdhxubzuyrhyzgslujw">
<header>
<b>%fa:chart-bar R% %i18n:@title%:</b>
<b><fa :icon="['far', 'chart-bar']"/> %i18n:@title%:</b>
<select v-model="src">
<optgroup label="%i18n:@federation%">
<option value="federation-instances">%i18n:@charts.federation-instances%</option>
@ -39,6 +39,7 @@
<script lang="ts">
import Vue from 'vue';
import * as tinycolor from 'tinycolor2';
import * as ApexCharts from 'apexcharts';
const limit = 90;
@ -147,7 +148,7 @@ export default Vue.extend({
this.chartInstance.destroy();
}
this.chartInstance = new ApexCharts(this.$refs.chart, Object.assign({
this.chartInstance = new ApexCharts(this.$refs.chart, {
chart: {
type: 'area',
height: 300,
@ -168,17 +169,41 @@ export default Vue.extend({
},
grid: {
clipMarkers: false,
borderColor: 'rgba(0, 0, 0, 0.1)'
},
stroke: {
curve: 'straight',
width: 2
},
legend: {
labels: {
color: tinycolor(getComputedStyle(document.documentElement).getPropertyValue('--text')).toRgbString()
},
},
xaxis: {
type: 'datetime'
type: 'datetime',
labels: {
style: {
colors: tinycolor(getComputedStyle(document.documentElement).getPropertyValue('--text')).toRgbString()
}
},
axisBorder: {
color: 'rgba(0, 0, 0, 0.1)'
},
axisTicks: {
color: 'rgba(0, 0, 0, 0.1)'
},
},
yaxis: {
}
}, this.data));
labels: {
formatter: this.data.bytes ? v => Vue.filter('bytes')(v, 0) : v => Vue.filter('number')(v),
style: {
color: tinycolor(getComputedStyle(document.documentElement).getPropertyValue('--text')).toRgbString()
}
}
},
series: this.data.series
});
this.chartInstance.render();
},
@ -286,6 +311,7 @@ export default Vue.extend({
driveChart(): any {
return {
bytes: true,
series: [{
name: 'All',
data: this.format(
@ -314,6 +340,7 @@ export default Vue.extend({
driveTotalChart(): any {
return {
bytes: true,
series: [{
name: 'Combined',
data: this.format(sum(this.stats.drive.local.totalSize, this.stats.drive.remote.totalSize))
@ -396,6 +423,7 @@ export default Vue.extend({
networkUsageChart(): any {
return {
bytes: true,
series: [{
name: 'Incoming',
data: this.format(this.stats.network.incomingBytes)
@ -424,8 +452,8 @@ export default Vue.extend({
margin 0 8px
padding 0 0 8px 0
font-size 1em
color #555
border-bottom solid 1px #eee
color var(--adminDashboardCardFg)
border-bottom solid 1px var(--adminDashboardCardDivider)
> b
margin-right 8px

View File

@ -2,14 +2,14 @@
<div class="zyknedwtlthezamcjlolyusmipqmjgxz">
<div>
<header>
<span>%fa:microchip% CPU <span>{{ cpuP }}%</span></span>
<span><fa icon="microchip"/> CPU <span>{{ cpuP }}%</span></span>
<span v-if="meta">{{ meta.cpu.model }}</span>
</header>
<div ref="cpu"></div>
</div>
<div>
<header>
<span>%fa:memory% MEM <span>{{ memP }}%</span></span>
<span><fa icon="memory"/> MEM <span>{{ memP }}%</span></span>
<span v-if="meta"></span>
</header>
<div ref="mem"></div>
@ -79,6 +79,7 @@ export default Vue.extend({
},
grid: {
clipMarkers: false,
borderColor: 'rgba(0, 0, 0, 0.1)'
},
stroke: {
curve: 'straight',
@ -153,7 +154,7 @@ export default Vue.extend({
display flex
padding 0 8px
margin-bottom -16px
color #555
color var(--adminDashboardCardFg)
font-size 14px
> span
@ -167,4 +168,13 @@ export default Vue.extend({
> div
margin-bottom -10px
@media (max-width 1000px)
display block
margin-bottom 26px
> div
&:first-child
margin-right 0
margin-bottom 26px
</style>

View File

@ -11,54 +11,54 @@
<div v-if="stats" class="stats">
<div>
<div>
<div>%fa:user%</div>
<div><fa icon="user"/></div>
<div>
<span>%i18n:@accounts%</span>
<b class="primary">{{ stats.originalUsersCount | number }}</b>
</div>
</div>
<div>
<span>%fa:home% %i18n:@this-instance%</span>
<span @click="setChartSrc('users')">%fa:chart-bar R%</span>
<span><fa icon="home"/> %i18n:@this-instance%</span>
<span @click="setChartSrc('users')"><fa :icon="['far', 'chart-bar']"/></span>
</div>
</div>
<div>
<div>
<div>%fa:pencil-alt%</div>
<div><fa icon="pencil-alt"/></div>
<div>
<span>%i18n:@notes%</span>
<b class="primary">{{ stats.originalNotesCount | number }}</b>
</div>
</div>
<div>
<span>%fa:home% %i18n:@this-instance%</span>
<span @click="setChartSrc('notes')">%fa:chart-bar R%</span>
<span><fa icon="home"/> %i18n:@this-instance%</span>
<span @click="setChartSrc('notes')"><fa :icon="['far', 'chart-bar']"/></span>
</div>
</div>
<div>
<div>
<div>%fa:database%</div>
<div><fa icon="database"/></div>
<div>
<span>%i18n:@drive%</span>
<b>{{ stats.driveUsageLocal | bytes }}</b>
</div>
</div>
<div>
<span>%fa:home% %i18n:@this-instance%</span>
<span @click="setChartSrc('drive')">%fa:chart-bar R%</span>
<span><fa icon="home"/> %i18n:@this-instance%</span>
<span @click="setChartSrc('drive')"><fa :icon="['far', 'chart-bar']"/></span>
</div>
</div>
<div>
<div>
<div>%fa:hdd R%</div>
<div><fa :icon="['far', 'hdd']"/></div>
<div>
<span>%i18n:@instances%</span>
<b>{{ stats.instances | number }}</b>
</div>
</div>
<div>
<span>%fa:globe% %i18n:@federated%</span>
<span @click="setChartSrc('federation-instances-total')">%fa:chart-bar R%</span>
<span><fa icon="globe"/> %i18n:@federated%</span>
<span @click="setChartSrc('federation-instances-total')"><fa :icon="['far', 'chart-bar']"/></span>
</div>
</div>
</div>
@ -124,17 +124,28 @@ export default Vue.extend({
<style lang="stylus" scoped>
.obdskegsannmntldydackcpzezagxqfy
padding 16px
@media (min-width 500px)
padding 32px
> header
display flex
margin-bottom 16px
padding-bottom 16px
border-bottom solid 1px #ccc
color #777
border-bottom solid 1px var(--adminDashboardHeaderBorder)
color var(--adminDashboardHeaderFg)
font-size 14px
white-space nowrap
@media (max-width 1000px)
display none
> p
display inline
display block
margin 0 32px 0 0
overflow hidden
text-overflow ellipsis
> b
&:after
@ -152,11 +163,10 @@ export default Vue.extend({
> div
flex 1
max-width 300px
margin-right 16px
color var(--text)
color var(--adminDashboardCardFg)
box-shadow 0 2px 4px rgba(0, 0, 0, 0.1)
background var(--face)
background var(--adminDashboardCardBg)
border-radius 8px
&:last-child
@ -192,7 +202,7 @@ export default Vue.extend({
> div:last-child
display flex
padding 6px 16px
border-top solid 1px #eee
border-top solid 1px var(--adminDashboardCardDivider)
> span
font-size 70%
@ -202,6 +212,21 @@ export default Vue.extend({
margin-left auto
cursor pointer
@media (max-width 900px)
display grid
grid-template-columns 1fr 1fr
grid-template-rows 1fr 1fr
gap 16px
> div
margin-right 0
@media (max-width 500px)
display block
> div:not(:last-child)
margin-bottom 16px
> .charts
margin-bottom 16px

View File

@ -1,42 +1,45 @@
<template>
<div>
<div class="tumhkfkmgtvzljezfvmgkeurkfncshbe">
<ui-card>
<div slot="title">%fa:plus% %i18n:@add-emoji.title%</div>
<div slot="title"><fa icon="plus"/> %i18n:@add-emoji.title%</div>
<section class="fit-top">
<ui-input v-model="name">
<span>%i18n:@add-emoji.name%</span>
<span slot="text">%i18n:@add-emoji.name-desc%</span>
</ui-input>
<ui-input v-model="aliases">
<span>%i18n:@add-emoji.aliases%</span>
<span slot="text">%i18n:@add-emoji.aliases-desc%</span>
</ui-input>
<ui-horizon-group inputs>
<ui-input v-model="name">
<span>%i18n:@add-emoji.name%</span>
<span slot="text">%i18n:@add-emoji.name-desc%</span>
</ui-input>
<ui-input v-model="aliases">
<span>%i18n:@add-emoji.aliases%</span>
<span slot="text">%i18n:@add-emoji.aliases-desc%</span>
</ui-input>
</ui-horizon-group>
<ui-input v-model="url">
<span>%i18n:@add-emoji.url%</span>
</ui-input>
<ui-info>%i18n:@add-emoji.info%</ui-info>
<ui-button @click="add">%i18n:@add-emoji.add%</ui-button>
</section>
</ui-card>
<ui-card>
<div slot="title">%fa:grin R% %i18n:@emojis.title%</div>
<div slot="title"><fa :icon="['far', 'grin']"/> %i18n:@emojis.title%</div>
<section v-for="emoji in emojis">
<img :src="emoji.url" :alt="emoji.name" style="width: 64px;"/>
<ui-input v-model="emoji.name">
<span>%i18n:@add-emoji.name%</span>
<span slot="text">%i18n:@add-emoji.name-desc%</span>
</ui-input>
<ui-input v-model="emoji.aliases">
<span>%i18n:@add-emoji.aliases%</span>
<span slot="text">%i18n:@add-emoji.aliases-desc%</span>
</ui-input>
<ui-horizon-group inputs>
<ui-input v-model="emoji.name">
<span>%i18n:@add-emoji.name%</span>
</ui-input>
<ui-input v-model="emoji.aliases">
<span>%i18n:@add-emoji.aliases%</span>
</ui-input>
</ui-horizon-group>
<ui-input v-model="emoji.url">
<span>%i18n:@add-emoji.url%</span>
</ui-input>
<ui-button-group>
<ui-button inline @click="updateEmoji(emoji)">%fa:save R% %i18n:@emojis.update%</ui-button>
<ui-button inline @click="removeEmoji(emoji)">%fa:trash-alt R% %i18n:@emojis.remove%</ui-button>
</ui-button-group>
<ui-horizon-group>
<ui-button @click="updateEmoji(emoji)"><fa :icon="['far', 'save']"/> %i18n:@emojis.update%</ui-button>
<ui-button @click="removeEmoji(emoji)"><fa :icon="['far', 'trash-alt']"/> %i18n:@emojis.remove%</ui-button>
</ui-horizon-group>
</section>
</ui-card>
</div>
@ -64,17 +67,24 @@ export default Vue.extend({
(this as any).api('admin/emoji/add', {
name: this.name,
url: this.url,
aliases: this.aliases.split(' ')
aliases: this.aliases.split(' ').filter(x => x.length > 0)
}).then(() => {
(this as any).os.apis.dialog({ text: `Added` });
this.$swal({
type: 'success',
text: '%i18n:@add-emoji.added%'
});
this.fetchEmojis();
}).catch(e => {
(this as any).os.apis.dialog({ text: `Failed ${e}` });
this.$swal({
type: 'error',
text: e
});
});
},
fetchEmojis() {
(this as any).api('admin/emoji/list').then(emojis => {
emojis.reverse();
emojis.forEach(e => e.aliases = (e.aliases || []).join(' '));
this.emojis = emojis;
});
@ -85,24 +95,51 @@ export default Vue.extend({
id: emoji.id,
name: emoji.name,
url: emoji.url,
aliases: emoji.aliases.split(' ')
aliases: emoji.aliases.split(' ').filter(x => x.length > 0)
}).then(() => {
(this as any).os.apis.dialog({ text: `Updated` });
this.$swal({
type: 'success',
text: '%i18n:@updated%'
});
}).catch(e => {
(this as any).os.apis.dialog({ text: `Failed ${e}` });
this.$swal({
type: 'error',
text: e
});
});
},
removeEmoji(emoji) {
(this as any).api('admin/emoji/remove', {
id: emoji.id
}).then(() => {
(this as any).os.apis.dialog({ text: `Removed` });
this.fetchEmojis();
}).catch(e => {
(this as any).os.apis.dialog({ text: `Failed ${e}` });
this.$swal({
type: 'warning',
text: '%i18n:@remove-emoji.are-you-sure%'.replace('$1', emoji.name),
showCancelButton: true
}).then(res => {
if (!res.value) return;
(this as any).api('admin/emoji/remove', {
id: emoji.id
}).then(() => {
this.$swal({
type: 'success',
text: '%i18n:@remove-emoji.removed%'
});
this.fetchEmojis();
}).catch(e => {
this.$swal({
type: 'error',
text: e
});
});
});
}
}
});
</script>
<style lang="stylus" scoped>
.tumhkfkmgtvzljezfvmgkeurkfncshbe
@media (min-width 500px)
padding 16px
</style>

View File

@ -29,9 +29,9 @@ export default Vue.extend({
(this as any).api('admin/update-meta', {
hidedTags: this.hidedTags.split('\n')
}).then(() => {
(this as any).os.apis.dialog({ text: `Saved` });
//(this as any).os.apis.dialog({ text: `Saved` });
}).catch(e => {
(this as any).os.apis.dialog({ text: `Failed ${e}` });
//(this as any).os.apis.dialog({ text: `Failed ${e}` });
});
}
}

View File

@ -1,6 +1,15 @@
<template>
<div class="mk-admin">
<nav>
<div class="mk-admin" :class="{ isMobile }">
<header v-show="isMobile">
<button class="nav" @click="navOpend = true"><fa icon="bars"/></button>
<span>MisskeyMyAdmin</span>
</header>
<div class="nav-backdrop"
v-if="navOpend && isMobile"
@click="navOpend = false"
@touchstart="navOpend = false"
></div>
<nav v-show="navOpend">
<div class="mi">
<img svg-inline src="../assets/header-icon.svg"/>
</div>
@ -9,18 +18,18 @@
<p class="name">{{ $store.state.i | userName }}</p>
</div>
<ul>
<li @click="nav('dashboard')" :class="{ active: page == 'dashboard' }">%fa:home .fw%%i18n:@dashboard%</li>
<li @click="nav('instance')" :class="{ active: page == 'instance' }">%fa:cog .fw%%i18n:@instance%</li>
<li @click="nav('users')" :class="{ active: page == 'users' }">%fa:users .fw%%i18n:@users%</li>
<li @click="nav('emoji')" :class="{ active: page == 'emoji' }">%fa:grin R .fw%%i18n:@emoji%</li>
<li @click="nav('announcements')" :class="{ active: page == 'announcements' }">%fa:broadcast-tower .fw%%i18n:@announcements%</li>
<li @click="nav('hashtags')" :class="{ active: page == 'hashtags' }">%fa:hashtag .fw%%i18n:@hashtags%</li>
<li @click="nav('dashboard')" :class="{ active: page == 'dashboard' }"><fa icon="home" fixed-width/>%i18n:@dashboard%</li>
<li @click="nav('instance')" :class="{ active: page == 'instance' }"><fa icon="cog" fixed-width/>%i18n:@instance%</li>
<li @click="nav('users')" :class="{ active: page == 'users' }"><fa icon="users" fixed-width/>%i18n:@users%</li>
<li @click="nav('emoji')" :class="{ active: page == 'emoji' }"><fa icon="grin R" fixed-width/>%i18n:@emoji%</li>
<li @click="nav('announcements')" :class="{ active: page == 'announcements' }"><fa icon="broadcast-tower" fixed-width/>%i18n:@announcements%</li>
<li @click="nav('hashtags')" :class="{ active: page == 'hashtags' }"><fa icon="hashtag" fixed-width/>%i18n:@hashtags%</li>
<!-- <li @click="nav('drive')" :class="{ active: page == 'drive' }">%fa:cloud .fw%%i18n:common.drive%</li> -->
<!-- <li @click="nav('drive')" :class="{ active: page == 'drive' }"><fa icon="cloud" fixed-width/>%i18n:common.drive%</li> -->
<!-- <li @click="nav('update')" :class="{ active: page == 'update' }">%i18n:@update%</li> -->
</ul>
<div class="back-to-misskey">
<a href="/">%fa:arrow-left% %i18n:@back-to-misskey%</a>
<a href="/"><fa icon="arrow-left"/> %i18n:@back-to-misskey%</a>
</div>
<div class="version">
<small>Misskey {{ version }}</small>
@ -49,6 +58,10 @@ import XAnnouncements from "./announcements.vue";
import XHashtags from "./hashtags.vue";
import XUsers from "./users.vue";
// Detect the user agent
const ua = navigator.userAgent.toLowerCase();
const isMobile = /mobile|iphone|ipad|android/.test(ua);
export default Vue.extend({
components: {
XDashboard,
@ -58,10 +71,15 @@ export default Vue.extend({
XHashtags,
XUsers
},
provide: {
isMobile
},
data() {
return {
page: 'dashboard',
version
version,
isMobile,
navOpend: !isMobile
};
},
methods: {
@ -74,12 +92,46 @@ export default Vue.extend({
<style lang="stylus">
.mk-admin
$headerHeight = 48px
display flex
height 100%
> header
position fixed
top 0
z-index 10000
width 100%
color var(--mobileHeaderFg)
background-color var(--mobileHeaderBg)
box-shadow 0 1px 0 rgba(#000, 0.075)
&, *
user-select none
> span
display block
line-height $headerHeight
text-align center
> .nav
display block
position absolute
top 0
left 0
z-index 10001
padding 0
width $headerHeight
font-size 1.4em
line-height $headerHeight
border-right solid 1px rgba(#000, 0.1)
> [data-icon]
transition all 0.2s ease
> nav
position fixed
z-index 10000
z-index 20001
top 0
left 0
width 250px
@ -136,7 +188,7 @@ export default Vue.extend({
&:hover
color #fff
> [data-fa]
> [data-icon]
margin-right 6px
> .version
@ -166,7 +218,7 @@ export default Vue.extend({
&:hover
color #fff
> [data-fa]
> [data-icon]
margin-right 6px
&.active
@ -187,9 +239,22 @@ export default Vue.extend({
border-bottom solid 16px transparent
border-left solid 16px transparent
> .nav-backdrop
position fixed
top 0
left 0
z-index 20000
width 100%
height 100%
background var(--mobileNavBackdrop)
> main
width 100%
padding 32px 32px 32px calc(32px + 250px)
padding 0 0 0 250px
max-width 1300px
&.isMobile
> main
padding $headerHeight 0 0 0
</style>

View File

@ -1,9 +1,12 @@
<template>
<div>
<div class="axbwjelsbymowqjyywpirzhdlszoncqs">
<ui-card>
<div slot="title">%i18n:@banner-url%</div>
<div slot="title">%fa:cog% %i18n:@instance%</div>
<section class="fit-top">
<ui-input v-model="bannerUrl"/>
<ui-input v-model="name">%i18n:@instance-name%</ui-input>
<ui-textarea v-model="description">%i18n:@instance-description%</ui-textarea>
<ui-input v-model="bannerUrl">%i18n:@banner-url%</ui-input>
<ui-input v-model="maxNoteTextLength">%i18n:@max-note-text-length%</ui-input>
<ui-button @click="updateMeta">%i18n:@save%</ui-button>
</section>
</ui-card>
@ -35,28 +38,61 @@ export default Vue.extend({
disableRegistration: false,
disableLocalTimeline: false,
bannerUrl: null,
name: null,
description: null,
maxNoteTextLength: null,
inviteCode: null,
};
},
created() {
(this as any).os.getMeta().then(meta => {
this.bannerUrl = meta.bannerUrl;
this.name = meta.name;
this.description = meta.description;
this.maxNoteTextLength = meta.maxNoteTextLength;
});
},
methods: {
invite() {
(this as any).api('admin/invite').then(x => {
this.inviteCode = x.code;
}).catch(e => {
(this as any).os.apis.dialog({ text: `Failed ${e}` });
this.$swal({
type: 'error',
text: e
});
});
},
updateMeta() {
(this as any).api('admin/update-meta', {
disableRegistration: this.disableRegistration,
disableLocalTimeline: this.disableLocalTimeline,
bannerUrl: this.bannerUrl
bannerUrl: this.bannerUrl,
name: this.name,
description: this.description,
maxNoteTextLength: parseInt(this.maxNoteTextLength, 10)
}).then(() => {
(this as any).os.apis.dialog({ text: `Saved` });
this.$swal({
type: 'success',
text: '%i18n:@saved%'
});
}).catch(e => {
(this as any).os.apis.dialog({ text: `Failed ${e}` });
this.$swal({
type: 'error',
text: e
});
});
}
}
});
</script>
<style lang="stylus" scoped>
.axbwjelsbymowqjyywpirzhdlszoncqs
@media (min-width 500px)
padding 16px
</style>

View File

@ -1,5 +1,5 @@
<template>
<div>
<div class="ucnffhbtogqgscfmqcymwmmupoknpfsw">
<ui-card>
<div slot="title">%i18n:@verify-user%</div>
<section class="fit-top">
@ -67,11 +67,11 @@ export default Vue.extend({
const process = async () => {
const user = await (this as any).os.api('users/show', parseAcct(this.verifyUsername));
await (this as any).os.api('admin/verify-user', { userId: user.id });
(this as any).os.apis.dialog({ text: '%i18n:@verified%' });
//(this as any).os.apis.dialog({ text: '%i18n:@verified%' });
};
await process().catch(e => {
(this as any).os.apis.dialog({ text: `Failed: ${e}` });
//(this as any).os.apis.dialog({ text: `Failed: ${e}` });
});
this.verifying = false;
@ -83,11 +83,11 @@ export default Vue.extend({
const process = async () => {
const user = await (this as any).os.api('users/show', parseAcct(this.unverifyUsername));
await (this as any).os.api('admin/unverify-user', { userId: user.id });
(this as any).os.apis.dialog({ text: '%i18n:@unverified%' });
//(this as any).os.apis.dialog({ text: '%i18n:@unverified%' });
};
await process().catch(e => {
(this as any).os.apis.dialog({ text: `Failed: ${e}` });
//(this as any).os.apis.dialog({ text: `Failed: ${e}` });
});
this.unverifying = false;
@ -99,11 +99,11 @@ export default Vue.extend({
const process = async () => {
const user = await (this as any).os.api('users/show', parseAcct(this.suspendUsername));
await (this as any).os.api('admin/suspend-user', { userId: user.id });
(this as any).os.apis.dialog({ text: '%i18n:@suspended%' });
//(this as any).os.apis.dialog({ text: '%i18n:@suspended%' });
};
await process().catch(e => {
(this as any).os.apis.dialog({ text: `Failed: ${e}` });
//(this as any).os.apis.dialog({ text: `Failed: ${e}` });
});
this.suspending = false;
@ -115,11 +115,11 @@ export default Vue.extend({
const process = async () => {
const user = await (this as any).os.api('users/show', parseAcct(this.unsuspendUsername));
await (this as any).os.api('admin/unsuspend-user', { userId: user.id });
(this as any).os.apis.dialog({ text: '%i18n:@unsuspended%' });
//(this as any).os.apis.dialog({ text: '%i18n:@unsuspended%' });
};
await process().catch(e => {
(this as any).os.apis.dialog({ text: `Failed: ${e}` });
//(this as any).os.apis.dialog({ text: `Failed: ${e}` });
});
this.unsuspending = false;
@ -127,3 +127,10 @@ export default Vue.extend({
}
});
</script>
<style lang="stylus" scoped>
.ucnffhbtogqgscfmqcymwmmupoknpfsw
@media (min-width 500px)
padding 16px
</style>

View File

@ -128,7 +128,7 @@ pre
overflow auto
tab-size 2
[data-fa]
[data-icon]
display inline-block
.swal2-container

View File

@ -1,6 +1,6 @@
<template>
<ui-card>
<div slot="title">%fa:key% API</div>
<div slot="title"><fa icon="key"/> API</div>
<section class="fit-top">
<ui-input :value="$store.state.i.token" readonly>
@ -9,11 +9,11 @@
<p>%i18n:@intro%</p>
<ui-info warn>%i18n:@caution%</ui-info>
<p>%i18n:@regeneration-of-token%</p>
<ui-button @click="regenerateToken">%fa:sync-alt% %i18n:@regenerate-token%</ui-button>
<ui-button @click="regenerateToken"><fa icon="sync-alt"/> %i18n:@regenerate-token%</ui-button>
</section>
<section>
<header>%fa:terminal% %i18n:@console.title%</header>
<header><fa icon="terminal"/> %i18n:@console.title%</header>
<ui-input v-model="endpoint">
<span>%i18n:@console.endpoint%</span>
</ui-input>
@ -22,7 +22,7 @@
</ui-textarea>
<ui-button @click="send" :disabled="sending">
<template v-if="sending">%i18n:@console.sending%</template>
<template v-else>%fa:paper-plane% %i18n:@console.send%</template>
<template v-else><fa icon="paper-plane"/> %i18n:@console.send%</template>
</ui-button>
<ui-textarea v-if="res" v-model="res" readonly tall>
<span>%i18n:@console.response%</span>

View File

@ -14,7 +14,8 @@
</ol>
<ol class="emojis" ref="suggests" v-if="emojis.length > 0">
<li v-for="emoji in emojis" @click="complete(type, emoji.emoji)" @keydown="onKeydown" tabindex="-1">
<span class="emoji" v-if="emoji.url"><img :src="emoji.url" :alt="emoji.emoji"/></span>
<span class="emoji" v-if="emoji.isCustomEmoji"><img :src="emoji.url" :alt="emoji.emoji"/></span>
<span class="emoji" v-else-if="!useOsDefaultEmojis"><img :src="emoji.url" :alt="emoji.emoji"/></span>
<span class="emoji" v-else>{{ emoji.emoji }}</span>
<span class="name" v-html="emoji.name.replace(q, `<b>${q}</b>`)"></span>
<span class="alias" v-if="emoji.aliasOf">({{ emoji.aliasOf }})</span>
@ -33,6 +34,7 @@ type EmojiDef = {
name: string;
aliasOf?: string;
url?: string;
isCustomEmoji?: boolean;
};
const lib = Object.entries(emojilib.lib).filter((x: any) => {
@ -42,7 +44,8 @@ const lib = Object.entries(emojilib.lib).filter((x: any) => {
const emjdb: EmojiDef[] = lib.map((x: any) => ({
emoji: x[1].char,
name: x[0],
aliasOf: null
aliasOf: null,
url: `https://twemoji.maxcdn.com/2/svg/${x[1].char.codePointAt(0).toString(16)}.svg`
}));
lib.forEach((x: any) => {
@ -51,7 +54,8 @@ lib.forEach((x: any) => {
emjdb.push({
emoji: x[1].char,
name: k,
aliasOf: x[0]
aliasOf: x[0],
url: `https://twemoji.maxcdn.com/2/svg/${x[1].char.codePointAt(0).toString(16)}.svg`
});
});
}
@ -77,6 +81,10 @@ export default Vue.extend({
computed: {
items(): HTMLCollection {
return (this.$refs.suggests as Element).children;
},
useOsDefaultEmojis(): boolean {
return this.$store.state.device.useOsDefaultEmojis;
}
},
@ -107,7 +115,8 @@ export default Vue.extend({
emojiDefinitions.push({
name: x.name,
emoji: `:${x.name}:`,
url: x.url
url: x.url,
isCustomEmoji: true
});
if (x.aliases) {
@ -116,7 +125,8 @@ export default Vue.extend({
name: alias,
aliasOf: x.name,
emoji: `:${x.name}:`,
url: x.url
url: x.url,
isCustomEmoji: true
});
});
}

View File

@ -1,35 +1,35 @@
<template>
<div class="troubleshooter">
<div class="body">
<h1>%fa:wrench%%i18n:@title%</h1>
<h1><fa icon="wrench"/>%i18n:@title%</h1>
<div>
<p :data-wip="network == null">
<template v-if="network != null">
<template v-if="network">%fa:check%</template>
<template v-if="!network">%fa:times%</template>
<template v-if="network"><fa icon="check"/></template>
<template v-if="!network"><fa icon="times"/></template>
</template>
{{ network == null ? '%i18n:@checking-network%' : '%i18n:@network%' }}<mk-ellipsis v-if="network == null"/>
</p>
<p v-if="network == true" :data-wip="internet == null">
<template v-if="internet != null">
<template v-if="internet">%fa:check%</template>
<template v-if="!internet">%fa:times%</template>
<template v-if="internet"><fa icon="check"/></template>
<template v-if="!internet"><fa icon="times"/></template>
</template>
{{ internet == null ? '%i18n:@checking-internet%' : '%i18n:@internet%' }}<mk-ellipsis v-if="internet == null"/>
</p>
<p v-if="internet == true" :data-wip="server == null">
<template v-if="server != null">
<template v-if="server">%fa:check%</template>
<template v-if="!server">%fa:times%</template>
<template v-if="server"><fa icon="check"/></template>
<template v-if="!server"><fa icon="times"/></template>
</template>
{{ server == null ? '%i18n:@checking-server%' : '%i18n:@server%' }}<mk-ellipsis v-if="server == null"/>
</p>
</div>
<p v-if="!end">%i18n:@finding%<mk-ellipsis/></p>
<p v-if="network === false"><b>%fa:exclamation-triangle%%i18n:@no-network%</b><br>%i18n:@no-network-desc%</p>
<p v-if="internet === false"><b>%fa:exclamation-triangle%%i18n:@no-internet%</b><br>%i18n:@no-internet-desc%</p>
<p v-if="server === false"><b>%fa:exclamation-triangle%%i18n:@no-server%</b><br>%i18n:@no-server-desc%</p>
<p v-if="server === true" class="success"><b>%fa:info-circle%%i18n:@success%</b><br>%i18n:@success-desc%</p>
<p v-if="network === false"><b><fa icon="exclamation-triangle"/>%i18n:@no-network%</b><br>%i18n:@no-network-desc%</p>
<p v-if="internet === false"><b><fa icon="exclamation-triangle"/>%i18n:@no-internet%</b><br>%i18n:@no-internet-desc%</p>
<p v-if="server === false"><b><fa icon="exclamation-triangle"/>%i18n:@no-server%</b><br>%i18n:@no-server-desc%</p>
<p v-if="server === true" class="success"><b><fa icon="info-circle"/>%i18n:@success%</b><br>%i18n:@success-desc%</p>
</div>
<footer>
<a href="/assets/flush.html">%i18n:@flush%</a> | <a href="/assets/version.html">%i18n:@set-version%</a>
@ -100,7 +100,7 @@ export default Vue.extend({
color #444
border-bottom solid 1px #eee
> [data-fa]
> [data-icon]
margin-right 0.25em
> div
@ -115,7 +115,7 @@ export default Vue.extend({
&[data-wip]
color #888
> [data-fa]
> [data-icon]
margin-right 0.25em
&.times
@ -132,7 +132,7 @@ export default Vue.extend({
border-top solid 1px #eee
> b
> [data-fa]
> [data-icon]
margin-right 0.25em
&.success

View File

@ -1,6 +1,6 @@
<template>
<div class="mk-connect-failed">
<img src="data:image/jpeg;base64,%base64:/assets/error.jpg%" alt=""/>
<img src="https://raw.githubusercontent.com/syuilo/misskey/develop/src/client/assets/error.jpg" alt=""/>
<h1>%i18n:@title%</h1>
<p class="text">
<span>{{ '%i18n:@description%'.substr(0, '%i18n:@description%'.indexOf('{')) }}</span>

View File

@ -1,6 +1,6 @@
<template>
<ui-card>
<div slot="title">%fa:cloud% %i18n:common.drive%</div>
<div slot="title"><fa icon="cloud"/> %i18n:common.drive%</div>
<section v-if="!fetching" class="juakhbxthdewydyreaphkepoxgxvfogn">
<div class="meter"><div :style="meterStyle"></div></div>

View File

@ -0,0 +1,82 @@
<template>
<img v-if="customEmoji" class="fvgwvorwhxigeolkkrcderjzcawqrscl custom" :src="url" :alt="alt" :title="alt"/>
<img v-else-if="char && !useOsDefaultEmojis" class="fvgwvorwhxigeolkkrcderjzcawqrscl" :src="url" :alt="alt" :title="alt"/>
<span v-else-if="char && useOsDefaultEmojis">{{ char }}</span>
<span v-else>:{{ name }}:</span>
</template>
<script lang="ts">
import Vue from 'vue';
import { lib } from 'emojilib';
export default Vue.extend({
props: {
name: {
type: String,
required: false
},
emoji: {
type: String,
required: false
},
customEmojis: {
required: false,
default: []
}
},
data() {
return {
url: null,
char: null,
customEmoji: null
}
},
computed: {
alt(): string {
return this.customEmoji ? `:${this.customEmoji.name}:` : this.char;
},
useOsDefaultEmojis(): boolean {
return this.$store.state.device.useOsDefaultEmojis;
}
},
created() {
if (this.name) {
const customEmoji = this.customEmojis.find(x => x.name == this.name);
if (customEmoji) {
this.customEmoji = customEmoji;
this.url = customEmoji.url;
} else {
const emoji = lib[this.name];
if (emoji) {
this.char = emoji.char;
}
}
} else {
this.char = this.emoji;
}
if (this.char) {
this.url = `https://twemoji.maxcdn.com/2/svg/${this.char.codePointAt(0).toString(16)}.svg`;
}
}
});
</script>
<style lang="stylus" scoped>
.fvgwvorwhxigeolkkrcderjzcawqrscl
height 1.25em
vertical-align -0.25em
&.custom
height 2.5em
vertical-align middle
transition transform 0.2s ease
&:hover
transform scale(1.2)
</style>

View File

@ -1,6 +1,6 @@
<template>
<div class="wjqjnyhzogztorhrdgcpqlkxhkmuetgj">
<p>%fa:exclamation-triangle% %i18n:common.error.title%</p>
<p><fa icon="exclamation-triangle"/> %i18n:common.error.title%</p>
<ui-button @click="() => $emit('retry')">%i18n:common.error.retry%</ui-button>
</div>
</template>

View File

@ -1,6 +1,6 @@
<template>
<span class="mk-file-type-icon">
<template v-if="kind == 'image'">%fa:file-image%</template>
<template v-if="kind == 'image'"><fa icon="file-image"/></template>
</span>
</template>

View File

@ -1,6 +1,6 @@
<template>
<div class="xqnhankfuuilcwvhgsopeqncafzsquya">
<button class="go-index" v-if="selfNav" @click="goIndex">%fa:arrow-left%</button>
<button class="go-index" v-if="selfNav" @click="goIndex"><fa icon="arrow-left"/></button>
<header><b><router-link :to="blackUser | userPage">{{ blackUser | userName }}</router-link></b>(%i18n:common.reversi.black%) vs <b><router-link :to="whiteUser | userPage">{{ whiteUser | userName }}</router-link></b>(%i18n:common.reversi.white%)</header>
<div style="overflow: hidden; line-height: 28px;">
@ -51,13 +51,13 @@
<div class="player" v-if="game.isEnded">
<div>
<button @click="logPos = 0" :disabled="logPos == 0">%fa:angle-double-left%</button>
<button @click="logPos--" :disabled="logPos == 0">%fa:angle-left%</button>
<button @click="logPos = 0" :disabled="logPos == 0"><fa icon="angle-double-left"/></button>
<button @click="logPos--" :disabled="logPos == 0"><fa icon="angle-left"/></button>
</div>
<span>{{ logPos }} / {{ logs.length }}</span>
<div>
<button @click="logPos++" :disabled="logPos == logs.length">%fa:angle-right%</button>
<button @click="logPos = logs.length" :disabled="logPos == logs.length">%fa:angle-double-right%</button>
<button @click="logPos++" :disabled="logPos == logs.length"><fa icon="angle-right"/></button>
<button @click="logPos = logs.length" :disabled="logPos == logs.length"><fa icon="angle-double-right"/></button>
</div>
</div>

View File

@ -17,13 +17,13 @@
</header>
<div>
<div class="random" v-if="game.settings.map == null">%fa:dice%</div>
<div class="random" v-if="game.settings.map == null"><fa icon="dice"/></div>
<div class="board" v-else :style="{ 'grid-template-rows': `repeat(${ game.settings.map.length }, 1fr)`, 'grid-template-columns': `repeat(${ game.settings.map[0].length }, 1fr)` }">
<div v-for="(x, i) in game.settings.map.join('')"
:data-none="x == ' '"
@click="onPixelClick(i, x)">
<template v-if="x == 'b'"><template v-if="$store.state.device.darkmode">%fa:circle R%</template><template v-else>%fa:circle%</template></template>
<template v-if="x == 'w'"><template v-if="$store.state.device.darkmode">%fa:circle%</template><template v-else>%fa:circle R%</template></template>
<template v-if="x == 'b'"><template v-if="$store.state.device.darkmode"><fa :icon="['far', 'circle']"/></template><template v-else><fa icon="circle"/></template></template>
<template v-if="x == 'w'"><template v-if="$store.state.device.darkmode"><fa :icon="['far', 'circle']"/></template><template v-else><fa icon="circle"/></template></template>
</div>
</div>
</div>

View File

@ -0,0 +1,63 @@
<template>
<div class="mk-github-setting">
<p>%i18n:@description%<a :href="`${docsUrl}/link-to-github`" target="_blank">%i18n:@detail%</a></p>
<p class="account" v-if="$store.state.i.github" :title="`GitHub ID: ${$store.state.i.github.id}`">%i18n:@connected-to%: <a :href="`https://github.com/${$store.state.i.github.login}`" target="_blank">@{{ $store.state.i.github.login }}</a></p>
<p>
<a :href="`${apiUrl}/connect/github`" target="_blank" @click.prevent="connect">{{ $store.state.i.github ? '%i18n:@reconnect%' : '%i18n:@connect%' }}</a>
<span v-if="$store.state.i.github"> or </span>
<a :href="`${apiUrl}/disconnect/github`" target="_blank" v-if="$store.state.i.github" @click.prevent="disconnect">%i18n:@disconnect%</a>
</p>
<p class="id" v-if="$store.state.i.github">GitHub ID: {{ $store.state.i.github.id }}</p>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import { apiUrl, docsUrl } from '../../../config';
export default Vue.extend({
data() {
return {
form: null,
apiUrl,
docsUrl
};
},
mounted() {
this.$watch('$store.state.i', () => {
if (this.$store.state.i.github && this.form)
this.form.close();
}, {
deep: true
});
},
methods: {
connect() {
this.form = window.open(apiUrl + '/connect/github',
'github_connect_window',
'height=570, width=520');
},
disconnect() {
window.open(apiUrl + '/disconnect/github',
'github_disconnect_window',
'height=570, width=520');
}
}
});
</script>
<style lang="stylus" scoped>
.mk-github-setting
.account
border solid 1px #e1e8ed
border-radius 4px
padding 16px
a
font-weight bold
color inherit
.id
color #8899a6
</style>

View File

@ -1,7 +1,7 @@
<template>
<div class="mk-google">
<input type="search" v-model="query" :placeholder="q">
<button @click="search">%fa:search% %i18n:common.search%</button>
<button @click="search"><fa icon="search"/> %i18n:common.search%</button>
</div>
</template>

View File

@ -37,12 +37,14 @@ import messaging from './messaging.vue';
import messagingRoom from './messaging-room.vue';
import urlPreview from './url-preview.vue';
import twitterSetting from './twitter-setting.vue';
import githubSetting from './github-setting.vue';
import fileTypeIcon from './file-type-icon.vue';
import emoji from './emoji.vue';
import Reversi from './games/reversi/reversi.vue';
import welcomeTimeline from './welcome-timeline.vue';
import uiInput from './ui/input.vue';
import uiButton from './ui/button.vue';
import uiButtonGroup from './ui/button-group.vue';
import uiHorizonGroup from './ui/horizon-group.vue';
import uiCard from './ui/card.vue';
import uiForm from './ui/form.vue';
import uiTextarea from './ui/textarea.vue';
@ -90,12 +92,14 @@ Vue.component('mk-messaging', messaging);
Vue.component('mk-messaging-room', messagingRoom);
Vue.component('mk-url-preview', urlPreview);
Vue.component('mk-twitter-setting', twitterSetting);
Vue.component('mk-github-setting', githubSetting);
Vue.component('mk-file-type-icon', fileTypeIcon);
Vue.component('mk-emoji', emoji);
Vue.component('mk-reversi', Reversi);
Vue.component('mk-welcome-timeline', welcomeTimeline);
Vue.component('ui-input', uiInput);
Vue.component('ui-button', uiButton);
Vue.component('ui-button-group', uiButtonGroup);
Vue.component('ui-horizon-group', uiHorizonGroup);
Vue.component('ui-card', uiCard);
Vue.component('ui-form', uiForm);
Vue.component('ui-textarea', uiTextarea);

View File

@ -1,7 +1,7 @@
<template>
<div class="mk-media-banner">
<div class="sensitive" v-if="media.isSensitive && hide" @click="hide = false">
<span class="icon">%fa:exclamation-triangle%</span>
<span class="icon"><fa icon="exclamation-triangle"/></span>
<b>%i18n:@sensitive%</b>
<span>%i18n:@click-to-show%</span>
</div>
@ -18,7 +18,7 @@
:title="media.name"
:download="media.name"
>
<span class="icon">%fa:download%</span>
<span class="icon"><fa icon="download"/></span>
<b>{{ media.name }}</b>
</a>
</div>

View File

@ -4,7 +4,9 @@
<div class="popover" :class="{ hukidasi }" ref="popover">
<template v-for="item, i in items">
<div v-if="item === null"></div>
<button v-if="item" @click="clicked(item.action)" v-html="item.icon ? item.icon + ' ' + item.text : item.text" :tabindex="i"></button>
<button v-if="item" @click="clicked(item.action)" :tabindex="i">
<fa v-if="item.icon" :icon="item.icon"/>{{ item.text }}
</button>
</template>
</div>
</div>
@ -188,6 +190,9 @@ export default Vue.extend({
color var(--primaryForeground)
background var(--primaryDarken10)
> [data-icon]
margin-right 4px
> div
margin 8px 0
height 1px

View File

@ -14,13 +14,13 @@
<div class="file" @click="file = null" v-if="file">{{ file.name }}</div>
<mk-uploader ref="uploader" @uploaded="onUploaded"/>
<button class="send" @click="send" :disabled="!canSend || sending" title="%i18n:@send%">
<template v-if="!sending">%fa:paper-plane%</template><template v-if="sending">%fa:spinner .spin%</template>
<template v-if="!sending"><fa icon="paper-plane"/></template><template v-if="sending"><fa icon="spinner .spin"/></template>
</button>
<button class="attach-from-local" @click="chooseFile" title="%i18n:@attach-from-local%">
%fa:upload%
<fa icon="upload"/>
</button>
<button class="attach-from-drive" @click="chooseFileFromDrive" title="%i18n:@attach-from-drive%">
%fa:R folder-open%
<fa :icon="['far', 'folder-open']"/>
</button>
<input ref="file" type="file" @change="onChangeFile"/>
</div>

View File

@ -24,7 +24,7 @@
<footer>
<span class="read" v-if="isMe && message.isRead">%i18n:@is-read%</span>
<mk-time :time="message.createdAt"/>
<template v-if="message.is_edited">%fa:pencil-alt%</template>
<template v-if="message.is_edited"><fa icon="pencil-alt"/></template>
</footer>
</div>
</div>
@ -179,7 +179,7 @@ export default Vue.extend({
font-size 10px
color var(--messagingRoomMessageInfo)
> [data-fa]
> [data-icon]
margin-left 4px
&:not([data-is-me])

View File

@ -4,11 +4,11 @@
@drop.prevent.stop="onDrop"
>
<div class="body">
<p class="init" v-if="init">%fa:spinner .spin%%i18n:common.loading%</p>
<p class="empty" v-if="!init && messages.length == 0">%fa:info-circle%%i18n:@empty%</p>
<p class="no-history" v-if="!init && messages.length > 0 && !existMoreMessages">%fa:flag%%i18n:@no-history%</p>
<p class="init" v-if="init"><fa icon="spinner .spin"/>%i18n:common.loading%</p>
<p class="empty" v-if="!init && messages.length == 0"><fa icon="info-circle"/>%i18n:@empty%</p>
<p class="no-history" v-if="!init && messages.length > 0 && !existMoreMessages"><fa icon="flag"/>%i18n:@no-history%</p>
<button class="more" :class="{ fetching: fetchingMoreMessages }" v-if="existMoreMessages" @click="fetchMoreMessages" :disabled="fetchingMoreMessages">
<template v-if="fetchingMoreMessages">%fa:spinner .pulse .fw%</template>{{ fetchingMoreMessages ? '%i18n:common.loading%' : '%i18n:@more%' }}
<template v-if="fetchingMoreMessages"><fa icon="spinner .pulse" fixed-width/></template>{{ fetchingMoreMessages ? '%i18n:common.loading%' : '%i18n:@more%' }}
</button>
<template v-for="(message, i) in _messages">
<x-message :message="message" :key="message.id"/>
@ -20,7 +20,7 @@
<footer>
<transition name="fade">
<div class="new-message" v-show="showIndicator">
<button @click="onIndicatorClick">%fa:arrow-circle-down%%i18n:@new-message%</button>
<button @click="onIndicatorClick"><i><fa icon="arrow-circle-down"/></i>%i18n:@new-message%</button>
</div>
</transition>
<x-form :user="user" ref="form"/>
@ -280,7 +280,7 @@ export default Vue.extend({
color var(--messagingRoomInfo)
opacity 0.5
[data-fa]
[data-icon]
margin-right 4px
> .no-history
@ -292,7 +292,7 @@ export default Vue.extend({
color var(--messagingRoomInfo)
opacity 0.5
[data-fa]
[data-icon]
margin-right 4px
> .more
@ -313,7 +313,7 @@ export default Vue.extend({
&.fetching
cursor wait
> [data-fa]
> [data-icon]
margin-right 4px
> .message
@ -381,7 +381,7 @@ export default Vue.extend({
&:active
background var(--primaryDarken10)
> [data-fa]
> i
position absolute
top 0
left 10px

View File

@ -2,7 +2,7 @@
<div class="mk-messaging" :data-compact="compact">
<div class="search" v-if="!compact" :style="{ top: headerTop + 'px' }">
<div class="form">
<label for="search-input">%fa:search%</label>
<label for="search-input"><i><fa icon="search"/></i></label>
<input v-model="q" type="search" @input="search" @keydown="onSearchKeydown" placeholder="%i18n:@search-user%"/>
</div>
<div class="result">
@ -45,7 +45,7 @@
</template>
</div>
<p class="no-history" v-if="!fetching && messages.length == 0">%i18n:@no-history%</p>
<p class="fetching" v-if="fetching">%fa:spinner .pulse .fw%%i18n:common.loading%<mk-ellipsis/></p>
<p class="fetching" v-if="fetching"><fa icon="spinner .pulse" fixed-width/>%i18n:common.loading%<mk-ellipsis/></p>
</div>
</template>
@ -213,7 +213,7 @@ export default Vue.extend({
width 38px
pointer-events none
> [data-fa]
> i
display block
position absolute
top 0
@ -418,7 +418,7 @@ export default Vue.extend({
text-align center
color #aaa
> [data-fa]
> [data-icon]
margin-right 4px
// TODO: element base media query

View File

@ -1,5 +1,4 @@
import Vue, { VNode } from 'vue';
import * as emojilib from 'emojilib';
import { length } from 'stringz';
import parse from '../../../../../mfm/parse';
import getAcct from '../../../../../misc/acct/render';
@ -188,24 +187,15 @@ export default Vue.component('misskey-flavored-markdown', {
}
case 'emoji': {
//#region カスタム絵文字
if (this.customEmojis != null) {
const customEmoji = this.customEmojis.find(e => e.name == token.emoji || (e.aliases || []).includes(token.emoji));
if (customEmoji) {
return [createElement('img', {
attrs: {
src: customEmoji.url,
alt: token.emoji,
title: token.emoji,
style: 'height: 2.5em; vertical-align: middle;'
}
})];
return [createElement('mk-emoji', {
attrs: {
emoji: token.emoji,
name: token.name
},
props: {
customEmojis: this.customEmojis
}
}
//#endregion
const emoji = emojilib.lib[token.emoji];
return [createElement('span', emoji ? emoji.char : token.content)];
})];
}
case 'search': {

View File

@ -1,6 +1,6 @@
<template>
<ui-card>
<div slot="title">%fa:ban% %i18n:@mute-and-block%</div>
<div slot="title"><fa icon="ban"/> %i18n:@mute-and-block%</div>
<section>
<header>%i18n:@mute%</header>

View File

@ -2,15 +2,11 @@
<span class="mk-nav">
<a :href="aboutUrl">%i18n:@about%</a>
<i></i>
<a href="/stats">%i18n:@stats%</a>
<i></i>
<a :href="repositoryUrl">%i18n:@repository%</a>
<i></i>
<a :href="feedbackUrl" target="_blank">%i18n:@feedback%</a>
<i></i>
<a href="/dev">%i18n:@develop%</a>
<i></i>
<a href="https://twitter.com/misskey_xyz" target="_blank">Follow us on %fa:B twitter%</a>
</span>
</template>

View File

@ -6,18 +6,18 @@
<span class="is-bot" v-if="note.user.isBot">bot</span>
<span class="is-cat" v-if="note.user.isCat">cat</span>
<span class="username"><mk-acct :user="note.user"/></span>
<span class="is-verified" v-if="note.user.isVerified" title="%i18n:common.verified-user%">%fa:star%</span>
<span class="is-verified" v-if="note.user.isVerified" title="%i18n:common.verified-user%"><fa icon="star"/></span>
<div class="info">
<span class="app" v-if="note.app && !mini">via <b>{{ note.app.name }}</b></span>
<span class="mobile" v-if="note.viaMobile">%fa:mobile-alt%</span>
<span class="mobile" v-if="note.viaMobile"><fa icon="mobile-alt"/></span>
<router-link class="created-at" :to="note | notePage">
<mk-time :time="note.createdAt"/>
</router-link>
<span class="visibility" v-if="note.visibility != 'public'">
<template v-if="note.visibility == 'home'">%fa:home%</template>
<template v-if="note.visibility == 'followers'">%fa:unlock%</template>
<template v-if="note.visibility == 'specified'">%fa:envelope%</template>
<template v-if="note.visibility == 'private'">%fa:lock%</template>
<template v-if="note.visibility == 'home'"><fa icon="home"/></template>
<template v-if="note.visibility == 'followers'"><fa icon="unlock"/></template>
<template v-if="note.visibility == 'specified'"><fa icon="envelope"/></template>
<template v-if="note.visibility == 'private'"><fa icon="lock"/></template>
</span>
</div>
</header>

View File

@ -15,18 +15,18 @@ export default Vue.extend({
computed: {
items() {
const items = [{
icon: '%fa:info-circle%',
icon: 'info-circle',
text: '%i18n:@detail%',
action: this.detail
}, {
icon: '%fa:link%',
icon: 'link',
text: '%i18n:@copy-link%',
action: this.copyLink
}];
if (this.note.uri) {
items.push({
icon: '%fa:external-link-square-alt%',
icon: 'external-link-square-alt',
text: '%i18n:@remote%',
action: () => {
window.open(this.note.uri, '_blank');
@ -38,13 +38,13 @@ export default Vue.extend({
if (this.note.isFavorited) {
items.push({
icon: '%fa:star%',
icon: 'star',
text: '%i18n:@unfavorite%',
action: this.unfavorite
});
} else {
items.push({
icon: '%fa:star%',
icon: 'star',
text: '%i18n:@favorite%',
action: this.favorite
});
@ -53,13 +53,13 @@ export default Vue.extend({
if (this.note.userId == this.$store.state.i.id) {
if ((this.$store.state.i.pinnedNoteIds || []).includes(this.note.id)) {
items.push({
icon: '%fa:thumbtack%',
icon: 'thumbtack',
text: '%i18n:@unpin%',
action: this.unpin
});
} else {
items.push({
icon: '%fa:thumbtack%',
icon: 'thumbtack',
text: '%i18n:@pin%',
action: this.pin
});
@ -69,7 +69,7 @@ export default Vue.extend({
if (this.note.userId == this.$store.state.i.id || this.$store.state.i.isAdmin) {
items.push(null);
items.push({
icon: '%fa:trash-alt R%',
icon: ['far', 'trash-alt'],
text: '%i18n:@delete%',
action: this.del
});

View File

@ -1,19 +1,19 @@
<template>
<div class="mk-poll-editor">
<p class="caution" v-if="choices.length < 2">
%fa:exclamation-triangle%%i18n:@no-only-one-choice%
<fa icon="exclamation-triangle"/>%i18n:@no-only-one-choice%
</p>
<ul ref="choices">
<li v-for="(choice, i) in choices">
<input :value="choice" @input="onInput(i, $event)" :placeholder="'%i18n:@choice-n%'.replace('{}', i + 1)">
<button @click="remove(i)" title="%i18n:@remove%">
%fa:times%
<fa icon="times"/>
</button>
</li>
</ul>
<button class="add" v-if="choices.length < 10" @click="add">%i18n:@add%</button>
<button class="destroy" @click="destroy" title="%i18n:@destroy%">
%fa:times%
<fa icon="times"/>
</button>
</div>
</template>
@ -76,7 +76,7 @@ export default Vue.extend({
font-size 0.8em
color #f00
> [data-fa]
> [data-icon]
margin-right 4px
> ul

View File

@ -4,7 +4,7 @@
<li v-for="choice in poll.choices" :key="choice.id" @click="vote(choice.id)" :class="{ voted: choice.voted }" :title="!isVoted ? '%i18n:@vote-to%'.replace('{}', choice.text) : ''">
<div class="backdrop" :style="{ 'width': (showResult ? (choice.votes / total * 100) : 0) + '%' }"></div>
<span>
<template v-if="choice.isVoted">%fa:check%</template>
<template v-if="choice.isVoted"><fa icon="check"/></template>
<span>{{ choice.text }}</span>
<span class="votes" v-if="showResult">({{ '%i18n:@vote-count%'.replace('{}', choice.votes) }})</span>
</span>
@ -100,7 +100,7 @@ export default Vue.extend({
transition width 1s ease
> span
> [data-fa]
> [data-icon]
margin-right 4px
> .votes

View File

@ -1,6 +1,6 @@
<template>
<ui-card>
<div slot="title">%fa:user% %i18n:@title%</div>
<div slot="title"><fa icon="user"/> %i18n:@title%</div>
<section class="fit-top">
<ui-form :disabled="saving">
@ -16,12 +16,12 @@
<ui-input v-model="location">
<span>%i18n:@location%</span>
<span slot="prefix">%fa:map-marker-alt%</span>
<span slot="prefix"><fa icon="map-marker-alt"/></span>
</ui-input>
<ui-input v-model="birthday" type="date">
<span>%i18n:@birthday%</span>
<span slot="prefix">%fa:birthday-cake%</span>
<span slot="prefix"><fa icon="birthday-cake"/></span>
</ui-input>
<ui-textarea v-model="description" :max="500">
@ -30,13 +30,13 @@
<ui-input type="file" @change="onAvatarChange">
<span>%i18n:@avatar%</span>
<span slot="icon">%fa:image%</span>
<span slot="icon"><fa icon="image"/></span>
<span slot="text" v-if="avatarUploading">%i18n:@uploading%<mk-ellipsis/></span>
</ui-input>
<ui-input type="file" @change="onBannerChange">
<span>%i18n:@banner%</span>
<span slot="icon">%fa:image%</span>
<span slot="icon"><fa icon="image"/></span>
<span slot="text" v-if="bannerUploading">%i18n:@uploading%<mk-ellipsis/></span>
</ui-input>

View File

@ -8,11 +8,12 @@
</ui-input>
<ui-input v-model="password" type="password" required styl="fill">
<span>%i18n:@password%</span>
<span slot="prefix">%fa:lock%</span>
<span slot="prefix"><fa icon="lock"/></span>
</ui-input>
<ui-input v-if="user && user.twoFactorEnabled" v-model="token" type="number" required styl="fill"/>
<ui-button type="submit" :disabled="signing">{{ signing ? '%i18n:@signing-in%' : '%i18n:@signin%' }}</ui-button>
<p style="margin: 8px 0;">%i18n:@or% <a :href="`${apiUrl}/signin/twitter`">%i18n:@signin-with-twitter%</a></p>
<p style="margin: 8px 0;">%i18n:@or% <a :href="`${apiUrl}/signin/github`">%i18n:@signin-with-github%</a></p>
</form>
</template>

View File

@ -3,36 +3,36 @@
<template v-if="meta">
<ui-input v-if="meta.disableRegistration" v-model="invitationCode" type="text" :autocomplete="Math.random()" spellcheck="false" required styl="fill">
<span>%i18n:@invitation-code%</span>
<span slot="prefix">%fa:id-card-alt%</span>
<span slot="prefix"><fa icon="id-card-alt"/></span>
<p slot="text" v-html="'%i18n:@invitation-info%'.replace('{}', meta.maintainer.url)"></p>
</ui-input>
<ui-input v-model="username" type="text" pattern="^[a-zA-Z0-9_]{1,20}$" :autocomplete="Math.random()" spellcheck="false" required @input="onChangeUsername" styl="fill">
<span>%i18n:@username%</span>
<span slot="prefix">@</span>
<span slot="suffix">@{{ host }}</span>
<p slot="text" v-if="usernameState == 'wait'" style="color:#999">%fa:spinner .pulse .fw% %i18n:@checking%</p>
<p slot="text" v-if="usernameState == 'ok'" style="color:#3CB7B5">%fa:check .fw% %i18n:@available%</p>
<p slot="text" v-if="usernameState == 'unavailable'" style="color:#FF1161">%fa:exclamation-triangle .fw% %i18n:@unavailable%</p>
<p slot="text" v-if="usernameState == 'error'" style="color:#FF1161">%fa:exclamation-triangle .fw% %i18n:@error%</p>
<p slot="text" v-if="usernameState == 'invalid-format'" style="color:#FF1161">%fa:exclamation-triangle .fw% %i18n:@invalid-format%</p>
<p slot="text" v-if="usernameState == 'min-range'" style="color:#FF1161">%fa:exclamation-triangle .fw% %i18n:@too-short%</p>
<p slot="text" v-if="usernameState == 'max-range'" style="color:#FF1161">%fa:exclamation-triangle .fw% %i18n:@too-long%</p>
<p slot="text" v-if="usernameState == 'wait'" style="color:#999"><fa icon="spinner .pulse" fixed-width/> %i18n:@checking%</p>
<p slot="text" v-if="usernameState == 'ok'" style="color:#3CB7B5"><fa icon="check" fixed-width/> %i18n:@available%</p>
<p slot="text" v-if="usernameState == 'unavailable'" style="color:#FF1161"><fa icon="exclamation-triangle" fixed-width/> %i18n:@unavailable%</p>
<p slot="text" v-if="usernameState == 'error'" style="color:#FF1161"><fa icon="exclamation-triangle" fixed-width/> %i18n:@error%</p>
<p slot="text" v-if="usernameState == 'invalid-format'" style="color:#FF1161"><fa icon="exclamation-triangle" fixed-width/> %i18n:@invalid-format%</p>
<p slot="text" v-if="usernameState == 'min-range'" style="color:#FF1161"><fa icon="exclamation-triangle" fixed-width/> %i18n:@too-short%</p>
<p slot="text" v-if="usernameState == 'max-range'" style="color:#FF1161"><fa icon="exclamation-triangle" fixed-width/> %i18n:@too-long%</p>
</ui-input>
<ui-input v-model="password" type="password" :autocomplete="Math.random()" required @input="onChangePassword" :with-password-meter="true" styl="fill">
<span>%i18n:@password%</span>
<span slot="prefix">%fa:lock%</span>
<span slot="prefix"><fa icon="lock"/></span>
<div slot="text">
<p slot="text" v-if="passwordStrength == 'low'" style="color:#FF1161">%fa:exclamation-triangle .fw% %i18n:@weak-password%</p>
<p slot="text" v-if="passwordStrength == 'medium'" style="color:#3CB7B5">%fa:check .fw% %i18n:@normal-password%</p>
<p slot="text" v-if="passwordStrength == 'high'" style="color:#3CB7B5">%fa:check .fw% %i18n:@strong-password%</p>
<p slot="text" v-if="passwordStrength == 'low'" style="color:#FF1161"><fa icon="exclamation-triangle" fixed-width/> %i18n:@weak-password%</p>
<p slot="text" v-if="passwordStrength == 'medium'" style="color:#3CB7B5"><fa icon="check" fixed-width/> %i18n:@normal-password%</p>
<p slot="text" v-if="passwordStrength == 'high'" style="color:#3CB7B5"><fa icon="check" fixed-width/> %i18n:@strong-password%</p>
</div>
</ui-input>
<ui-input v-model="retypedPassword" type="password" :autocomplete="Math.random()" required @input="onChangePasswordRetype" styl="fill">
<span>%i18n:@password% (%i18n:@retype%)</span>
<span slot="prefix">%fa:lock%</span>
<span slot="prefix"><fa icon="lock"/></span>
<div slot="text">
<p slot="text" v-if="passwordRetypeState == 'match'" style="color:#3CB7B5">%fa:check .fw% %i18n:@password-matched%</p>
<p slot="text" v-if="passwordRetypeState == 'not-match'" style="color:#FF1161">%fa:exclamation-triangle .fw% %i18n:@password-not-matched%</p>
<p slot="text" v-if="passwordRetypeState == 'match'" style="color:#3CB7B5"><fa icon="check" fixed-width/> %i18n:@password-matched%</p>
<p slot="text" v-if="passwordRetypeState == 'not-match'" style="color:#FF1161"><fa icon="exclamation-triangle" fixed-width/> %i18n:@password-not-matched%</p>
</div>
</ui-input>
<div v-if="meta.recaptchaSitekey != null" class="g-recaptcha" :data-sitekey="meta.recaptchaSitekey" style="margin: 16px 0;"></div>

View File

@ -1,15 +1,15 @@
<template>
<div class="mk-stream-indicator">
<p v-if="stream.state == 'initializing'">
%fa:spinner .pulse%
<fa icon="spinner .pulse"/>
<span>%i18n:@connecting%<mk-ellipsis/></span>
</p>
<p v-if="stream.state == 'reconnecting'">
%fa:spinner .pulse%
<fa icon="spinner .pulse"/>
<span>%i18n:@reconnecting%<mk-ellipsis/></span>
</p>
<p v-if="stream.state == 'connected'">
%fa:check%
<fa icon="check"/>
<span>%i18n:@connected%</span>
</p>
</div>
@ -80,7 +80,7 @@ export default Vue.extend({
display block
margin 0
> [data-fa]
> [data-icon]
margin-right 0.25em
</style>

View File

@ -1,7 +1,7 @@
<template>
<div class="jtivnzhfwquxpsfidertopbmwmchmnmo">
<p class="fetching" v-if="fetching">%fa:spinner .pulse .fw%%i18n:common.loading%<mk-ellipsis/></p>
<p class="empty" v-else-if="tags.length == 0">%fa:exclamation-circle%%i18n:@empty%</p>
<p class="fetching" v-if="fetching"><fa icon="spinner .pulse" fixed-width/>%i18n:common.loading%<mk-ellipsis/></p>
<p class="empty" v-else-if="tags.length == 0"><fa icon="exclamation-circle"/>%i18n:@empty%</p>
<div v-else>
<vue-word-cloud
:words="tags.slice(0, 20).map(x => [x.name, x.count])"
@ -74,7 +74,7 @@ export default Vue.extend({
text-align center
color #aaa
> [data-fa]
> [data-icon]
margin-right 4px
> div

View File

@ -25,7 +25,7 @@
</label>
<details class="creator">
<summary>%fa:palette% %i18n:@create-a-theme%</summary>
<summary><fa icon="palette"/> %i18n:@create-a-theme%</summary>
<div>
<span>%i18n:@base-theme%:</span>
<ui-radio v-model="myThemeBase" value="light">%i18n:@base-theme-light%</ui-radio>
@ -51,23 +51,23 @@
<div style="padding-bottom:8px;">%i18n:@text-color%:</div>
<color-picker v-model="myThemeText"/>
</div>
<ui-button @click="preview()">%fa:eye% %i18n:@preview-created-theme%</ui-button>
<ui-button primary @click="gen()">%fa:save R% %i18n:@save-created-theme%</ui-button>
<ui-button @click="preview()"><fa icon="eye"/> %i18n:@preview-created-theme%</ui-button>
<ui-button primary @click="gen()"><fa :icon="['far', 'save']"/> %i18n:@save-created-theme%</ui-button>
</details>
<details>
<summary>%fa:download% %i18n:@install-a-theme%</summary>
<ui-button @click="import_()">%fa:file-import% %i18n:@import%</ui-button>
<summary><fa icon="download"/> %i18n:@install-a-theme%</summary>
<ui-button @click="import_()"><fa icon="file-import"/> %i18n:@import%</ui-button>
<input ref="file" type="file" accept=".misskeytheme" style="display:none;" @change="onUpdateImportFile"/>
<p>%i18n:@import-by-code%:</p>
<ui-textarea v-model="installThemeCode">
<span>%i18n:@theme-code%</span>
</ui-textarea>
<ui-button @click="() => install(this.installThemeCode)">%fa:check% %i18n:@install%</ui-button>
<ui-button @click="() => install(this.installThemeCode)"><fa icon="check"/> %i18n:@install%</ui-button>
</details>
<details>
<summary>%fa:folder-open% %i18n:@manage-themes%</summary>
<summary><fa icon="folder-open"/> %i18n:@manage-themes%</summary>
<ui-select v-model="selectedThemeId" placeholder="%i18n:@select-theme%">
<optgroup label="%i18n:@builtin-themes%">
<option v-for="x in builtinThemes" :value="x.id" :key="x.id">{{ x.name }}</option>
@ -89,8 +89,8 @@
<ui-textarea readonly :value="selectedThemeCode">
<span>%i18n:@theme-code%</span>
</ui-textarea>
<ui-button @click="export_()" link :download="`${selectedTheme.name}.misskeytheme`" ref="export">%fa:box% %i18n:@export%</ui-button>
<ui-button @click="uninstall()" v-if="!builtinThemes.some(t => t.id == selectedTheme.id)">%fa:trash-alt R% %i18n:@uninstall%</ui-button>
<ui-button @click="export_()" link :download="`${selectedTheme.name}.misskeytheme`" ref="export"><fa icon="box"/> %i18n:@export%</ui-button>
<ui-button @click="uninstall()" v-if="!builtinThemes.some(t => t.id == selectedTheme.id)"><fa :icon="['far', 'trash-alt']"/> %i18n:@uninstall%</ui-button>
</template>
</details>
</div>

View File

@ -1,7 +1,7 @@
<template>
<div class="csqvmxybqbycalfhkxvyfrgbrdalkaoc">
<p class="fetching" v-if="fetching">%fa:spinner .pulse .fw%%i18n:common.loading%<mk-ellipsis/></p>
<p class="empty" v-else-if="stats.length == 0">%fa:exclamation-circle%%i18n:@empty%</p>
<p class="fetching" v-if="fetching"><fa icon="spinner .pulse" fixed-width/>%i18n:common.loading%<mk-ellipsis/></p>
<p class="empty" v-else-if="stats.length == 0"><fa icon="exclamation-circle"/>%i18n:@empty%</p>
<!-- トランジションを有効にするとなぜかメモリリークする -->
<transition-group v-else tag="div" name="chart">
<div v-for="stat in stats" :key="stat.tag">
@ -58,7 +58,7 @@ export default Vue.extend({
color var(--text)
opacity 0.7
> [data-fa]
> [data-icon]
margin-right 4px
> div

View File

@ -1,21 +0,0 @@
<template>
<div class="pfzekjfwkwvadvlujpdnnxfggqgqjoze">
<slot></slot>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
export default Vue.extend({});
</script>
<style lang="stylus" scoped>
.pfzekjfwkwvadvlujpdnnxfggqgqjoze
display flex
> *
flex 1
&:not(:last-child)
margin-right 16px
</style>

View File

@ -1,5 +1,10 @@
<template>
<component class="dmtdnykelhudezerjlfpbhgovrgnqqgr" :is="link ? 'a' : 'button'" :class="[styl, { inline, primary }]" :type="type" @click="$emit('click')">
<component class="dmtdnykelhudezerjlfpbhgovrgnqqgr"
:is="link ? 'a' : 'button'"
:class="[styl, { inline, primary }]"
:type="type"
@click="$emit('click')"
>
<slot></slot>
</component>
</template>
@ -7,6 +12,11 @@
<script lang="ts">
import Vue from 'vue';
export default Vue.extend({
inject: {
horizonGrouped: {
default: false
}
},
props: {
type: {
type: String,
@ -20,7 +30,9 @@ export default Vue.extend({
inline: {
type: Boolean,
required: false,
default: false
default(): boolean {
return this.horizonGrouped;
}
},
link: {
type: Boolean,

View File

@ -0,0 +1,35 @@
<template>
<div class="pfzekjfwkwvadvlujpdnnxfggqgqjoze" :class="{ inputs }">
<slot></slot>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
export default Vue.extend({
provide: {
horizonGrouped: true
},
props: {
inputs: {
type: Boolean,
required: false,
default: false
}
}
});
</script>
<style lang="stylus" scoped>
.pfzekjfwkwvadvlujpdnnxfggqgqjoze
display flex
&.inputs
margin 32px 0
> *
flex 1
&:not(:last-child)
margin-right 16px
</style>

View File

@ -1,7 +1,7 @@
<template>
<div class="ymxyweixqwsxauxldgpvecjepnwxbylu" :class="{ warn }">
<i v-if="warn">%fa:exclamation-triangle%</i>
<i v-else>%fa:info-circle%</i>
<i v-if="warn"><fa icon="exclamation-triangle"/></i>
<i v-else><fa icon="info-circle"/></i>
<slot></slot>
</div>
</template>

View File

@ -1,5 +1,5 @@
<template>
<div class="ui-input" :class="[{ focused, filled }, styl]">
<div class="ui-input" :class="[{ focused, filled, inline }, styl]">
<div class="icon" ref="icon"><slot name="icon"></slot></div>
<div class="input">
<div class="password-meter" v-if="withPasswordMeter" v-show="passwordStrength != ''" :data-strength="passwordStrength">
@ -41,6 +41,11 @@ import Vue from 'vue';
const getPasswordStrength = require('syuilo-password-strength');
export default Vue.extend({
inject: {
horizonGrouped: {
default: false
}
},
props: {
value: {
required: false
@ -72,6 +77,13 @@ export default Vue.extend({
required: false,
default: false
},
inline: {
type: Boolean,
required: false,
default(): boolean {
return this.horizonGrouped;
}
},
styl: {
type: String,
required: false,
@ -337,4 +349,8 @@ root(fill)
&:not(.fill)
root(false)
&.inline
display inline-block
margin 0
</style>

View File

@ -3,7 +3,7 @@
<ol v-if="uploads.length > 0">
<li v-for="ctx in uploads" :key="ctx.id">
<div class="img" :style="{ backgroundImage: `url(${ ctx.img })` }"></div>
<p class="name">%fa:spinner .pulse%{{ ctx.name }}</p>
<p class="name"><fa icon="spinner .pulse"/>{{ ctx.name }}</p>
<p class="status">
<span class="initing" v-if="ctx.progress == undefined">%i18n:@waiting%<mk-ellipsis/></span>
<span class="kb" v-if="ctx.progress != undefined">{{ String(Math.floor(ctx.progress.value / 1024)).replace(/(\d)(?=(\d\d\d)+(?!\d))/g, '$1,') }}<i>KB</i> / {{ String(Math.floor(ctx.progress.max / 1024)).replace(/(\d)(?=(\d\d\d)+(?!\d))/g, '$1,') }}<i>KB</i></span>
@ -155,7 +155,7 @@ export default Vue.extend({
text-overflow ellipsis
overflow hidden
> [data-fa]
> [data-icon]
margin-right 4px
> .status

View File

@ -6,7 +6,7 @@
<span class="pathname" v-if="pathname != ''">{{ pathname }}</span>
<span class="query">{{ query }}</span>
<span class="hash">{{ hash }}</span>
%fa:external-link-square-alt%
<fa icon="external-link-square-alt"/>
</a>
</template>
@ -40,7 +40,7 @@ export default Vue.extend({
<style lang="stylus" scoped>
.mk-url
word-break break-all
> [data-fa]
> [data-icon]
padding-left 2px
font-size .9em
font-weight 400

View File

@ -3,34 +3,34 @@
<div class="backdrop" ref="backdrop" @click="close"></div>
<div class="popover" :class="{ compact }" ref="popover">
<div @click="choose('public')" :class="{ active: v == 'public' }">
<div>%fa:globe%</div>
<div><fa icon="globe"/></div>
<div>
<span>%i18n:@public%</span>
</div>
</div>
<div @click="choose('home')" :class="{ active: v == 'home' }">
<div>%fa:home%</div>
<div><fa icon="home"/></div>
<div>
<span>%i18n:@home%</span>
<span>%i18n:@home-desc%</span>
</div>
</div>
<div @click="choose('followers')" :class="{ active: v == 'followers' }">
<div>%fa:unlock%</div>
<div><fa icon="unlock"/></div>
<div>
<span>%i18n:@followers%</span>
<span>%i18n:@followers-desc%</span>
</div>
</div>
<div @click="choose('specified')" :class="{ active: v == 'specified' }">
<div>%fa:envelope%</div>
<div><fa icon="envelope"/></div>
<div>
<span>%i18n:@specified%</span>
<span>%i18n:@specified-desc%</span>
</div>
</div>
<div @click="choose('private')" :class="{ active: v == 'private' }">
<div>%fa:lock%</div>
<div><fa icon="lock"/></div>
<div>
<span>%i18n:@private%</span>
</div>

View File

@ -145,6 +145,7 @@ class Autocomplete {
} else {
// サジェスト要素作成
this.suggestion = new MkAutocomplete({
parent: this.vm,
propsData: {
textarea: this.textarea,
complete: this.complete,
@ -222,8 +223,6 @@ class Autocomplete {
const trimmedBefore = before.substring(0, before.lastIndexOf(':'));
const after = source.substr(caret);
if (value.startsWith(':')) value = value + ' ';
// 挿入
this.text = trimmedBefore + value + after;

View File

@ -19,13 +19,13 @@
@click="onClick"
:disabled="followWait">
<template v-if="!followWait">
<template v-if="user.hasPendingFollowRequestFromYou && user.isLocked">%fa:hourglass-half% %i18n:@request-pending%</template>
<template v-else-if="user.hasPendingFollowRequestFromYou && !user.isLocked">%fa:hourglass-start% %i18n:@follow-processing%</template>
<template v-else-if="user.isFollowing">%fa:minus% %i18n:@following%</template>
<template v-else-if="!user.isFollowing && user.isLocked">%fa:plus% %i18n:@follow-request%</template>
<template v-else-if="!user.isFollowing && !user.isLocked">%fa:plus% %i18n:@follow%</template>
<template v-if="user.hasPendingFollowRequestFromYou && user.isLocked"><fa icon="hourglass-half"/> %i18n:@request-pending%</template>
<template v-else-if="user.hasPendingFollowRequestFromYou && !user.isLocked"><fa icon="hourglass-start"/> %i18n:@follow-processing%</template>
<template v-else-if="user.isFollowing"><fa icon="minus"/> %i18n:@following%</template>
<template v-else-if="!user.isFollowing && user.isLocked"><fa icon="plus"/> %i18n:@follow-request%</template>
<template v-else-if="!user.isFollowing && !user.isLocked"><fa icon="plus"/> %i18n:@follow%</template>
</template>
<template v-else>%fa:spinner .pulse .fw%</template>
<template v-else><fa icon="spinner .pulse" fixed-width/></template>
</button>
</div>
</template>

View File

@ -2,7 +2,7 @@
<div>
<mk-widget-container :show-header="false">
<article class="dolfvtibguprpxxhfndqaosjitixjohx">
<h1>%fa:heart%%i18n:@title%</h1>
<h1><fa icon="heart"/>%i18n:@title%</h1>
<p v-if="meta">
{{ '%i18n:@text%'.substr(0, '%i18n:@text%'.indexOf('{')) }}
<a :href="meta.maintainer.url">{{ meta.maintainer.name }}</a>
@ -41,7 +41,7 @@ export default define({
margin 0 0 5px 0
font-size 1em
> [data-fa]
> [data-icon]
margin-right 0.25em
> p

View File

@ -1,7 +1,7 @@
<template>
<div class="mkw-hashtags">
<mk-widget-container :show-header="!props.compact">
<template slot="header">%fa:hashtag%%i18n:@title%</template>
<template slot="header"><fa icon="hashtag"/>%i18n:@title%</template>
<div class="mkw-hashtags--body" :data-mobile="platform == 'mobile'">
<mk-trends/>

View File

@ -1,7 +1,7 @@
<template>
<div class="mkw-memo">
<mk-widget-container :show-header="!props.compact">
<template slot="header">%fa:R sticky-note%%i18n:@title%</template>
<template slot="header"><fa :icon="['far', 'sticky-note']"/>%i18n:@title%</template>
<div class="mkw-memo--body">
<textarea v-model="text" placeholder="%i18n:@memo%" @input="onChange"></textarea>

View File

@ -1,9 +1,9 @@
<template>
<div class="mkw-photo-stream" :class="$style.root" :data-melt="props.design == 2">
<mk-widget-container :show-header="props.design == 0" :naked="props.design == 2">
<template slot="header">%fa:camera%%i18n:@title%</template>
<template slot="header"><fa icon="camera"/>%i18n:@title%</template>
<p :class="$style.fetching" v-if="fetching">%fa:spinner .pulse .fw%%i18n:common.loading%<mk-ellipsis/></p>
<p :class="$style.fetching" v-if="fetching"><fa icon="spinner .pulse" fixed-width/>%i18n:common.loading%<mk-ellipsis/></p>
<div :class="$style.stream" v-if="!fetching && images.length > 0">
<div v-for="image in images" :class="$style.img" :style="`background-image: url(${image.thumbnailUrl || image.url})`"></div>
</div>
@ -94,7 +94,7 @@ export default define({
text-align center
color #aaa
> [data-fa]
> [data-icon]
margin-right 4px
</style>

View File

@ -1,8 +1,8 @@
<template>
<div class="mkw-posts-monitor">
<mk-widget-container :show-header="props.design == 0" :naked="props.design == 2">
<template slot="header">%fa:chart-line%%i18n:@title%</template>
<button slot="func" @click="toggle" title="%i18n:@toggle%">%fa:sort%</button>
<template slot="header"><fa icon="chart-line"/>%i18n:@title%</template>
<button slot="func" @click="toggle" title="%i18n:@toggle%"><fa icon="sort"/></button>
<div class="qpdmibaztplkylerhdbllwcokyrfxeyj" :class="{ dual: props.view == 0 }">
<svg :viewBox="`0 0 ${ viewBoxX } ${ viewBoxY }`" v-show="props.view != 2">

View File

@ -1,11 +1,11 @@
<template>
<div class="mkw-rss">
<mk-widget-container :show-header="!props.compact">
<template slot="header">%fa:rss-square%RSS</template>
<button slot="func" title="設定" @click="setting">%fa:cog%</button>
<template slot="header"><fa icon="rss-square"/>RSS</template>
<button slot="func" title="設定" @click="setting"><fa icon="cog"/></button>
<div class="mkw-rss--body" :data-mobile="platform == 'mobile'">
<p class="fetching" v-if="fetching">%fa:spinner .pulse .fw%%i18n:common.loading%<mk-ellipsis/></p>
<p class="fetching" v-if="fetching"><fa icon="spinner .pulse" fixed-width/>%i18n:common.loading%<mk-ellipsis/></p>
<div class="feed" v-else>
<a v-for="item in items" :href="item.link" target="_blank">{{ item.title }}</a>
</div>
@ -85,7 +85,7 @@ export default define({
text-align center
color #aaa
> [data-fa]
> [data-icon]
margin-right 4px
&[data-mobile]

View File

@ -2,7 +2,7 @@
<div class="cpu">
<x-pie class="pie" :value="usage"/>
<div>
<p>%fa:microchip%CPU</p>
<p><fa icon="microchip"/>CPU</p>
<p>{{ meta.cpu.cores }} Cores</p>
<p>{{ meta.cpu.model }}</p>
</div>
@ -57,7 +57,7 @@ export default Vue.extend({
&:first-child
font-weight bold
> [data-fa]
> [data-icon]
margin-right 4px
&:after

View File

@ -2,7 +2,7 @@
<div class="disk">
<x-pie class="pie" :value="usage"/>
<div>
<p>%fa:R hdd%Storage</p>
<p><fa :icon="['far', 'hdd']"/>Storage</p>
<p>Total: {{ total | bytes(1) }}</p>
<p>Free: {{ available | bytes(1) }}</p>
<p>Used: {{ used | bytes(1) }}</p>
@ -65,7 +65,7 @@ export default Vue.extend({
&:first-child
font-weight bold
> [data-fa]
> [data-icon]
margin-right 4px
&:after

View File

@ -2,7 +2,7 @@
<div class="memory">
<x-pie class="pie" :value="usage"/>
<div>
<p>%fa:flask%Memory</p>
<p><fa icon="flask"/>Memory</p>
<p>Total: {{ total | bytes(1) }}</p>
<p>Used: {{ used | bytes(1) }}</p>
<p>Free: {{ free | bytes(1) }}</p>
@ -65,7 +65,7 @@ export default Vue.extend({
&:first-child
font-weight bold
> [data-fa]
> [data-icon]
margin-right 4px
&:after

View File

@ -1,10 +1,10 @@
<template>
<div class="mkw-server">
<mk-widget-container :show-header="props.design == 0" :naked="props.design == 2">
<template slot="header">%fa:server%%i18n:@title%</template>
<button slot="func" @click="toggle" title="%i18n:@toggle%">%fa:sort%</button>
<template slot="header"><fa icon="server"/>%i18n:@title%</template>
<button slot="func" @click="toggle" title="%i18n:@toggle%"><fa icon="sort"/></button>
<p :class="$style.fetching" v-if="fetching">%fa:spinner .pulse .fw%%i18n:common.loading%<mk-ellipsis/></p>
<p :class="$style.fetching" v-if="fetching"><fa icon="spinner .pulse" fixed-width/>%i18n:common.loading%<mk-ellipsis/></p>
<template v-if="!fetching">
<x-cpu-memory v-show="props.view == 0" :connection="connection"/>
<x-cpu v-show="props.view == 1" :connection="connection" :meta="meta"/>
@ -87,7 +87,7 @@ export default define({
text-align center
color #aaa
> [data-fa]
> [data-icon]
margin-right 4px
</style>

View File

@ -1,6 +1,6 @@
<template>
<div class="mkw-tips">
<p ref="tip">%fa:R lightbulb%<span v-html="tip"></span></p>
<p ref="tip"><fa :icon="['far', 'lightbulb']"/><span v-html="tip"></span></p>
</div>
</template>
@ -88,7 +88,7 @@ export default define({
font-size 0.7em
color #999
> [data-fa]
> [data-icon]
margin-right 4px
kbd

View File

@ -23,7 +23,6 @@ import updateBanner from './api/update-banner';
import MkIndex from './views/pages/index.vue';
import MkHome from './views/pages/home.vue';
import MkDeck from './views/pages/deck/deck.vue';
import MkStats from './views/pages/stats/stats.vue';
import MkUser from './views/pages/user/user.vue';
import MkFavorites from './views/pages/favorites.vue';
import MkSelectDrive from './views/pages/selectdrive.vue';
@ -56,7 +55,6 @@ init(async (launch) => {
{ path: '/', name: 'index', component: MkIndex },
{ path: '/home', name: 'home', component: MkHome },
{ path: '/deck', name: 'deck', component: MkDeck },
{ path: '/stats', name: 'stats', component: MkStats },
{ path: '/i/customize-home', component: MkHomeCustomize },
{ path: '/i/favorites', component: MkFavorites },
{ path: '/i/messaging/:user', component: MkMessagingRoom },

View File

@ -1,10 +1,10 @@
<template>
<div class="mk-activity">
<mk-widget-container :show-header="design == 0" :naked="design == 2">
<template slot="header">%fa:chart-bar%%i18n:@title%</template>
<button slot="func" title="%i18n:@toggle%" @click="toggle">%fa:sort%</button>
<template slot="header"><fa icon="chart-bar"/>%i18n:@title%</template>
<button slot="func" title="%i18n:@toggle%" @click="toggle"><fa icon="sort"/></button>
<p :class="$style.fetching" v-if="fetching">%fa:spinner .pulse .fw%%i18n:common.loading%<mk-ellipsis/></p>
<p :class="$style.fetching" v-if="fetching"><fa icon="spinner .pulse" fixed-width/>%i18n:common.loading%<mk-ellipsis/></p>
<template v-else>
<x-calendar v-show="view == 0" :data="[].concat(activity)"/>
<x-chart v-show="view == 1" :data="[].concat(activity)"/>
@ -78,7 +78,7 @@ export default Vue.extend({
text-align center
color #aaa
> [data-fa]
> [data-icon]
margin-right 4px
</style>

View File

@ -1,9 +1,9 @@
<template>
<div class="mk-calendar" :data-melt="design == 4 || design == 5">
<template v-if="design == 0 || design == 1">
<button @click="prev" title="%i18n:@prev%">%fa:chevron-circle-left%</button>
<button @click="prev" title="%i18n:@prev%"><fa icon="chevron-circle-left"/></button>
<p class="title">{{ '%i18n:@title%'.replace('{1}', year).replace('{2}', month) }}</p>
<button @click="next" title="%i18n:@next%">%fa:chevron-circle-right%</button>
<button @click="next" title="%i18n:@next%"><fa icon="chevron-circle-right"/></button>
</template>
<div class="calendar">
@ -151,7 +151,7 @@ export default Vue.extend({
background var(--faceHeader)
box-shadow 0 1px rgba(#000, 0.07)
> [data-fa]
> [data-icon]
margin-right 4px
> button

View File

@ -1,42 +0,0 @@
import Vue from 'vue';
import { Line } from 'vue-chartjs';
import * as mergeOptions from 'merge-options';
export default Vue.extend({
extends: Line,
props: {
data: {
required: true
},
opts: {
required: false
}
},
watch: {
data() {
this.render();
}
},
mounted() {
this.render();
},
methods: {
render() {
this.renderChart(this.data, mergeOptions({
responsive: true,
maintainAspectRatio: false,
scales: {
xAxes: [{
type: 'time',
distribution: 'series'
}]
},
tooltips: {
intersect: false,
mode: 'index',
position: 'nearest'
}
}, this.opts || {}));
}
}
});

View File

@ -1,723 +0,0 @@
<template>
<div class="gkgckalzgidaygcxnugepioremxvxvpt">
<header>
<b>%i18n:@title%:</b>
<select v-model="chartType">
<optgroup label="%i18n:@federation%">
<option value="federation-instances">%i18n:@charts.federation-instances%</option>
<option value="federation-instances-total">%i18n:@charts.federation-instances-total%</option>
</optgroup>
<optgroup label="%i18n:@users%">
<option value="users">%i18n:@charts.users%</option>
<option value="users-total">%i18n:@charts.users-total%</option>
</optgroup>
<optgroup label="%i18n:@notes%">
<option value="notes">%i18n:@charts.notes%</option>
<option value="local-notes">%i18n:@charts.local-notes%</option>
<option value="remote-notes">%i18n:@charts.remote-notes%</option>
<option value="notes-total">%i18n:@charts.notes-total%</option>
</optgroup>
<optgroup label="%i18n:@drive%">
<option value="drive-files">%i18n:@charts.drive-files%</option>
<option value="drive-files-total">%i18n:@charts.drive-files-total%</option>
<option value="drive">%i18n:@charts.drive%</option>
<option value="drive-total">%i18n:@charts.drive-total%</option>
</optgroup>
<optgroup label="%i18n:@network%">
<option value="network-requests">%i18n:@charts.network-requests%</option>
<option value="network-time">%i18n:@charts.network-time%</option>
<option value="network-usage">%i18n:@charts.network-usage%</option>
</optgroup>
</select>
<div>
<span @click="span = 'day'" :class="{ active: span == 'day' }">%i18n:@per-day%</span> | <span @click="span = 'hour'" :class="{ active: span == 'hour' }">%i18n:@per-hour%</span>
</div>
</header>
<div>
<x-chart v-if="chart" :data="data[0]" :opts="data[1]"/>
</div>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import XChart from './charts.chart.ts';
const colors = {
local: 'rgb(246, 88, 79)',
remote: 'rgb(65, 221, 222)',
localPlus: 'rgb(52, 178, 118)',
remotePlus: 'rgb(158, 255, 209)',
localMinus: 'rgb(255, 97, 74)',
remoteMinus: 'rgb(255, 149, 134)',
incoming: 'rgb(52, 178, 118)',
outgoing: 'rgb(255, 97, 74)',
};
const rgba = (color: string): string => {
return color.replace('rgb', 'rgba').replace(')', ', 0.1)');
};
const limit = 35;
const sum = (...arr) => arr.reduce((r, a) => r.map((b, i) => a[i] + b));
const negate = arr => arr.map(x => -x);
export default Vue.extend({
components: {
XChart
},
data() {
return {
now: null,
chart: null,
chartType: 'notes',
span: 'hour'
};
},
computed: {
data(): any {
if (this.chart == null) return null;
switch (this.chartType) {
case 'federation-instances': return this.federationInstancesChart(false);
case 'federation-instances-total': return this.federationInstancesChart(true);
case 'users': return this.usersChart(false);
case 'users-total': return this.usersChart(true);
case 'notes': return this.notesChart('combined');
case 'local-notes': return this.notesChart('local');
case 'remote-notes': return this.notesChart('remote');
case 'notes-total': return this.notesTotalChart();
case 'drive': return this.driveChart();
case 'drive-total': return this.driveTotalChart();
case 'drive-files': return this.driveFilesChart();
case 'drive-files-total': return this.driveFilesTotalChart();
case 'network-requests': return this.networkRequestsChart();
case 'network-time': return this.networkTimeChart();
case 'network-usage': return this.networkUsageChart();
}
},
stats(): any[] {
const stats =
this.span == 'day' ? this.chart.perDay :
this.span == 'hour' ? this.chart.perHour :
null;
return stats;
}
},
async created() {
this.now = new Date();
const [perHour, perDay] = await Promise.all([Promise.all([
(this as any).api('charts/federation', { limit: limit, span: 'hour' }),
(this as any).api('charts/users', { limit: limit, span: 'hour' }),
(this as any).api('charts/notes', { limit: limit, span: 'hour' }),
(this as any).api('charts/drive', { limit: limit, span: 'hour' }),
(this as any).api('charts/network', { limit: limit, span: 'hour' })
]), Promise.all([
(this as any).api('charts/federation', { limit: limit, span: 'day' }),
(this as any).api('charts/users', { limit: limit, span: 'day' }),
(this as any).api('charts/notes', { limit: limit, span: 'day' }),
(this as any).api('charts/drive', { limit: limit, span: 'day' }),
(this as any).api('charts/network', { limit: limit, span: 'day' })
])]);
const chart = {
perHour: {
federation: perHour[0],
users: perHour[1],
notes: perHour[2],
drive: perHour[3],
network: perHour[4]
},
perDay: {
federation: perDay[0],
users: perDay[1],
notes: perDay[2],
drive: perDay[3],
network: perDay[4]
}
};
this.chart = chart;
},
methods: {
getDate(i: number) {
const y = this.now.getFullYear();
const m = this.now.getMonth();
const d = this.now.getDate();
const h = this.now.getHours();
return (
this.span == 'day' ? new Date(y, m, d - i) :
this.span == 'hour' ? new Date(y, m, d, h - i) :
null
);
},
format(arr) {
return arr.map((v, i) => ({ t: this.getDate(i).getTime(), y: v }));
},
federationInstancesChart(total: boolean): any {
return [{
datasets: [{
label: 'Instances',
fill: true,
backgroundColor: rgba(colors.localPlus),
borderColor: colors.localPlus,
borderWidth: 2,
pointBackgroundColor: '#fff',
lineTension: 0,
data: this.format(total
? this.stats.federation.instance.total
: sum(this.stats.federation.instance.inc, negate(this.stats.federation.instance.dec)))
}]
}];
},
notesChart(type: string): any {
return [{
datasets: [{
label: 'All',
fill: false,
borderColor: '#555',
borderWidth: 2,
borderDash: [4, 4],
pointBackgroundColor: '#fff',
lineTension: 0,
data: this.format(type == 'combined'
? sum(this.stats.notes.local.inc, negate(this.stats.notes.local.dec), this.stats.notes.remote.inc, negate(this.stats.notes.remote.dec))
: sum(this.stats.notes[type].inc, negate(this.stats.notes[type].dec))
)
}, {
label: 'Renotes',
fill: true,
backgroundColor: 'rgba(161, 222, 65, 0.1)',
borderColor: '#a1de41',
borderWidth: 2,
pointBackgroundColor: '#fff',
lineTension: 0,
data: this.format(type == 'combined'
? sum(this.stats.notes.local.diffs.renote, this.stats.notes.remote.diffs.renote)
: this.stats.notes[type].diffs.renote
)
}, {
label: 'Replies',
fill: true,
backgroundColor: 'rgba(247, 121, 108, 0.1)',
borderColor: '#f7796c',
borderWidth: 2,
pointBackgroundColor: '#fff',
lineTension: 0,
data: this.format(type == 'combined'
? sum(this.stats.notes.local.diffs.reply, this.stats.notes.remote.diffs.reply)
: this.stats.notes[type].diffs.reply
)
}, {
label: 'Normal',
fill: true,
backgroundColor: 'rgba(65, 221, 222, 0.1)',
borderColor: '#41ddde',
borderWidth: 2,
pointBackgroundColor: '#fff',
lineTension: 0,
data: this.format(type == 'combined'
? sum(this.stats.notes.local.diffs.normal, this.stats.notes.remote.diffs.normal)
: this.stats.notes[type].diffs.normal
)
}]
}, {
scales: {
yAxes: [{
ticks: {
callback: value => {
return Vue.filter('number')(value);
}
}
}]
},
tooltips: {
callbacks: {
label: (tooltipItem, data) => {
const label = data.datasets[tooltipItem.datasetIndex].label || '';
return `${label}: ${Vue.filter('number')(tooltipItem.yLabel)}`;
}
}
}
}];
},
notesTotalChart(): any {
return [{
datasets: [{
label: 'Combined',
fill: false,
borderColor: '#555',
borderWidth: 2,
borderDash: [4, 4],
pointBackgroundColor: '#fff',
lineTension: 0,
data: this.format(sum(this.stats.notes.local.total, this.stats.notes.remote.total))
}, {
label: 'Local',
fill: true,
backgroundColor: rgba(colors.local),
borderColor: colors.local,
borderWidth: 2,
pointBackgroundColor: '#fff',
lineTension: 0,
data: this.format(this.stats.notes.local.total)
}, {
label: 'Remote',
fill: true,
backgroundColor: rgba(colors.remote),
borderColor: colors.remote,
borderWidth: 2,
pointBackgroundColor: '#fff',
lineTension: 0,
data: this.format(this.stats.notes.remote.total)
}]
}, {
scales: {
yAxes: [{
ticks: {
callback: value => {
return Vue.filter('number')(value);
}
}
}]
},
tooltips: {
callbacks: {
label: (tooltipItem, data) => {
const label = data.datasets[tooltipItem.datasetIndex].label || '';
return `${label}: ${Vue.filter('number')(tooltipItem.yLabel)}`;
}
}
}
}];
},
usersChart(total: boolean): any {
return [{
datasets: [{
label: 'Combined',
fill: false,
borderColor: '#555',
borderWidth: 2,
borderDash: [4, 4],
pointBackgroundColor: '#fff',
lineTension: 0,
data: this.format(total
? sum(this.stats.users.local.total, this.stats.users.remote.total)
: sum(this.stats.users.local.inc, negate(this.stats.users.local.dec), this.stats.users.remote.inc, negate(this.stats.users.remote.dec))
)
}, {
label: 'Local',
fill: true,
backgroundColor: rgba(colors.local),
borderColor: colors.local,
borderWidth: 2,
pointBackgroundColor: '#fff',
lineTension: 0,
data: this.format(total
? this.stats.users.local.total
: sum(this.stats.users.local.inc, negate(this.stats.users.local.dec))
)
}, {
label: 'Remote',
fill: true,
backgroundColor: rgba(colors.remote),
borderColor: colors.remote,
borderWidth: 2,
pointBackgroundColor: '#fff',
lineTension: 0,
data: this.format(total
? this.stats.users.remote.total
: sum(this.stats.users.remote.inc, negate(this.stats.users.remote.dec))
)
}]
}, {
scales: {
yAxes: [{
ticks: {
callback: value => {
return Vue.filter('number')(value);
}
}
}]
},
tooltips: {
callbacks: {
label: (tooltipItem, data) => {
const label = data.datasets[tooltipItem.datasetIndex].label || '';
return `${label}: ${Vue.filter('number')(tooltipItem.yLabel)}`;
}
}
}
}];
},
driveChart(): any {
return [{
datasets: [{
label: 'All',
fill: false,
borderColor: '#555',
borderWidth: 2,
borderDash: [4, 4],
pointBackgroundColor: '#fff',
lineTension: 0,
data: this.format(sum(this.stats.drive.local.incSize, negate(this.stats.drive.local.decSize), this.stats.drive.remote.incSize, negate(this.stats.drive.remote.decSize)))
}, {
label: 'Local +',
fill: true,
backgroundColor: rgba(colors.localPlus),
borderColor: colors.localPlus,
borderWidth: 2,
pointBackgroundColor: '#fff',
lineTension: 0,
data: this.format(this.stats.drive.local.incSize)
}, {
label: 'Local -',
fill: true,
backgroundColor: rgba(colors.localMinus),
borderColor: colors.localMinus,
borderWidth: 2,
pointBackgroundColor: '#fff',
lineTension: 0,
data: this.format(negate(this.stats.drive.local.decSize))
}, {
label: 'Remote +',
fill: true,
backgroundColor: rgba(colors.remotePlus),
borderColor: colors.remotePlus,
borderWidth: 2,
pointBackgroundColor: '#fff',
lineTension: 0,
data: this.format(this.stats.drive.remote.incSize)
}, {
label: 'Remote -',
fill: true,
backgroundColor: rgba(colors.remoteMinus),
borderColor: colors.remoteMinus,
borderWidth: 2,
pointBackgroundColor: '#fff',
lineTension: 0,
data: this.format(negate(this.stats.drive.remote.decSize))
}]
}, {
scales: {
yAxes: [{
ticks: {
callback: value => {
return Vue.filter('bytes')(value, 1);
}
}
}]
},
tooltips: {
callbacks: {
label: (tooltipItem, data) => {
const label = data.datasets[tooltipItem.datasetIndex].label || '';
return `${label}: ${Vue.filter('bytes')(tooltipItem.yLabel, 1)}`;
}
}
}
}];
},
driveTotalChart(): any {
return [{
datasets: [{
label: 'Combined',
fill: false,
borderColor: '#555',
borderWidth: 2,
borderDash: [4, 4],
pointBackgroundColor: '#fff',
lineTension: 0,
data: this.format(sum(this.stats.drive.local.totalSize, this.stats.drive.remote.totalSize))
}, {
label: 'Local',
fill: true,
backgroundColor: rgba(colors.local),
borderColor: colors.local,
borderWidth: 2,
pointBackgroundColor: '#fff',
lineTension: 0,
data: this.format(this.stats.drive.local.totalSize)
}, {
label: 'Remote',
fill: true,
backgroundColor: rgba(colors.remote),
borderColor: colors.remote,
borderWidth: 2,
pointBackgroundColor: '#fff',
lineTension: 0,
data: this.format(this.stats.drive.remote.totalSize)
}]
}, {
scales: {
yAxes: [{
ticks: {
callback: value => {
return Vue.filter('bytes')(value, 1);
}
}
}]
},
tooltips: {
callbacks: {
label: (tooltipItem, data) => {
const label = data.datasets[tooltipItem.datasetIndex].label || '';
return `${label}: ${Vue.filter('bytes')(tooltipItem.yLabel, 1)}`;
}
}
}
}];
},
driveFilesChart(): any {
return [{
datasets: [{
label: 'All',
fill: false,
borderColor: '#555',
borderWidth: 2,
borderDash: [4, 4],
pointBackgroundColor: '#fff',
lineTension: 0,
data: this.format(sum(this.stats.drive.local.incCount, negate(this.stats.drive.local.decCount), this.stats.drive.remote.incCount, negate(this.stats.drive.remote.decCount)))
}, {
label: 'Local +',
fill: true,
backgroundColor: rgba(colors.localPlus),
borderColor: colors.localPlus,
borderWidth: 2,
pointBackgroundColor: '#fff',
lineTension: 0,
data: this.format(this.stats.drive.local.incCount)
}, {
label: 'Local -',
fill: true,
backgroundColor: rgba(colors.localMinus),
borderColor: colors.localMinus,
borderWidth: 2,
pointBackgroundColor: '#fff',
lineTension: 0,
data: this.format(negate(this.stats.drive.local.decCount))
}, {
label: 'Remote +',
fill: true,
backgroundColor: rgba(colors.remotePlus),
borderColor: colors.remotePlus,
borderWidth: 2,
pointBackgroundColor: '#fff',
lineTension: 0,
data: this.format(this.stats.drive.remote.incCount)
}, {
label: 'Remote -',
fill: true,
backgroundColor: rgba(colors.remoteMinus),
borderColor: colors.remoteMinus,
borderWidth: 2,
pointBackgroundColor: '#fff',
lineTension: 0,
data: this.format(negate(this.stats.drive.remote.decCount))
}]
}, {
scales: {
yAxes: [{
ticks: {
callback: value => {
return Vue.filter('number')(value);
}
}
}]
},
tooltips: {
callbacks: {
label: (tooltipItem, data) => {
const label = data.datasets[tooltipItem.datasetIndex].label || '';
return `${label}: ${Vue.filter('number')(tooltipItem.yLabel)}`;
}
}
}
}];
},
driveFilesTotalChart(): any {
return [{
datasets: [{
label: 'Combined',
fill: false,
borderColor: '#555',
borderWidth: 2,
borderDash: [4, 4],
pointBackgroundColor: '#fff',
lineTension: 0,
data: this.format(sum(this.stats.drive.local.totalCount, this.stats.drive.remote.totalCount))
}, {
label: 'Local',
fill: true,
backgroundColor: rgba(colors.local),
borderColor: colors.local,
borderWidth: 2,
pointBackgroundColor: '#fff',
lineTension: 0,
data: this.format(this.stats.drive.local.totalCount)
}, {
label: 'Remote',
fill: true,
backgroundColor: rgba(colors.remote),
borderColor: colors.remote,
borderWidth: 2,
pointBackgroundColor: '#fff',
lineTension: 0,
data: this.format(this.stats.drive.remote.totalCount)
}]
}, {
scales: {
yAxes: [{
ticks: {
callback: value => {
return Vue.filter('number')(value);
}
}
}]
},
tooltips: {
callbacks: {
label: (tooltipItem, data) => {
const label = data.datasets[tooltipItem.datasetIndex].label || '';
return `${label}: ${Vue.filter('number')(tooltipItem.yLabel)}`;
}
}
}
}];
},
networkRequestsChart(): any {
return [{
datasets: [{
label: 'Incoming',
fill: true,
backgroundColor: rgba(colors.localPlus),
borderColor: colors.localPlus,
borderWidth: 2,
pointBackgroundColor: '#fff',
lineTension: 0,
data: this.format(this.stats.network.incomingRequests)
}]
}];
},
networkTimeChart(): any {
const data = [];
for (let i = 0; i < limit; i++) {
data.push(this.stats.network.incomingRequests[i] != 0 ? (this.stats.network.totalTime[i] / this.stats.network.incomingRequests[i]) : 0);
}
return [{
datasets: [{
label: 'Avg time (ms)',
fill: true,
backgroundColor: rgba(colors.localPlus),
borderColor: colors.localPlus,
borderWidth: 2,
pointBackgroundColor: '#fff',
lineTension: 0,
data: this.format(data)
}]
}];
},
networkUsageChart(): any {
return [{
datasets: [{
label: 'Incoming',
fill: true,
backgroundColor: rgba(colors.incoming),
borderColor: colors.incoming,
borderWidth: 2,
pointBackgroundColor: '#fff',
lineTension: 0,
data: this.format(this.stats.network.incomingBytes)
}, {
label: 'Outgoing',
fill: true,
backgroundColor: rgba(colors.outgoing),
borderColor: colors.outgoing,
borderWidth: 2,
pointBackgroundColor: '#fff',
lineTension: 0,
data: this.format(this.stats.network.outgoingBytes)
}]
}, {
scales: {
yAxes: [{
ticks: {
callback: value => {
return Vue.filter('bytes')(value, 1);
}
}
}]
},
tooltips: {
callbacks: {
label: (tooltipItem, data) => {
const label = data.datasets[tooltipItem.datasetIndex].label || '';
return `${label}: ${Vue.filter('bytes')(tooltipItem.yLabel, 1)}`;
}
}
}
}];
},
}
});
</script>
<style lang="stylus" scoped>
.gkgckalzgidaygcxnugepioremxvxvpt
padding 32px
background #fff
box-shadow 0 2px 8px rgba(#000, 0.1)
*
user-select none
> header
display flex
margin 0 0 1em 0
padding 0 0 8px 0
font-size 1em
color #555
border-bottom solid 1px #eee
> b
margin-right 8px
> *:last-child
margin-left auto
*
&:not(.active)
color var(--primary)
cursor pointer
> div
> *
display block
height 350px
</style>

View File

@ -13,7 +13,7 @@
@change-selection="onChangeSelection"
/>
<div :class="$style.footer">
<button :class="$style.upload" title="%i18n:@upload%" @click="upload">%fa:upload%</button>
<button :class="$style.upload" title="%i18n:@upload%" @click="upload"><fa icon="upload"/></button>
<button :class="$style.cancel" @click="cancel">%i18n:@cancel%</button>
<button :class="$style.ok" :disabled="multiple && files.length == 0" @click="ok">%i18n:@ok%</button>
</div>
@ -28,7 +28,7 @@ export default Vue.extend({
default: false
},
title: {
default: '%fa:R file%%i18n:@choose-prompt%'
default: '<fa :icon="['far', 'file']"/>%i18n:@choose-prompt%'
}
},
data() {
@ -62,7 +62,7 @@ export default Vue.extend({
.title
> [data-fa]
> [data-icon]
margin-right 4px
.count

View File

@ -21,7 +21,7 @@ import Vue from 'vue';
export default Vue.extend({
props: {
title: {
default: '%fa:R folder%%i18n:@choose-prompt%'
default: '<fa :icon="['far', 'folder']"/>%i18n:@choose-prompt%'
}
},
methods: {
@ -40,7 +40,7 @@ export default Vue.extend({
.title
> [data-fa]
> [data-icon]
margin-right 4px
.browser

View File

@ -3,13 +3,13 @@
<li v-for="(item, i) in menu" :class="item ? item.type : item === null ? 'divider' : null">
<template v-if="item">
<template v-if="item.type == null || item.type == 'item'">
<p @click="click(item)"><span :class="$style.icon" v-if="item.icon" v-html="item.icon"></span>{{ item.text }}</p>
<p @click="click(item)"><i v-if="item.icon" :class="$style.icon"><fa :icon="item.icon"/></i>{{ item.text }}</p>
</template>
<template v-else-if="item.type == 'link'">
<a :href="item.href" :target="item.target" @click="click(item)"><span :class="$style.icon" v-if="item.icon" v-html="item.icon"></span>{{ item.text }}</a>
<a :href="item.href" :target="item.target" @click="click(item)"><i v-if="item.icon" :class="$style.icon"><fa :icon="item.icon"/></i>{{ item.text }}</a>
</template>
<template v-else-if="item.type == 'nest'">
<p><span :class="$style.icon" v-if="item.icon" v-html="item.icon"></span>{{ item.text }}...<span class="caret">%fa:caret-right%</span></p>
<p><i v-if="item.icon" :class="$style.icon"><fa :icon="item.icon"/></i>{{ item.text }}...<span class="caret"><fa icon="caret-right"/></span></p>
<me-nu :menu="item.menu" @x="click"/>
</template>
</template>
@ -113,9 +113,9 @@ export default Vue.extend({
<style lang="stylus" module>
.icon
> *
width 28px
margin-left -28px
text-align center
display inline-block
width 28px
margin-left -28px
text-align center
</style>

View File

@ -1,6 +1,6 @@
<template>
<mk-window ref="window" is-modal width="800px" :can-close="false">
<span slot="header">%fa:crop%{{ title }}</span>
<span slot="header"><fa icon="crop"/>{{ title }}</span>
<div class="body">
<vue-cropper ref="cropper"
:src="image.url"
@ -64,7 +64,7 @@ export default Vue.extend({
.header
> [data-fa]
> [data-icon]
margin-right 4px
.img

View File

@ -91,8 +91,6 @@ export default Vue.extend({
</script>
<style lang="stylus" scoped>
.mk-dialog
> .bg
display block

View File

@ -2,7 +2,7 @@
<mk-window ref="window" @closed="destroyDom" width="800px" height="500px" :popout-url="popout">
<template slot="header">
<p v-if="usage" :class="$style.info"><b>{{ usage.toFixed(1) }}%</b> %i18n:@used%</p>
<span :class="$style.title">%fa:cloud%%i18n:common.drive%</span>
<span :class="$style.title"><fa icon="cloud"/>%i18n:common.drive%</span>
</template>
<mk-drive :class="$style.browser" multiple :init-folder="folder" ref="browser"/>
</mk-window>
@ -39,7 +39,7 @@ export default Vue.extend({
<style lang="stylus" module>
.title
> [data-fa]
> [data-icon]
margin-right 4px
.info

View File

@ -71,27 +71,27 @@ export default Vue.extend({
contextmenu((this as any).os)(e, [{
type: 'item',
text: '%i18n:@contextmenu.rename%',
icon: '%fa:i-cursor%',
icon: 'i-cursor',
action: this.rename
}, {
type: 'item',
text: this.file.isSensitive ? '%i18n:@contextmenu.unmark-as-sensitive%' : '%i18n:@contextmenu.mark-as-sensitive%',
icon: this.file.isSensitive ? '%fa:R eye%' : '%fa:R eye-slash%',
icon: this.file.isSensitive ? ['far', 'eye'] : ['far', 'eye-slash'],
action: this.toggleSensitive
}, null, {
type: 'item',
text: '%i18n:@contextmenu.copy-url%',
icon: '%fa:link%',
icon: 'link',
action: this.copyUrl
}, {
type: 'link',
href: `${this.file.url}?download`,
text: '%i18n:@contextmenu.download%',
icon: '%fa:download%',
icon: 'download',
}, null, {
type: 'item',
text: '%i18n:common.delete%',
icon: '%fa:R trash-alt%',
icon: ['far', 'trash-alt'],
action: this.deleteFile
}, null, {
type: 'nest',
@ -170,7 +170,7 @@ export default Vue.extend({
copyUrl() {
copyToClipboard(this.file.url);
(this as any).apis.dialog({
title: '%fa:check%%i18n:@contextmenu.copied%',
title: '<fa icon="check"/>%i18n:@contextmenu.copied%',
text: '%i18n:@contextmenu.copied-url-to-clipboard%',
actions: [{
text: '%i18n:common.ok%'

View File

@ -16,8 +16,8 @@
:title="title"
>
<p class="name">
<template v-if="hover">%fa:R folder-open .fw%</template>
<template v-if="!hover">%fa:R folder .fw%</template>
<template v-if="hover"><fa :icon="['far', 'folder-open']" fixed-width/></template>
<template v-if="!hover"><fa :icon="['far', 'folder']" fixed-width/></template>
{{ folder.name }}
</p>
</div>
@ -55,22 +55,22 @@ export default Vue.extend({
contextmenu((this as any).os)(e, [{
type: 'item',
text: '%i18n:@contextmenu.move-to-this-folder%',
icon: '%fa:arrow-right%',
icon: 'arrow-right',
action: this.go
}, {
type: 'item',
text: '%i18n:@contextmenu.show-in-new-window%',
icon: '%fa:R window-restore%',
icon: ['far', 'window-restore'],
action: this.newWindow
}, null, {
type: 'item',
text: '%i18n:@contextmenu.rename%',
icon: '%fa:i-cursor%',
icon: 'i-cursor',
action: this.rename
}, null, {
type: 'item',
text: '%i18n:common.delete%',
icon: '%fa:R trash-alt%',
icon: ['far', 'trash-alt'],
action: this.deleteFolder
}], {
closed: () => {
@ -155,7 +155,7 @@ export default Vue.extend({
switch (err) {
case 'detected-circular-definition':
(this as any).apis.dialog({
title: '%fa:exclamation-triangle%%i18n:@unable-to-process%',
title: '<fa icon="exclamation-triangle"/>%i18n:@unable-to-process%',
text: '%i18n:@circular-reference-detected%',
actions: [{
text: '%i18n:common.ok%'
@ -255,7 +255,7 @@ export default Vue.extend({
font-size 0.9em
color var(--desktopDriveFolderFg)
> [data-fa]
> [data-icon]
margin-right 4px
margin-left 2px
text-align left

View File

@ -7,7 +7,7 @@
@dragleave="onDragleave"
@drop.stop="onDrop"
>
<template v-if="folder == null">%fa:cloud%</template>
<i v-if="folder == null" class="cloud"><fa icon="cloud"/></i>
<span>{{ folder == null ? '%i18n:common.drive%' : folder.name }}</span>
</div>
</template>
@ -110,7 +110,7 @@ export default Vue.extend({
&[data-draghover]
background #eee
[data-fa].cloud
i.cloud
margin-right 4px
</style>

View File

@ -4,10 +4,10 @@
<div class="path" @contextmenu.prevent.stop="() => {}">
<x-nav-folder :class="{ current: folder == null }"/>
<template v-for="folder in hierarchyFolders">
<span class="separator">%fa:angle-right%</span>
<span class="separator"><fa icon="angle-right"/></span>
<x-nav-folder :folder="folder" :key="folder.id"/>
</template>
<span class="separator" v-if="folder != null">%fa:angle-right%</span>
<span class="separator" v-if="folder != null"><fa icon="angle-right"/></span>
<span class="folder current" v-if="folder != null">{{ folder.name }}</span>
</div>
<!--
@ -138,17 +138,17 @@ export default Vue.extend({
contextmenu((this as any).os)(e, [{
type: 'item',
text: '%i18n:@contextmenu.create-folder%',
icon: '%fa:R folder%',
icon: ['far', 'folder'],
action: this.createFolder
}, {
type: 'item',
text: '%i18n:@contextmenu.upload%',
icon: '%fa:upload%',
icon: 'upload',
action: this.selectLocalFile
}, {
type: 'item',
text: '%i18n:@contextmenu.url-upload%',
icon: '%fa:cloud-upload-alt%',
icon: 'cloud-upload-alt',
action: this.urlUpload
}]);
},
@ -313,7 +313,7 @@ export default Vue.extend({
switch (err) {
case 'detected-circular-definition':
(this as any).apis.dialog({
title: '%fa:exclamation-triangle%%i18n:@unable-to-process%',
title: '<fa icon="exclamation-triangle"/>%i18n:@unable-to-process%',
text: '%i18n:@circular-reference-detected%',
actions: [{
text: '%i18n:common.ok%'
@ -343,7 +343,7 @@ export default Vue.extend({
});
(this as any).apis.dialog({
title: '%fa:check%%i18n:@url-upload-requested%',
title: '<fa icon="check"/>%i18n:@url-upload-requested%',
text: '%i18n:@may-take-time%',
actions: [{
text: '%i18n:common.ok%'
@ -613,9 +613,6 @@ export default Vue.extend({
line-height 38px
cursor pointer
i
margin-right 4px
*
pointer-events none
@ -635,7 +632,7 @@ export default Vue.extend({
opacity 0.5
cursor default
> [data-fa]
> [data-icon]
margin 0
> .search

View File

@ -5,13 +5,13 @@
:disabled="wait"
>
<template v-if="!wait">
<template v-if="u.hasPendingFollowRequestFromYou && u.isLocked">%fa:hourglass-half%<template v-if="size == 'big'"> %i18n:@request-pending%</template></template>
<template v-else-if="u.hasPendingFollowRequestFromYou && !u.isLocked">%fa:hourglass-start%<template v-if="size == 'big'"> %i18n:@follow-processing%</template></template>
<template v-else-if="u.isFollowing">%fa:minus%<template v-if="size == 'big'"> %i18n:@following%</template></template>
<template v-else-if="!u.isFollowing && u.isLocked">%fa:plus%<template v-if="size == 'big'"> %i18n:@follow-request%</template></template>
<template v-else-if="!u.isFollowing && !u.isLocked">%fa:plus%<template v-if="size == 'big'"> %i18n:@follow%</template></template>
<template v-if="u.hasPendingFollowRequestFromYou && u.isLocked"><fa icon="hourglass-half"/><template v-if="size == 'big'"> %i18n:@request-pending%</template></template>
<template v-else-if="u.hasPendingFollowRequestFromYou && !u.isLocked"><fa icon="hourglass-start"/><template v-if="size == 'big'"> %i18n:@follow-processing%</template></template>
<template v-else-if="u.isFollowing"><fa icon="minus"/><template v-if="size == 'big'"> %i18n:@following%</template></template>
<template v-else-if="!u.isFollowing && u.isLocked"><fa icon="plus"/><template v-if="size == 'big'"> %i18n:@follow-request%</template></template>
<template v-else-if="!u.isFollowing && !u.isLocked"><fa icon="plus"/><template v-if="size == 'big'"> %i18n:@follow%</template></template>
</template>
<template v-else>%fa:spinner .pulse .fw%</template>
<template v-else><fa icon="spinner .pulse" fixed-width/></template>
</button>
</template>

View File

@ -11,9 +11,9 @@
</div>
</div>
<p class="empty" v-if="!fetching && users.length == 0">%i18n:@empty%</p>
<p class="fetching" v-if="fetching">%fa:spinner .pulse .fw%%i18n:@fetching%<mk-ellipsis/></p>
<p class="fetching" v-if="fetching"><fa icon="spinner .pulse" fixed-width/>%i18n:@fetching%<mk-ellipsis/></p>
<a class="refresh" @click="refresh">%i18n:@refresh%</a>
<button class="close" @click="destroyDom()" title="%i18n:@close%">%fa:times%</button>
<button class="close" @click="destroyDom()" title="%i18n:@close%"><fa icon="times"/></button>
</div>
</template>
@ -124,7 +124,7 @@ export default Vue.extend({
text-align center
color #aaa
> [data-fa]
> [data-icon]
margin-right 4px
> .refresh
@ -155,7 +155,7 @@ export default Vue.extend({
&:active
color #222
> [data-fa]
> [data-icon]
padding 14px
</style>

View File

@ -1,6 +1,6 @@
<template>
<mk-window ref="window" width="500px" height="560px" :popout-url="popout" @closed="destroyDom">
<span slot="header" :class="$style.header">%fa:gamepad%%i18n:@game%</span>
<span slot="header" :class="$style.header"><fa icon="gamepad"/>%i18n:@game%</span>
<mk-reversi :class="$style.content" @gamed="g => game = g"/>
</mk-window>
</template>
@ -27,7 +27,7 @@ export default Vue.extend({
<style lang="stylus" module>
.header
> [data-fa]
> [data-icon]
margin-right 4px
.content

View File

@ -1,7 +1,7 @@
<template>
<div class="mk-home" :data-customize="customize">
<div class="customize" v-if="customize">
<router-link to="/">%fa:check%%i18n:@done%</router-link>
<router-link to="/"><fa icon="check"/>%i18n:@done%</router-link>
<div>
<div class="adder">
<p>%i18n:@add-widget%</p>
@ -185,7 +185,7 @@ export default Vue.extend({
methods: {
hint() {
(this as any).apis.dialog({
title: '%fa:info-circle%%i18n:common.customization-tips.title%',
title: '<fa icon="info-circle"/>%i18n:common.customization-tips.title%',
text: '<p>%i18n:common.customization-tips.paragraph1%</p>' +
'<p>%i18n:common.customization-tips.paragraph2%</p>' +
'<p>%i18n:common.customization-tips.paragraph3%</p>' +
@ -299,7 +299,7 @@ export default Vue.extend({
background var(--primaryDarken10)
transition background 0s ease
> [data-fa]
> [data-icon]
margin-right 8px
> div

View File

@ -1,7 +1,7 @@
<template>
<mk-window ref="window" is-modal width="500px" @before-close="beforeClose" @closed="destroyDom">
<span slot="header" :class="$style.header">
%fa:i-cursor%{{ title }}
<fa icon="i-cursor"/>{{ title }}
</span>
<div :class="$style.body">
@ -76,10 +76,8 @@ export default Vue.extend({
<style lang="stylus" module>
.header
> [data-fa]
> [data-icon]
margin-right 4px
.body

View File

@ -1,7 +1,7 @@
<template>
<div class="ldwbgwstjsdgcjruamauqdrffetqudry" v-if="image.isSensitive && hide && !$store.state.device.alwaysShowNsfw" @click="hide = false">
<div>
<b>%fa:exclamation-triangle% %i18n:@sensitive%</b>
<b><fa icon="exclamation-triangle"/> %i18n:@sensitive%</b>
<span>%i18n:@click-to-show%</span>
</div>
</div>

View File

@ -1,7 +1,7 @@
<template>
<div class="uofhebxjdgksfmltszlxurtjnjjsvioh" v-if="video.isSensitive && hide" @click="hide = false">
<div>
<b>%fa:exclamation-triangle% %i18n:@sensitive%</b>
<b><fa icon="exclamation-triangle"/> %i18n:@sensitive%</b>
<span>%i18n:@click-to-show%</span>
</div>
</div>
@ -12,7 +12,7 @@
@click.prevent="onClick"
:title="video.name"
>
%fa:R play-circle%
<fa :icon="['far', 'play-circle']"/>
</a>
</div>
</template>

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